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