395090648a598ea2f24e633196787e8bfd8145f1
[enigma2.git] / lib / gui / elistboxcontent.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
4 #include <lib/python/python.h>
5
6 /*
7     The basic idea is to have an interface which gives all relevant list
8     processing functions, and can be used by the listbox to browse trough
9     the list.
10     
11     The listbox directly uses the implemented cursor. It tries hard to avoid
12     iterating trough the (possibly very large) list, so it should be O(1),
13     i.e. the performance should not be influenced by the size of the list.
14     
15     The list interface knows how to draw the current entry to a specified 
16     offset. Different interfaces can be used to adapt different lists,
17     pre-filter lists on the fly etc.
18     
19                 cursorSave/Restore is used to avoid re-iterating the list on redraw.
20                 The current selection is always selected as cursor position, the
21     cursor is then positioned to the start, and then iterated. This gives
22     at most 2x m_items_per_page cursor movements per redraw, indepenent
23     of the size of the list.
24     
25     Although cursorSet is provided, it should be only used when there is no
26     other way, as it involves iterating trough the list.
27  */
28
29 iListboxContent::~iListboxContent()
30 {
31 }
32
33 iListboxContent::iListboxContent(): m_listbox(0)
34 {
35 }
36
37 void iListboxContent::setListbox(eListbox *lb)
38 {
39         m_listbox = lb;
40         m_listbox->setItemHeight(getItemHeight());
41 }
42
43 int iListboxContent::currentCursorSelectable()
44 {
45         return 1;
46 }
47
48 //////////////////////////////////////
49
50 DEFINE_REF(eListboxPythonStringContent);
51
52 eListboxPythonStringContent::eListboxPythonStringContent(): m_itemheight(25)
53 {
54 }
55
56 eListboxPythonStringContent::~eListboxPythonStringContent()
57 {
58         Py_XDECREF(m_list);
59 }
60
61 void eListboxPythonStringContent::cursorHome()
62 {
63         m_cursor = 0;
64 }
65
66 void eListboxPythonStringContent::cursorEnd()
67 {
68         m_cursor = size();
69 }
70
71 int eListboxPythonStringContent::cursorMove(int count)
72 {
73         m_cursor += count;
74         
75         if (m_cursor < 0)
76                 cursorHome();
77         else if (m_cursor > size())
78                 cursorEnd();
79         return 0;
80 }
81
82 int eListboxPythonStringContent::cursorValid()
83 {
84         return m_cursor < size();
85 }
86
87 int eListboxPythonStringContent::cursorSet(int n)
88 {
89         m_cursor = n;
90         
91         if (m_cursor < 0)
92                 cursorHome();
93         else if (m_cursor > size())
94                 cursorEnd();
95         return 0;
96 }
97
98 int eListboxPythonStringContent::cursorGet()
99 {
100         return m_cursor;
101 }
102
103 int eListboxPythonStringContent::currentCursorSelectable()
104 {
105         if (m_list && cursorValid())
106         {
107                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
108                 if (!PyTuple_Check(item))
109                         return 1;
110                 if (PyTuple_Size(item) >= 2)
111                         return 1;
112         }
113         return 0;
114 }
115
116 void eListboxPythonStringContent::cursorSave()
117 {
118         m_saved_cursor = m_cursor;
119 }
120
121 void eListboxPythonStringContent::cursorRestore()
122 {
123         m_cursor = m_saved_cursor;
124 }
125
126 int eListboxPythonStringContent::size()
127 {
128         if (!m_list)
129                 return 0;
130         return PyList_Size(m_list);
131 }
132         
133 void eListboxPythonStringContent::setSize(const eSize &size)
134 {
135         m_itemsize = size;
136 }
137
138 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
139 {
140         ePtr<gFont> fnt = new gFont("Regular", 20);
141         painter.clip(eRect(offset, m_itemsize));
142         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
143
144         eListboxStyle *local_style = 0;
145
146                 /* get local listbox style, if present */
147         if (m_listbox)
148                 local_style = m_listbox->getLocalStyle();
149
150         if (local_style)
151         {
152                 if (selected)
153                 {
154                         /* if we have a local background color set, use that. */
155                         if (local_style->m_background_color_selected_set)
156                                 painter.setBackgroundColor(local_style->m_background_color_selected);
157                         /* same for foreground */
158                         if (local_style->m_foreground_color_selected_set)
159                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
160                 }
161                 else
162                 {
163                         /* if we have a local background color set, use that. */
164                         if (local_style->m_background_color_set)
165                                 painter.setBackgroundColor(local_style->m_background_color);
166                         /* same for foreground */
167                         if (local_style->m_foreground_color_set)
168                                 painter.setForegroundColor(local_style->m_foreground_color);
169                 }
170         }
171
172         /* if we have no transparent background */
173         if (!local_style || !local_style->m_transparent_background)
174         {
175                         /* blit background picture, if available (otherwise, clear only) */
176                 if (local_style && local_style->m_background)
177                         painter.blit(local_style->m_background, offset, eRect(), 0);
178                 else
179                         painter.clear();
180         } else
181         {
182                 if (local_style->m_background)
183                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
184                 else if (selected && !local_style->m_selection)
185                         painter.clear();
186         }
187
188         if (m_list && cursorValid())
189         {
190                 int gray = 0;
191                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
192                 painter.setFont(fnt);
193
194                         /* the user can supply tuples, in this case the first one will be displayed. */         
195                 if (PyTuple_Check(item))
196                 {
197                         if (PyTuple_Size(item) == 1)
198                                 gray = 1;
199                         item = PyTuple_GET_ITEM(item, 0);
200                 }
201
202                 if (selected && local_style && local_style->m_selection)
203                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
204
205                 if (item == Py_None)
206                 {
207                                 /* seperator */
208                         int half_height = m_itemsize.height() / 2;
209                         painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
210                 } else
211                 {
212                         const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
213                         ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
214                         if (gray)
215                                 painter.setForegroundColor(gRGB(0x808080));
216                         painter.renderText(eRect(text_offset, m_itemsize), string);
217                 }
218
219                 if (selected && (!local_style || !local_style->m_selection))
220                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
221         }
222
223         painter.clippop();
224 }
225
226 void eListboxPythonStringContent::setList(ePyObject list)
227 {
228         Py_XDECREF(m_list);
229         if (!PyList_Check(list))
230         {
231                 m_list = ePyObject();
232         } else
233         {
234                 m_list = list;
235                 Py_INCREF(m_list);
236         }
237
238         if (m_listbox)
239                 m_listbox->entryReset(false);
240 }
241
242 PyObject *eListboxPythonStringContent::getCurrentSelection()
243 {
244         if (!(m_list && cursorValid()))
245                 Py_RETURN_NONE;
246
247         ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
248         Py_XINCREF(r);
249         return r;
250 }
251
252 void eListboxPythonStringContent::invalidateEntry(int index)
253 {
254         if (m_listbox)
255                 m_listbox->entryChanged(index);
256 }
257
258 void eListboxPythonStringContent::invalidate()
259 {
260         if (m_listbox)
261         {
262                 int s = size();
263                 if ( m_cursor >= s )
264                         m_listbox->moveSelectionTo(s?s-1:0);
265                 m_listbox->invalidate();
266         }
267 }
268
269 //////////////////////////////////////
270
271 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
272 {
273         ePtr<gFont> fnt = new gFont("Regular", 20);
274         ePtr<gFont> fnt2 = new gFont("Regular", 16);
275         eRect itemrect(offset, m_itemsize);
276         eListboxStyle *local_style = 0;
277
278         painter.clip(itemrect);
279         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
280
281                 /* get local listbox style, if present */
282         if (m_listbox)
283                 local_style = m_listbox->getLocalStyle();
284
285         if (local_style)
286         {
287                 if (selected)
288                 {
289                         /* if we have a local background color set, use that. */
290                         if (local_style->m_background_color_selected_set)
291                                 painter.setBackgroundColor(local_style->m_background_color_selected);
292                         /* same for foreground */
293                         if (local_style->m_foreground_color_selected_set)
294                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
295                 }
296                 else
297                 {
298                         /* if we have a local background color set, use that. */
299                         if (local_style->m_background_color_set)
300                                 painter.setBackgroundColor(local_style->m_background_color);
301                         /* same for foreground */
302                         if (local_style->m_foreground_color_set)
303                                 painter.setForegroundColor(local_style->m_foreground_color);
304                 }
305         }
306
307         if (!local_style || !local_style->m_transparent_background)
308                 /* if we have no transparent background */
309         {
310                 /* blit background picture, if available (otherwise, clear only) */
311                 if (local_style && local_style->m_background)
312                         painter.blit(local_style->m_background, offset, eRect(), 0);
313                 else
314                         painter.clear();
315         } else
316         {
317                 if (local_style->m_background)
318                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
319                 else if (selected && !local_style->m_selection)
320                         painter.clear();
321         }
322
323         if (m_list && cursorValid())
324         {
325                         /* get current list item */
326                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
327                 ePyObject text, value;
328                 painter.setFont(fnt);
329
330                 if (selected && local_style && local_style->m_selection)
331                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
332
333                         /* the first tuple element is a string for the left side.
334                            the second one will be called, and the result shall be an tuple.
335                            
336                            of this tuple,
337                            the first one is the type (string).
338                            the second one is the value. */
339                 if (PyTuple_Check(item))
340                 {
341                                 /* handle left part. get item from tuple, convert to string, display. */
342                                 
343                         text = PyTuple_GET_ITEM(item, 0);
344                         text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
345                         const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
346                         eSize item_left = eSize(m_seperation, m_itemsize.height());
347                         eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
348                         painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
349                         Py_XDECREF(text);
350                         
351                                 /* when we have no label, align value to the left. (FIXME: 
352                                    don't we want to specifiy this individually?) */
353                         int value_alignment_left = !*string;
354                         
355                                 /* now, handle the value. get 2nd part from tuple*/
356                         value = PyTuple_GET_ITEM(item, 1);
357                         if (value)
358                         {
359                                 ePyObject args = PyTuple_New(1);
360                                 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
361                                 
362                                         /* CallObject will call __call__ which should return the value tuple */
363                                 value = PyObject_CallObject(value, args);
364                                 
365                                 if (PyErr_Occurred())
366                                         PyErr_Print();
367
368                                 Py_DECREF(args);
369                                         /* the PyInt was stolen. */
370                         }
371                         
372                                 /*  check if this is really a tuple */
373                         if (value && PyTuple_Check(value))
374                         {
375                                         /* convert type to string */
376                                 ePyObject type = PyTuple_GET_ITEM(value, 0);
377                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
378                                 
379                                 if (atype)
380                                 {
381                                         if (!strcmp(atype, "text"))
382                                         {
383                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
384                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
385                                                 painter.setFont(fnt2);
386                                                 if (value_alignment_left)
387                                                         painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
388                                                 else
389                                                         painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
390
391                                                         /* pvalue is borrowed */
392                                         } else if (!strcmp(atype, "slider"))
393                                         {
394                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
395                                                 ePyObject psize = PyTuple_GET_ITEM(value, 2);
396                                                 
397                                                         /* convert value to Long. fallback to -1 on error. */
398                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
399                                                 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
400                                                 
401                                                         /* calc. slider length */
402                                                 int width = item_right.width() * value / size;
403                                                 int height = item_right.height();
404                                                 
405                                                                                                 
406                                                         /* draw slider */
407                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
408                                                 //hack - make it customizable
409                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
410                                                 
411                                                         /* pvalue is borrowed */
412                                         } else if (!strcmp(atype, "mtext"))
413                                         {
414                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
415                                                 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
416                                                 int xoffs = value_alignment_left ? 0 : m_seperation;
417                                                 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
418                                                 para->setFont(fnt2);
419                                                 para->renderString(text, 0);
420                                                 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
421                                                 int glyphs = para->size();
422                                                 
423                                                 ePyObject plist;
424                                                 
425                                                 if (PyTuple_Size(value) >= 3)
426                                                         plist = PyTuple_GET_ITEM(value, 2);
427                                                 
428                                                 int entries = 0;
429
430                                                 if (plist && PyList_Check(plist))
431                                                         entries = PyList_Size(plist);
432                                                 
433                                                 for (int i = 0; i < entries; ++i)
434                                                 {
435                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
436                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
437                                                         
438                                                         if ((num < 0) || (num >= glyphs))
439                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
440                                                         else
441                                                         {
442                                                                 para->setGlyphFlag(num, GS_INVERT);
443                                                                 eRect bbox;
444                                                                 bbox = para->getGlyphBBox(num);
445                                                                 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
446                                                                 painter.fill(bbox);
447                                                         }
448                                                                 /* entry is borrowed */
449                                                 }
450                                                 
451                                                 painter.renderPara(para, ePoint(0, 0));
452                                                         /* pvalue is borrowed */
453                                                         /* plist is 0 or borrowed */
454                                         }
455                                 }
456                                         /* type is borrowed */
457                         } else
458                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
459                                 /* value is borrowed */
460                 }
461
462                 if (selected && (!local_style || !local_style->m_selection))
463                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
464         }
465         
466         painter.clippop();
467 }
468
469 int eListboxPythonConfigContent::currentCursorSelectable()
470 {
471         return eListboxPythonStringContent::currentCursorSelectable();
472 }
473
474 //////////////////////////////////////
475
476         /* todo: make a real infrastructure here! */
477 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
478
479 eListboxPythonMultiContent::eListboxPythonMultiContent()
480         :m_clip(gRegion::invalidRegion()), m_old_clip(gRegion::invalidRegion())
481 {
482 }
483
484 eListboxPythonMultiContent::~eListboxPythonMultiContent()
485 {
486         Py_XDECREF(m_buildFunc);
487         Py_XDECREF(m_selectableFunc);
488 }
489
490 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
491 {
492         m_selection_clip = rect;
493         if (m_listbox)
494                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
495         if (m_clip.valid())
496                 m_clip |= rect;
497         else
498                 m_clip = rect;
499         if (update && m_listbox)
500                 m_listbox->entryChanged(m_cursor);
501 }
502
503 static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *local_style, ePyObject pforeColor, ePyObject pforeColorSelected, ePyObject pbackColor, ePyObject pbackColorSelected, int selected, gRegion &rc, eRect &sel_clip)
504 {
505         if (selected && sel_clip.valid())
506         {
507                 bool clear=true;
508                 painter.clip(rc-sel_clip);
509                 if (pbackColor)
510                 {
511                         int color = PyInt_AsLong(pbackColor);
512                         painter.setBackgroundColor(gRGB(color));
513                 } // transparent background?
514                 else if (local_style && local_style->m_transparent_background) 
515                         clear=false;
516                 // if we have a local background color set, use that. 
517                 else if (local_style && local_style->m_background_color_set)
518                         painter.setBackgroundColor(local_style->m_background_color);
519                 else
520                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
521                 if (clear)
522                         painter.clear();
523                 painter.clippop();
524                 painter.clip(rc&sel_clip);
525                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
526                 if (pbackColorSelected)
527                 {
528                         int color = PyInt_AsLong(pbackColorSelected);
529                         painter.setBackgroundColor(gRGB(color));
530                 }
531                 else if (local_style && local_style->m_background_color_selected_set)
532                         painter.setBackgroundColor(local_style->m_background_color_selected);
533                 painter.clear();
534                 painter.clippop();
535         }
536         else
537         {
538                 if (selected)
539                 {
540                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
541                         if (pbackColorSelected)
542                         {
543                                 int color = PyInt_AsLong(pbackColorSelected);
544                                 painter.setBackgroundColor(gRGB(color));
545                         }
546                         else if (local_style && local_style->m_background_color_selected_set)
547                                 painter.setBackgroundColor(local_style->m_background_color_selected);
548                         painter.clear();
549                 }
550                 else
551                 {
552                         bool clear=true;
553                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
554                         if (pbackColor)
555                         {
556                                 int color = PyInt_AsLong(pbackColor);
557                                 painter.setBackgroundColor(gRGB(color));
558                         }/* if we have a local background color set, use that. */
559                         else if (local_style)
560                         {
561                                 if (local_style->m_transparent_background)
562                                         clear=false;
563                                 else if (local_style->m_background_color_set)
564                                         painter.setBackgroundColor(local_style->m_background_color);
565                         }
566                         /* if we have no transparent background */
567                         if (clear)
568                                 painter.clear();
569                 }
570         }
571         if (selected)
572         {
573                 if (pforeColorSelected)
574                 {
575                         int color = PyInt_AsLong(pforeColor);
576                         painter.setForegroundColor(gRGB(color));
577                 }
578                 /* if we have a local foreground color set, use that. */
579                 else if (local_style && local_style->m_foreground_color_selected_set)
580                         painter.setForegroundColor(local_style->m_foreground_color_selected);
581         }
582         else
583         {
584                 if (pforeColor)
585                 {
586                         int color = PyInt_AsLong(pforeColor);
587                         painter.setForegroundColor(gRGB(color));
588                 }
589                 /* if we have a local foreground color set, use that. */
590                 else if (local_style && local_style->m_foreground_color_set)
591                         painter.setForegroundColor(local_style->m_foreground_color);
592         }
593 }
594
595 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
596 {
597         gRegion itemregion(eRect(offset, m_itemsize));
598         eListboxStyle *local_style = 0;
599         eRect sel_clip(m_selection_clip);
600         if (sel_clip.valid())
601                 sel_clip.moveBy(offset);
602
603                 /* get local listbox style, if present */
604         if (m_listbox)
605                 local_style = m_listbox->getLocalStyle();
606
607         painter.clip(itemregion);
608
609         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip);
610
611         ePyObject items;
612
613         if (m_list && cursorValid())
614         {
615                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
616
617                 if (m_buildFunc)
618                 {
619                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
620                         {
621                                 if (PyTuple_Check(items))
622                                         items = PyObject_CallObject(m_buildFunc, items);
623                                 else
624                                         eDebug("items is no tuple");
625                         }
626                         else
627                                 eDebug("buildfunc is not callable");
628                 }
629
630                 if (!items)
631                 {
632                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
633                         goto error_out;
634                 }
635
636                 if (!PyList_Check(items))
637                 {
638                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
639                         goto error_out;
640                 }
641
642                 int size = PyList_Size(items);
643                 for (int i = 1; i < size; ++i)
644                 {
645                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
646                         bool reset_colors=false;
647
648                         if (!item)
649                         {
650                                 eDebug("eListboxPythonMultiContent: ?");
651                                 goto error_out;
652                         }
653
654                         if (!PyTuple_Check(item))
655                         {
656                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
657                                 goto error_out;
658                         }
659
660                         int size = PyTuple_Size(item);
661
662                         if (!size)
663                         {
664                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
665                                 goto error_out;
666                         }
667
668                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
669
670                         switch (type)
671                         {
672                         case TYPE_TEXT: // text
673                         {
674                         /*
675                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
676                         */
677                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
678                                                         py = PyTuple_GET_ITEM(item, 2),
679                                                         pwidth = PyTuple_GET_ITEM(item, 3),
680                                                         pheight = PyTuple_GET_ITEM(item, 4),
681                                                         pfnt = PyTuple_GET_ITEM(item, 5),
682                                                         pflags = PyTuple_GET_ITEM(item, 6),
683                                                         pstring = PyTuple_GET_ITEM(item, 7),
684                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
685
686                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
687                                 {
688                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
689                                         goto error_out;
690                                 }
691
692                                 if (size > 8)
693                                 {
694                                         pforeColor = PyTuple_GET_ITEM(item, 8);
695                                         if (pforeColor == Py_None)
696                                                 pforeColor=ePyObject();
697                                 }
698                                 if (size > 9)
699                                 {
700                                         pforeColorSelected = PyTuple_GET_ITEM(item, 9);
701                                         if (pforeColorSelected == Py_None)
702                                                 pforeColorSelected=ePyObject();
703                                 }
704                                 if (size > 10)
705                                 {
706                                         pbackColor = PyTuple_GET_ITEM(item, 10);
707                                         if (pbackColor == Py_None)
708                                                 pbackColor=ePyObject();
709                                 }
710                                 if (size > 11)
711                                 {
712                                         pbackColorSelected = PyTuple_GET_ITEM(item, 11);
713                                         if (pbackColorSelected == Py_None)
714                                                 pbackColorSelected=ePyObject();
715                                 }
716                                 if (size > 12)
717                                 {
718                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
719                                         if (pborderWidth == Py_None)
720                                                 pborderWidth=ePyObject();
721                                 }
722                                 if (size > 13)
723                                 {
724                                         pborderColor = PyTuple_GET_ITEM(item, 13);
725                                         if (pborderColor == Py_None)
726                                                 pborderColor=ePyObject();
727                                 }
728
729                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
730                                 int x = PyInt_AsLong(px) + offset.x();
731                                 int y = PyInt_AsLong(py) + offset.y();
732                                 int width = PyInt_AsLong(pwidth);
733                                 int height = PyInt_AsLong(pheight);
734                                 int flags = PyInt_AsLong(pflags);
735                                 int fnt = PyInt_AsLong(pfnt);
736                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
737
738                                 if (m_font.find(fnt) == m_font.end())
739                                 {
740                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
741                                         goto error_out;
742                                 }
743
744                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
745                                 painter.clip(rect);
746                                 if (pbackColor || pbackColorSelected || pforeColor || pforeColorSelected)
747                                 {
748                                         gRegion rc(rect);
749                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
750                                         reset_colors=true;
751                                 }
752
753                                 painter.setFont(m_font[fnt]);
754                                 painter.renderText(rect, string, flags);
755                                 painter.clippop();
756
757                                 // draw border
758                                 if (bwidth)
759                                 {
760                                         eRect rect(eRect(x, y, width, height));
761                                         painter.clip(rect);
762                                         if (pborderColor)
763                                         {
764                                                 int color = PyInt_AsLong(pborderColor);
765                                                 painter.setForegroundColor(gRGB(color));
766                                         }
767                                         else if (pforeColor) // reset to normal color
768                                                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
769
770                                         rect.setRect(x, y, width, bwidth);
771                                         painter.fill(rect);
772
773                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
774                                         painter.fill(rect);
775
776                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
777                                         painter.fill(rect);
778
779                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
780                                         painter.fill(rect);
781
782                                         painter.clippop();
783                                 }
784                                 break;
785                         }
786                         case TYPE_PROGRESS: // Progress
787                         {
788                         /*
789                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
790                         */
791                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
792                                                         py = PyTuple_GET_ITEM(item, 2),
793                                                         pwidth = PyTuple_GET_ITEM(item, 3),
794                                                         pheight = PyTuple_GET_ITEM(item, 4),
795                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
796                                                         pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
797
798                                 if (!(px && py && pwidth && pheight && pfilled_perc))
799                                 {
800                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
801                                         goto error_out;
802                                 }
803
804                                 if (size > 6)
805                                 {
806                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
807                                         if (pborderWidth == Py_None)
808                                                 pborderWidth = ePyObject();
809                                 }
810                                 if (size > 7)
811                                 {
812                                         pforeColor = PyTuple_GET_ITEM(item, 7);
813                                         if (pforeColor == Py_None)
814                                                 pforeColor = ePyObject();
815                                 }
816                                 if (size > 8)
817                                 {
818                                         pforeColorSelected = PyTuple_GET_ITEM(item, 8);
819                                         if (pforeColorSelected == Py_None)
820                                                 pforeColorSelected=ePyObject();
821                                 }
822                                 if (size > 9)
823                                 {
824                                         pbackColor = PyTuple_GET_ITEM(item, 9);
825                                         if (pbackColor == Py_None)
826                                                 pbackColor=ePyObject();
827                                 }
828                                 if (size > 10)
829                                 {
830                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
831                                         if (pbackColorSelected == Py_None)
832                                                 pbackColorSelected=ePyObject();
833                                 }
834
835                                 int x = PyInt_AsLong(px) + offset.x();
836                                 int y = PyInt_AsLong(py) + offset.y();
837                                 int width = PyInt_AsLong(pwidth);
838                                 int height = PyInt_AsLong(pheight);
839                                 int filled = PyInt_AsLong(pfilled_perc);
840                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
841
842                                 eRect rect(x, y, width, height);
843                                 painter.clip(rect);
844                                 if (pbackColor || pbackColorSelected || pforeColor || pforeColorSelected)
845                                 {
846                                         gRegion rc(rect);
847                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
848                                         reset_colors=true;
849                                 }
850
851                                 // border
852                                 rect.setRect(x, y, width, bwidth);
853                                 painter.fill(rect);
854
855                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
856                                 painter.fill(rect);
857
858                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
859                                 painter.fill(rect);
860
861                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
862                                 painter.fill(rect);
863
864                                 // progress
865                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
866                                 painter.fill(rect);
867
868                                 painter.clippop();
869
870                                 break;
871                         }
872                         case TYPE_PIXMAP_ALPHATEST:
873                         case TYPE_PIXMAP: // pixmap
874                         {
875                         /*
876                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
877                         */
878
879                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
880                                                         py = PyTuple_GET_ITEM(item, 2),
881                                                         pwidth = PyTuple_GET_ITEM(item, 3),
882                                                         pheight = PyTuple_GET_ITEM(item, 4),
883                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
884                                                         pbackColor, pbackColorSelected;
885
886                                 if (!(px && py && pwidth && pheight && ppixmap))
887                                 {
888                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
889                                         goto error_out;
890                                 }
891
892                                 int x = PyInt_AsLong(px) + offset.x();
893                                 int y = PyInt_AsLong(py) + offset.y();
894                                 int width = PyInt_AsLong(pwidth);
895                                 int height = PyInt_AsLong(pheight);
896                                 ePtr<gPixmap> pixmap;
897                                 if (SwigFromPython(pixmap, ppixmap))
898                                 {
899                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
900                                         goto error_out;
901                                 }
902
903                                 if (size > 6)
904                                 {
905                                         pbackColor = PyTuple_GET_ITEM(item, 6);
906                                         if (pbackColor == Py_None)
907                                                 pbackColor=ePyObject();
908                                 }
909                                 if (size > 7)
910                                 {
911                                         pbackColorSelected = PyTuple_GET_ITEM(item, 7);
912                                         if (pbackColorSelected == Py_None)
913                                                 pbackColorSelected=ePyObject();
914                                 }
915
916                                 eRect rect(x, y, width, height);
917                                 painter.clip(rect);
918                                 if (pbackColor || pbackColorSelected)
919                                 {
920                                         gRegion rc(rect);
921                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
922                                         reset_colors=true;
923                                 }
924                                 
925                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
926                                 painter.clippop();
927                                 break;
928                         }
929                         default:
930                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
931                                 goto error_out;
932                         }
933                         if (reset_colors)
934                                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
935                 }
936         }
937
938         if (selected)
939                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
940
941 error_out:
942         if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
943                 Py_DECREF(items);
944
945         painter.clippop();
946 }
947
948 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
949 {
950         Py_XDECREF(m_buildFunc);
951         m_buildFunc=cb;
952         Py_XINCREF(m_buildFunc);
953 }
954
955 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
956 {
957         Py_XDECREF(m_selectableFunc);
958         m_selectableFunc=cb;
959         Py_XINCREF(m_selectableFunc);
960 }
961
962 int eListboxPythonMultiContent::currentCursorSelectable()
963 {
964         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
965         if (m_list && cursorValid())
966         {
967                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
968                 {
969                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
970                         if (PyTuple_Check(args))
971                         {
972                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
973                                 if (ret)
974                                         return ret == Py_True;
975                                 eDebug("call m_selectableFunc failed!!! assume not callable");
976                         }
977                         else
978                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
979                 }
980                 else
981                 {
982                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
983                         if (PyList_Check(item))
984                         {
985                                 item = PyList_GET_ITEM(item, 0);
986                                 if (item != Py_None)
987                                         return 1;
988                         }
989                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
990                                 return 1;
991                 }
992         }
993         return 0;
994 }
995
996 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
997 {
998         if (font)
999                 m_font[fnt] = font;
1000         else
1001                 m_font.erase(fnt);
1002 }
1003
1004 void eListboxPythonMultiContent::setItemHeight(int height)
1005 {
1006         m_itemheight = height;
1007         if (m_listbox)
1008                 m_listbox->setItemHeight(height);
1009 }
1010
1011 void eListboxPythonMultiContent::setList(ePyObject list)
1012 {
1013         m_old_clip = m_clip = gRegion::invalidRegion();
1014         eListboxPythonStringContent::setList(list);
1015 }
1016
1017 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1018 {
1019         if (m_clip.valid())
1020         {
1021                 clip &= m_clip;
1022                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1023                         m_clip -= m_old_clip;
1024                 m_old_clip = m_clip;
1025         }
1026         else
1027                 m_old_clip = m_clip = gRegion::invalidRegion();
1028 }