elistboxcontent.cpp: code cleanup, only draw background pixmap for valid(used) listbo...
[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         bool cursorValid = this->cursorValid();
146
147                 /* get local listbox style, if present */
148         if (m_listbox)
149                 local_style = m_listbox->getLocalStyle();
150
151         if (local_style)
152         {
153                 if (selected)
154                 {
155                         /* if we have a local background color set, use that. */
156                         if (local_style->m_background_color_selected_set)
157                                 painter.setBackgroundColor(local_style->m_background_color_selected);
158                         /* same for foreground */
159                         if (local_style->m_foreground_color_selected_set)
160                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
161                 }
162                 else
163                 {
164                         /* if we have a local background color set, use that. */
165                         if (local_style->m_background_color_set)
166                                 painter.setBackgroundColor(local_style->m_background_color);
167                         /* same for foreground */
168                         if (local_style->m_foreground_color_set)
169                                 painter.setForegroundColor(local_style->m_foreground_color);
170                 }
171         }
172
173         /* if we have no transparent background */
174         if (!local_style || !local_style->m_transparent_background)
175         {
176                         /* blit background picture, if available (otherwise, clear only) */
177                 if (local_style && local_style->m_background && cursorValid)
178                         painter.blit(local_style->m_background, offset, eRect(), 0);
179                 else
180                         painter.clear();
181         } else
182         {
183                 if (local_style->m_background && cursorValid)
184                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
185                 else
186                         painter.clear();
187         }
188
189         if (m_list && cursorValid)
190         {
191                 int gray = 0;
192                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
193                 painter.setFont(fnt);
194
195                         /* the user can supply tuples, in this case the first one will be displayed. */
196                 if (PyTuple_Check(item))
197                 {
198                         if (PyTuple_Size(item) == 1)
199                                 gray = 1;
200                         item = PyTuple_GET_ITEM(item, 0);
201                 }
202
203                 if (selected && local_style && local_style->m_selection)
204                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
205
206                 if (item == Py_None)
207                 {
208                                 /* seperator */
209                         int half_height = m_itemsize.height() / 2;
210                         painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
211                 } else
212                 {
213                         const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
214                         ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
215                         if (gray)
216                                 painter.setForegroundColor(gRGB(0x808080));
217                         painter.renderText(eRect(text_offset, m_itemsize), string);
218                 }
219
220                 if (selected && (!local_style || !local_style->m_selection))
221                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
222         }
223
224         painter.clippop();
225 }
226
227 void eListboxPythonStringContent::setList(ePyObject list)
228 {
229         Py_XDECREF(m_list);
230         if (!PyList_Check(list))
231         {
232                 m_list = ePyObject();
233         } else
234         {
235                 m_list = list;
236                 Py_INCREF(m_list);
237         }
238
239         if (m_listbox)
240                 m_listbox->entryReset(false);
241 }
242
243 PyObject *eListboxPythonStringContent::getCurrentSelection()
244 {
245         if (!(m_list && cursorValid()))
246                 Py_RETURN_NONE;
247
248         ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
249         Py_XINCREF(r);
250         return r;
251 }
252
253 void eListboxPythonStringContent::invalidateEntry(int index)
254 {
255         if (m_listbox)
256                 m_listbox->entryChanged(index);
257 }
258
259 void eListboxPythonStringContent::invalidate()
260 {
261         if (m_listbox)
262         {
263                 int s = size();
264                 if ( m_cursor >= s )
265                         m_listbox->moveSelectionTo(s?s-1:0);
266                 else
267                         m_listbox->invalidate();
268         }
269 }
270
271 //////////////////////////////////////
272
273 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
274 {
275         ePtr<gFont> fnt = new gFont("Regular", 20);
276         ePtr<gFont> fnt2 = new gFont("Regular", 16);
277         eRect itemrect(offset, m_itemsize);
278         eListboxStyle *local_style = 0;
279         bool cursorValid = this->cursorValid();
280
281         painter.clip(itemrect);
282         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
283
284                 /* get local listbox style, if present */
285         if (m_listbox)
286                 local_style = m_listbox->getLocalStyle();
287
288         if (local_style)
289         {
290                 if (selected)
291                 {
292                         /* if we have a local background color set, use that. */
293                         if (local_style->m_background_color_selected_set)
294                                 painter.setBackgroundColor(local_style->m_background_color_selected);
295                         /* same for foreground */
296                         if (local_style->m_foreground_color_selected_set)
297                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
298                 }
299                 else
300                 {
301                         /* if we have a local background color set, use that. */
302                         if (local_style->m_background_color_set)
303                                 painter.setBackgroundColor(local_style->m_background_color);
304                         /* same for foreground */
305                         if (local_style->m_foreground_color_set)
306                                 painter.setForegroundColor(local_style->m_foreground_color);
307                 }
308         }
309
310         if (!local_style || !local_style->m_transparent_background)
311                 /* if we have no transparent background */
312         {
313                 /* blit background picture, if available (otherwise, clear only) */
314                 if (local_style && local_style->m_background && cursorValid)
315                         painter.blit(local_style->m_background, offset, eRect(), 0);
316                 else
317                         painter.clear();
318         } else
319         {
320                 if (local_style->m_background && cursorValid)
321                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
322                 else
323                         painter.clear();
324         }
325
326         if (m_list && cursorValid)
327         {
328                         /* get current list item */
329                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
330                 ePyObject text, value;
331                 painter.setFont(fnt);
332
333                 if (selected && local_style && local_style->m_selection)
334                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
335
336                         /* the first tuple element is a string for the left side.
337                            the second one will be called, and the result shall be an tuple.
338
339                            of this tuple,
340                            the first one is the type (string).
341                            the second one is the value. */
342                 if (PyTuple_Check(item))
343                 {
344                                 /* handle left part. get item from tuple, convert to string, display. */
345
346                         text = PyTuple_GET_ITEM(item, 0);
347                         text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
348                         const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
349                         eSize item_left = eSize(m_seperation, m_itemsize.height());
350                         eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
351                         painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
352                         Py_XDECREF(text);
353
354                                 /* when we have no label, align value to the left. (FIXME:
355                                    don't we want to specifiy this individually?) */
356                         int value_alignment_left = !*string;
357
358                                 /* now, handle the value. get 2nd part from tuple*/
359                         value = PyTuple_GET_ITEM(item, 1);
360                         if (value)
361                         {
362                                 ePyObject args = PyTuple_New(1);
363                                 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
364
365                                         /* CallObject will call __call__ which should return the value tuple */
366                                 value = PyObject_CallObject(value, args);
367
368                                 if (PyErr_Occurred())
369                                         PyErr_Print();
370
371                                 Py_DECREF(args);
372                                         /* the PyInt was stolen. */
373                         }
374
375                                 /*  check if this is really a tuple */
376                         if (value && PyTuple_Check(value))
377                         {
378                                         /* convert type to string */
379                                 ePyObject type = PyTuple_GET_ITEM(value, 0);
380                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
381
382                                 if (atype)
383                                 {
384                                         if (!strcmp(atype, "text"))
385                                         {
386                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
387                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
388                                                 painter.setFont(fnt2);
389                                                 if (value_alignment_left)
390                                                         painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
391                                                 else
392                                                         painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
393
394                                                         /* pvalue is borrowed */
395                                         } else if (!strcmp(atype, "slider"))
396                                         {
397                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
398                                                 ePyObject psize = PyTuple_GET_ITEM(value, 2);
399
400                                                         /* convert value to Long. fallback to -1 on error. */
401                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
402                                                 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
403
404                                                         /* calc. slider length */
405                                                 int width = item_right.width() * value / size;
406                                                 int height = item_right.height();
407
408
409                                                         /* draw slider */
410                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
411                                                 //hack - make it customizable
412                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
413
414                                                         /* pvalue is borrowed */
415                                         } else if (!strcmp(atype, "mtext"))
416                                         {
417                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
418                                                 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
419                                                 int xoffs = value_alignment_left ? 0 : m_seperation;
420                                                 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
421                                                 para->setFont(fnt2);
422                                                 para->renderString(text, 0);
423                                                 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
424                                                 int glyphs = para->size();
425
426                                                 ePyObject plist;
427
428                                                 if (PyTuple_Size(value) >= 3)
429                                                         plist = PyTuple_GET_ITEM(value, 2);
430
431                                                 int entries = 0;
432
433                                                 if (plist && PyList_Check(plist))
434                                                         entries = PyList_Size(plist);
435
436                                                 int left=0, right=0, last=-1;
437                                                 eRect bbox;
438                                                 for (int i = 0; i < entries; ++i)
439                                                 {
440                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
441                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
442
443                                                         if ((num < 0) || (num >= glyphs))
444                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
445                                                         else
446                                                         {
447                                                                 if (last+1 != num && last != -1) {
448                                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
449                                                                         painter.fill(bbox);
450                                                                 }
451                                                                 para->setGlyphFlag(num, GS_INVERT);
452                                                                 bbox = para->getGlyphBBox(num);
453                                                                 if (last+1 != num || last == -1)
454                                                                         left = bbox.left();
455                                                                 right = bbox.left() + bbox.width();
456                                                                 last = num;
457                                                         }
458                                                                 /* entry is borrowed */
459                                                 }
460                                                 if (last != -1) {
461                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
462                                                         painter.fill(bbox);
463                                                 }
464                                                 painter.renderPara(para, ePoint(0, 0));
465                                                         /* pvalue is borrowed */
466                                                         /* plist is 0 or borrowed */
467                                         }
468                                 }
469                                         /* type is borrowed */
470                         } else
471                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
472                         if (value)
473                                 Py_DECREF(value);
474                 }
475
476                 if (selected && (!local_style || !local_style->m_selection))
477                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
478         }
479
480         painter.clippop();
481 }
482
483 int eListboxPythonConfigContent::currentCursorSelectable()
484 {
485         return eListboxPythonStringContent::currentCursorSelectable();
486 }
487
488 //////////////////////////////////////
489
490         /* todo: make a real infrastructure here! */
491 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
492
493 eListboxPythonMultiContent::eListboxPythonMultiContent()
494         :m_clip(gRegion::invalidRegion()), m_old_clip(gRegion::invalidRegion())
495 {
496 }
497
498 eListboxPythonMultiContent::~eListboxPythonMultiContent()
499 {
500         Py_XDECREF(m_buildFunc);
501         Py_XDECREF(m_selectableFunc);
502 }
503
504 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
505 {
506         m_selection_clip = rect;
507         if (m_listbox)
508                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
509         if (m_clip.valid())
510                 m_clip |= rect;
511         else
512                 m_clip = rect;
513         if (update && m_listbox)
514                 m_listbox->entryChanged(m_cursor);
515 }
516
517 static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid)
518 {
519         if (pbackColor)
520         {
521                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColor);
522                 painter.setBackgroundColor(gRGB(color));
523         }
524         else if (local_style)
525         {
526                 if (local_style && local_style->m_background_color_set)
527                         painter.setBackgroundColor(local_style->m_background_color);
528                 if (local_style->m_background && cursorValid)
529                 {
530                         if (local_style->m_transparent_background)
531                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
532                         else
533                                 painter.blit(local_style->m_background, offset, eRect(), 0);
534                         return;
535                 }
536         }
537         painter.clear();
538 }
539
540 static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid)
541 {
542         if (pbackColorSelected)
543         {
544                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColorSelected);
545                 painter.setBackgroundColor(gRGB(color));
546         }
547         else if (local_style)
548         {
549                 if (local_style && local_style->m_background_color_selected_set)
550                         painter.setBackgroundColor(local_style->m_background_color_selected);
551                 if (local_style->m_background && cursorValid)
552                 {
553                         if (local_style->m_transparent_background)
554                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
555                         else
556                                 painter.blit(local_style->m_background, offset, eRect(), 0);
557                         return;
558                 }
559         }
560         painter.clear();
561 }
562
563 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, const ePoint &offset, bool cursorValid)
564 {
565         if (selected && sel_clip.valid())
566         {
567                 gRegion part = rc - sel_clip;
568                 if (!part.empty())
569                 {
570                         painter.clip(part);
571                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
572                         clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid);
573                         painter.clippop();
574                         selected = 0;
575                 }
576                 part = rc & sel_clip;
577                 if (!part.empty())
578                 {
579                         painter.clip(part);
580                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
581                         clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid);
582                         painter.clippop();
583                         selected = 1;
584                 }
585         }
586         else if (selected)
587         {
588                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
589                 clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid);
590                 if (local_style && local_style->m_selection)
591                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
592         }
593         else
594         {
595                 style.setStyle(painter, eWindowStyle::styleListboxNormal);
596                 clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid);
597         }
598
599         if (selected)
600         {
601                 if (pforeColorSelected)
602                 {
603                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColorSelected);
604                         painter.setForegroundColor(gRGB(color));
605                 }
606                 /* if we have a local foreground color set, use that. */
607                 else if (local_style && local_style->m_foreground_color_selected_set)
608                         painter.setForegroundColor(local_style->m_foreground_color_selected);
609         }
610         else
611         {
612                 if (pforeColor)
613                 {
614                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColor);
615                         painter.setForegroundColor(gRGB(color));
616                 }
617                 /* if we have a local foreground color set, use that. */
618                 else if (local_style && local_style->m_foreground_color_set)
619                         painter.setForegroundColor(local_style->m_foreground_color);
620         }
621 }
622
623 static ePyObject lookupColor(ePyObject color, ePyObject data)
624 {
625         if (color == Py_None)
626                 return ePyObject();
627
628         if ((!color) && (!data))
629                 return color;
630
631         unsigned int icolor = PyInt_AsUnsignedLongMask(color);
632
633                 /* check if we have the "magic" template color */
634         if ((icolor & 0xFF000000) == 0xFF000000)
635         {
636                 int index = icolor & 0xFFFFFF;
637                 eDebug("[eListboxPythonMultiContent] template color index: %d", index);
638                 return PyTuple_GetItem(data, index);
639         }
640
641         if (color == Py_None)
642                 return ePyObject();
643
644         return color;
645 }
646
647 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
648 {
649         gRegion itemregion(eRect(offset, m_itemsize));
650         eListboxStyle *local_style = 0;
651         eRect sel_clip(m_selection_clip);
652         bool cursorValid = this->cursorValid();
653         if (sel_clip.valid())
654                 sel_clip.moveBy(offset);
655
656                 /* get local listbox style, if present */
657         if (m_listbox)
658                 local_style = m_listbox->getLocalStyle();
659
660         painter.clip(itemregion);
661         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip, offset, cursorValid);
662
663         ePyObject items, buildfunc_ret;
664
665         if (m_list && cursorValid)
666         {
667                         /* a multicontent list can be used in two ways:
668                                 either each item is a list of (TYPE,...)-tuples,
669                                 or there is a template defined, which is a list of (TYPE,...)-tuples,
670                                 and the list is an unformatted tuple. The template then references items from the list.
671                         */
672                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
673
674                 if (m_buildFunc)
675                 {
676                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
677                         {
678                                 if (PyTuple_Check(items))
679                                         buildfunc_ret = items = PyObject_CallObject(m_buildFunc, items);
680                                 else
681                                         eDebug("items is no tuple");
682                         }
683                         else
684                                 eDebug("buildfunc is not callable");
685                 }
686
687                 if (!items)
688                 {
689                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
690                         goto error_out;
691                 }
692
693                 if (!m_template)
694                 {
695                         if (!PyList_Check(items))
696                         {
697                                 eDebug("eListboxPythonMultiContent: list entry %d is not a list (non-templated)", m_cursor);
698                                 goto error_out;
699                         }
700                 } else
701                 {
702                         if (!PyTuple_Check(items))
703                         {
704                                 eDebug("eListboxPythonMultiContent: list entry %d is not a tuple (templated)", m_cursor);
705                                 goto error_out;
706                         }
707                 }
708
709                 ePyObject data;
710
711                         /* if we have a template, use the template for the actual formatting. 
712                                 we will later detect that "data" is present, and refer to that, instead
713                                 of the immediate value. */
714                 int start = 1;
715                 if (m_template)
716                 {
717                         data = items;
718                         items = m_template;
719                         start = 0;
720                 }
721
722                 int size = PyList_Size(items);
723                 for (int i = start; i < size; ++i)
724                 {
725                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
726
727                         if (!item)
728                         {
729                                 eDebug("eListboxPythonMultiContent: ?");
730                                 goto error_out;
731                         }
732
733                         if (!PyTuple_Check(item))
734                         {
735                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
736                                 goto error_out;
737                         }
738
739                         int size = PyTuple_Size(item);
740
741                         if (!size)
742                         {
743                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
744                                 goto error_out;
745                         }
746
747                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
748
749                         switch (type)
750                         {
751                         case TYPE_TEXT: // text
752                         {
753                         /*
754                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
755                         */
756                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
757                                                         py = PyTuple_GET_ITEM(item, 2),
758                                                         pwidth = PyTuple_GET_ITEM(item, 3),
759                                                         pheight = PyTuple_GET_ITEM(item, 4),
760                                                         pfnt = PyTuple_GET_ITEM(item, 5),
761                                                         pflags = PyTuple_GET_ITEM(item, 6),
762                                                         pstring = PyTuple_GET_ITEM(item, 7),
763                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
764
765                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
766                                 {
767                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
768                                         goto error_out;
769                                 }
770
771                                 if (size > 8)
772                                         pforeColor = lookupColor(PyTuple_GET_ITEM(item, 8), data);
773
774                                 if (size > 9)
775                                         pforeColorSelected = lookupColor(PyTuple_GET_ITEM(item, 9), data);
776
777                                 if (size > 10)
778                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 10), data);
779
780                                 if (size > 11)
781                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 11), data);
782
783                                 if (size > 12)
784                                 {
785                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
786                                         if (pborderWidth == Py_None)
787                                                 pborderWidth=ePyObject();
788                                 }
789                                 if (size > 13)
790                                         pborderColor = lookupColor(PyTuple_GET_ITEM(item, 13), data);
791
792                                 if (PyInt_Check(pstring) && data) /* if the string is in fact a number, it refers to the 'data' list. */
793                                         pstring = PyTuple_GetItem(data, PyInt_AsLong(pstring));
794
795                                                         /* don't do anything if we have 'None' as string */
796                                 if (pstring == Py_None)
797                                         continue;
798
799                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
800                                 int x = PyInt_AsLong(px) + offset.x();
801                                 int y = PyInt_AsLong(py) + offset.y();
802                                 int width = PyInt_AsLong(pwidth);
803                                 int height = PyInt_AsLong(pheight);
804                                 int flags = PyInt_AsLong(pflags);
805                                 int fnt = PyInt_AsLong(pfnt);
806                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
807
808                                 if (m_font.find(fnt) == m_font.end())
809                                 {
810                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
811                                         goto error_out;
812                                 }
813
814                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
815                                 painter.clip(rect);
816
817                                 {
818                                         gRegion rc(rect);
819                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
820                                 }
821
822                                 painter.setFont(m_font[fnt]);
823                                 painter.renderText(rect, string, flags);
824                                 painter.clippop();
825
826                                 // draw border
827                                 if (bwidth)
828                                 {
829                                         eRect rect(eRect(x, y, width, height));
830                                         painter.clip(rect);
831                                         if (pborderColor)
832                                         {
833                                                 unsigned int color = PyInt_AsUnsignedLongMask(pborderColor);
834                                                 painter.setForegroundColor(gRGB(color));
835                                         }
836
837                                         rect.setRect(x, y, width, bwidth);
838                                         painter.fill(rect);
839
840                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
841                                         painter.fill(rect);
842
843                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
844                                         painter.fill(rect);
845
846                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
847                                         painter.fill(rect);
848
849                                         painter.clippop();
850                                 }
851                                 break;
852                         }
853                         case TYPE_PROGRESS: // Progress
854                         {
855                         /*
856                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
857                         */
858                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
859                                                         py = PyTuple_GET_ITEM(item, 2),
860                                                         pwidth = PyTuple_GET_ITEM(item, 3),
861                                                         pheight = PyTuple_GET_ITEM(item, 4),
862                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
863                                                         pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
864
865                                 if (!(px && py && pwidth && pheight && pfilled_perc))
866                                 {
867                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
868                                         goto error_out;
869                                 }
870
871                                 if (size > 6)
872                                 {
873                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
874                                         if (pborderWidth == Py_None)
875                                                 pborderWidth = ePyObject();
876                                 }
877                                 if (size > 7)
878                                 {
879                                         pforeColor = PyTuple_GET_ITEM(item, 7);
880                                         if (pforeColor == Py_None)
881                                                 pforeColor = ePyObject();
882                                 }
883                                 if (size > 8)
884                                 {
885                                         pforeColorSelected = PyTuple_GET_ITEM(item, 8);
886                                         if (pforeColorSelected == Py_None)
887                                                 pforeColorSelected=ePyObject();
888                                 }
889                                 if (size > 9)
890                                 {
891                                         pbackColor = PyTuple_GET_ITEM(item, 9);
892                                         if (pbackColor == Py_None)
893                                                 pbackColor=ePyObject();
894                                 }
895                                 if (size > 10)
896                                 {
897                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
898                                         if (pbackColorSelected == Py_None)
899                                                 pbackColorSelected=ePyObject();
900                                 }
901
902                                 int x = PyInt_AsLong(px) + offset.x();
903                                 int y = PyInt_AsLong(py) + offset.y();
904                                 int width = PyInt_AsLong(pwidth);
905                                 int height = PyInt_AsLong(pheight);
906                                 int filled = PyInt_AsLong(pfilled_perc);
907
908                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
909                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
910
911                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
912
913                                 eRect rect(x, y, width, height);
914                                 painter.clip(rect);
915
916                                 {
917                                         gRegion rc(rect);
918                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
919                                 }
920
921                                 // border
922                                 rect.setRect(x, y, width, bwidth);
923                                 painter.fill(rect);
924
925                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
926                                 painter.fill(rect);
927
928                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
929                                 painter.fill(rect);
930
931                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
932                                 painter.fill(rect);
933
934                                 // progress
935                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
936                                 painter.fill(rect);
937
938                                 painter.clippop();
939
940                                 break;
941                         }
942                         case TYPE_PIXMAP_ALPHATEST:
943                         case TYPE_PIXMAP: // pixmap
944                         {
945                         /*
946                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
947                         */
948
949                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
950                                                         py = PyTuple_GET_ITEM(item, 2),
951                                                         pwidth = PyTuple_GET_ITEM(item, 3),
952                                                         pheight = PyTuple_GET_ITEM(item, 4),
953                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
954                                                         pbackColor, pbackColorSelected;
955
956                                 if (!(px && py && pwidth && pheight && ppixmap))
957                                 {
958                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
959                                         goto error_out;
960                                 }
961
962                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
963                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
964
965                                                         /* don't do anything if we have 'None' as pixmap */
966                                 if (ppixmap == Py_None)
967                                         continue;
968
969                                 int x = PyInt_AsLong(px) + offset.x();
970                                 int y = PyInt_AsLong(py) + offset.y();
971                                 int width = PyInt_AsLong(pwidth);
972                                 int height = PyInt_AsLong(pheight);
973                                 ePtr<gPixmap> pixmap;
974                                 if (SwigFromPython(pixmap, ppixmap))
975                                 {
976                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
977                                         goto error_out;
978                                 }
979
980                                 if (size > 6)
981                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
982
983                                 if (size > 7)
984                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
985
986                                 eRect rect(x, y, width, height);
987                                 painter.clip(rect);
988
989                                 {
990                                         gRegion rc(rect);
991                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
992                                 }
993
994                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
995                                 painter.clippop();
996                                 break;
997                         }
998                         default:
999                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1000                                 goto error_out;
1001                         }
1002                 }
1003         }
1004
1005         if (selected && (!local_style || !local_style->m_selection))
1006                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1007
1008 error_out:
1009         if (buildfunc_ret)
1010                 Py_DECREF(buildfunc_ret);
1011
1012         painter.clippop();
1013 }
1014
1015 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1016 {
1017         Py_XDECREF(m_buildFunc);
1018         m_buildFunc=cb;
1019         Py_XINCREF(m_buildFunc);
1020 }
1021
1022 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1023 {
1024         Py_XDECREF(m_selectableFunc);
1025         m_selectableFunc=cb;
1026         Py_XINCREF(m_selectableFunc);
1027 }
1028
1029 int eListboxPythonMultiContent::currentCursorSelectable()
1030 {
1031         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1032         if (m_list && cursorValid())
1033         {
1034                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1035                 {
1036                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1037                         if (PyTuple_Check(args))
1038                         {
1039                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1040                                 if (ret)
1041                                 {
1042                                         bool retval = ret == Py_True;
1043                                         Py_DECREF(ret);
1044                                         return ret;
1045                                 }
1046                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1047                         }
1048                         else
1049                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1050                 }
1051                 else
1052                 {
1053                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1054                         if (PyList_Check(item))
1055                         {
1056                                 item = PyList_GET_ITEM(item, 0);
1057                                 if (item != Py_None)
1058                                         return 1;
1059                         } else if (PyTuple_Check(item))
1060                         {
1061                                 item = PyTuple_GET_ITEM(item, 0);
1062                                 if (item != Py_None)
1063                                         return 1;
1064                         }
1065                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1066                                 return 1;
1067                 }
1068         }
1069         return 0;
1070 }
1071
1072 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1073 {
1074         if (font)
1075                 m_font[fnt] = font;
1076         else
1077                 m_font.erase(fnt);
1078 }
1079
1080 void eListboxPythonMultiContent::setItemHeight(int height)
1081 {
1082         m_itemheight = height;
1083         if (m_listbox)
1084                 m_listbox->setItemHeight(height);
1085 }
1086
1087 void eListboxPythonMultiContent::setList(ePyObject list)
1088 {
1089         m_old_clip = m_clip = gRegion::invalidRegion();
1090         eListboxPythonStringContent::setList(list);
1091 }
1092
1093 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1094 {
1095         if (m_clip.valid())
1096         {
1097                 clip &= m_clip;
1098                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1099                         m_clip -= m_old_clip;
1100                 m_old_clip = m_clip;
1101         }
1102         else
1103                 m_old_clip = m_clip = gRegion::invalidRegion();
1104 }
1105
1106 void eListboxPythonMultiContent::entryRemoved(int idx)
1107 {
1108         if (m_listbox)
1109                 m_listbox->entryRemoved(idx);
1110 }
1111
1112 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1113 {
1114         m_template = tmplate;
1115 }