in TemplatedMultiContent Lists, use the default skin color if color tuple element...
[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()
53         :m_cursor(0), m_itemheight(25)
54 {
55 }
56
57 eListboxPythonStringContent::~eListboxPythonStringContent()
58 {
59         Py_XDECREF(m_list);
60 }
61
62 void eListboxPythonStringContent::cursorHome()
63 {
64         m_cursor = 0;
65 }
66
67 void eListboxPythonStringContent::cursorEnd()
68 {
69         m_cursor = size();
70 }
71
72 int eListboxPythonStringContent::cursorMove(int count)
73 {
74         m_cursor += count;
75         
76         if (m_cursor < 0)
77                 cursorHome();
78         else if (m_cursor > size())
79                 cursorEnd();
80         return 0;
81 }
82
83 int eListboxPythonStringContent::cursorValid()
84 {
85         return m_cursor < size();
86 }
87
88 int eListboxPythonStringContent::cursorSet(int n)
89 {
90         m_cursor = n;
91         
92         if (m_cursor < 0)
93                 cursorHome();
94         else if (m_cursor > size())
95                 cursorEnd();
96         return 0;
97 }
98
99 int eListboxPythonStringContent::cursorGet()
100 {
101         return m_cursor;
102 }
103
104 int eListboxPythonStringContent::currentCursorSelectable()
105 {
106         if (m_list && cursorValid())
107         {
108                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
109                 if (!PyTuple_Check(item))
110                         return 1;
111                 if (PyTuple_Size(item) >= 2)
112                         return 1;
113         }
114         return 0;
115 }
116
117 void eListboxPythonStringContent::cursorSave()
118 {
119         m_saved_cursor = m_cursor;
120 }
121
122 void eListboxPythonStringContent::cursorRestore()
123 {
124         m_cursor = m_saved_cursor;
125 }
126
127 int eListboxPythonStringContent::size()
128 {
129         if (!m_list)
130                 return 0;
131         return PyList_Size(m_list);
132 }
133         
134 void eListboxPythonStringContent::setSize(const eSize &size)
135 {
136         m_itemsize = size;
137 }
138
139 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
140 {
141         ePtr<gFont> fnt = new gFont("Regular", 20);
142         painter.clip(eRect(offset, m_itemsize));
143         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
144
145         eListboxStyle *local_style = 0;
146         bool cursorValid = this->cursorValid();
147
148                 /* get local listbox style, if present */
149         if (m_listbox)
150                 local_style = m_listbox->getLocalStyle();
151
152         if (local_style)
153         {
154                 if (selected)
155                 {
156                         /* if we have a local background color set, use that. */
157                         if (local_style->m_background_color_selected_set)
158                                 painter.setBackgroundColor(local_style->m_background_color_selected);
159                         /* same for foreground */
160                         if (local_style->m_foreground_color_selected_set)
161                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
162                 }
163                 else
164                 {
165                         /* if we have a local background color set, use that. */
166                         if (local_style->m_background_color_set)
167                                 painter.setBackgroundColor(local_style->m_background_color);
168                         /* same for foreground */
169                         if (local_style->m_foreground_color_set)
170                                 painter.setForegroundColor(local_style->m_foreground_color);
171                 }
172         }
173
174         /* if we have no transparent background */
175         if (!local_style || !local_style->m_transparent_background)
176         {
177                         /* blit background picture, if available (otherwise, clear only) */
178                 if (local_style && local_style->m_background && cursorValid)
179                         painter.blit(local_style->m_background, offset, eRect(), 0);
180                 else
181                         painter.clear();
182         } else
183         {
184                 if (local_style->m_background && cursorValid)
185                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
186                 else if (selected && !local_style->m_selection)
187                         painter.clear();
188         }
189
190         if (m_list && cursorValid)
191         {
192                 int gray = 0;
193                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
194                 painter.setFont(fnt);
195
196                         /* the user can supply tuples, in this case the first one will be displayed. */
197                 if (PyTuple_Check(item))
198                 {
199                         if (PyTuple_Size(item) == 1)
200                                 gray = 1;
201                         item = PyTuple_GET_ITEM(item, 0);
202                 }
203
204                 if (selected && local_style && local_style->m_selection)
205                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
206
207                 if (item == Py_None)
208                 {
209                                 /* seperator */
210                         int half_height = m_itemsize.height() / 2;
211                         painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
212                 } else
213                 {
214                         const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
215                         ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
216                         if (gray)
217                                 painter.setForegroundColor(gRGB(0x808080));
218                         painter.renderText(eRect(text_offset, m_itemsize), string);
219                 }
220
221                 if (selected && (!local_style || !local_style->m_selection))
222                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
223         }
224
225         painter.clippop();
226 }
227
228 void eListboxPythonStringContent::setList(ePyObject list)
229 {
230         Py_XDECREF(m_list);
231         if (!PyList_Check(list))
232         {
233                 m_list = ePyObject();
234         } else
235         {
236                 m_list = list;
237                 Py_INCREF(m_list);
238         }
239
240         if (m_listbox)
241                 m_listbox->entryReset(false);
242 }
243
244 PyObject *eListboxPythonStringContent::getCurrentSelection()
245 {
246         if (!(m_list && cursorValid()))
247                 Py_RETURN_NONE;
248
249         ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
250         Py_XINCREF(r);
251         return r;
252 }
253
254 void eListboxPythonStringContent::invalidateEntry(int index)
255 {
256         if (m_listbox)
257                 m_listbox->entryChanged(index);
258 }
259
260 void eListboxPythonStringContent::invalidate()
261 {
262         if (m_listbox)
263         {
264                 int s = size();
265                 if ( m_cursor >= s )
266                         m_listbox->moveSelectionTo(s?s-1:0);
267                 else
268                         m_listbox->invalidate();
269         }
270 }
271
272 //////////////////////////////////////
273
274 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
275 {
276         ePtr<gFont> fnt = new gFont("Regular", 20);
277         ePtr<gFont> fnt2 = new gFont("Regular", 16);
278         eRect itemrect(offset, m_itemsize);
279         eListboxStyle *local_style = 0;
280         bool cursorValid = this->cursorValid();
281
282         painter.clip(itemrect);
283         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
284
285                 /* get local listbox style, if present */
286         if (m_listbox)
287                 local_style = m_listbox->getLocalStyle();
288
289         if (local_style)
290         {
291                 if (selected)
292                 {
293                         /* if we have a local background color set, use that. */
294                         if (local_style->m_background_color_selected_set)
295                                 painter.setBackgroundColor(local_style->m_background_color_selected);
296                         /* same for foreground */
297                         if (local_style->m_foreground_color_selected_set)
298                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
299                 }
300                 else
301                 {
302                         /* if we have a local background color set, use that. */
303                         if (local_style->m_background_color_set)
304                                 painter.setBackgroundColor(local_style->m_background_color);
305                         /* same for foreground */
306                         if (local_style->m_foreground_color_set)
307                                 painter.setForegroundColor(local_style->m_foreground_color);
308                 }
309         }
310
311         if (!local_style || !local_style->m_transparent_background)
312                 /* if we have no transparent background */
313         {
314                 /* blit background picture, if available (otherwise, clear only) */
315                 if (local_style && local_style->m_background && cursorValid)
316                         painter.blit(local_style->m_background, offset, eRect(), 0);
317                 else
318                         painter.clear();
319         } else
320         {
321                 if (local_style->m_background && cursorValid)
322                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
323                 else if (selected && !local_style->m_selection)
324                         painter.clear();
325         }
326
327         if (m_list && cursorValid)
328         {
329                         /* get current list item */
330                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
331                 ePyObject text, value;
332                 painter.setFont(fnt);
333
334                 if (selected && local_style && local_style->m_selection)
335                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
336
337                         /* the first tuple element is a string for the left side.
338                            the second one will be called, and the result shall be an tuple.
339
340                            of this tuple,
341                            the first one is the type (string).
342                            the second one is the value. */
343                 if (PyTuple_Check(item))
344                 {
345                                 /* handle left part. get item from tuple, convert to string, display. */
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                         if (PyTuple_Size(item) >= 2) // when no 2nd entry is in tuple this is a non selectable entry without config part
360                                 value = PyTuple_GET_ITEM(item, 1);
361
362                         if (value)
363                         {
364                                 ePyObject args = PyTuple_New(1);
365                                 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
366
367                                         /* CallObject will call __call__ which should return the value tuple */
368                                 value = PyObject_CallObject(value, args);
369
370                                 if (PyErr_Occurred())
371                                         PyErr_Print();
372
373                                 Py_DECREF(args);
374                                         /* the PyInt was stolen. */
375                         }
376
377                                 /*  check if this is really a tuple */
378                         if (value && PyTuple_Check(value))
379                         {
380                                         /* convert type to string */
381                                 ePyObject type = PyTuple_GET_ITEM(value, 0);
382                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
383
384                                 if (atype)
385                                 {
386                                         if (!strcmp(atype, "text"))
387                                         {
388                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
389                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
390                                                 painter.setFont(fnt2);
391                                                 if (value_alignment_left)
392                                                         painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
393                                                 else
394                                                         painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
395
396                                                         /* pvalue is borrowed */
397                                         } else if (!strcmp(atype, "slider"))
398                                         {
399                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
400                                                 ePyObject psize = PyTuple_GET_ITEM(value, 2);
401
402                                                         /* convert value to Long. fallback to -1 on error. */
403                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
404                                                 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
405
406                                                         /* calc. slider length */
407                                                 int width = item_right.width() * value / size;
408                                                 int height = item_right.height();
409
410
411                                                         /* draw slider */
412                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
413                                                 //hack - make it customizable
414                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
415
416                                                         /* pvalue is borrowed */
417                                         } else if (!strcmp(atype, "mtext"))
418                                         {
419                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
420                                                 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
421                                                 int xoffs = value_alignment_left ? 0 : m_seperation;
422                                                 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
423                                                 para->setFont(fnt2);
424                                                 para->renderString(text, 0);
425                                                 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
426                                                 int glyphs = para->size();
427
428                                                 ePyObject plist;
429
430                                                 if (PyTuple_Size(value) >= 3)
431                                                         plist = PyTuple_GET_ITEM(value, 2);
432
433                                                 int entries = 0;
434
435                                                 if (plist && PyList_Check(plist))
436                                                         entries = PyList_Size(plist);
437
438                                                 int left=0, right=0, last=-1;
439                                                 eRect bbox;
440                                                 for (int i = 0; i < entries; ++i)
441                                                 {
442                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
443                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
444
445                                                         if ((num < 0) || (num >= glyphs))
446                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
447                                                         else
448                                                         {
449                                                                 if (last+1 != num && last != -1) {
450                                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
451                                                                         painter.fill(bbox);
452                                                                 }
453                                                                 para->setGlyphFlag(num, GS_INVERT);
454                                                                 bbox = para->getGlyphBBox(num);
455                                                                 if (last+1 != num || last == -1)
456                                                                         left = bbox.left();
457                                                                 right = bbox.left() + bbox.width();
458                                                                 last = num;
459                                                         }
460                                                                 /* entry is borrowed */
461                                                 }
462                                                 if (last != -1) {
463                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
464                                                         painter.fill(bbox);
465                                                 }
466                                                 painter.renderPara(para, ePoint(0, 0));
467                                                         /* pvalue is borrowed */
468                                                         /* plist is 0 or borrowed */
469                                         }
470                                 }
471                                         /* type is borrowed */
472                         } else if (value)
473                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
474                         if (value)
475                                 Py_DECREF(value);
476                 }
477
478                 if (selected && (!local_style || !local_style->m_selection))
479                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
480         }
481
482         painter.clippop();
483 }
484
485 int eListboxPythonConfigContent::currentCursorSelectable()
486 {
487         return eListboxPythonStringContent::currentCursorSelectable();
488 }
489
490 //////////////////////////////////////
491
492         /* todo: make a real infrastructure here! */
493 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
494
495 eListboxPythonMultiContent::eListboxPythonMultiContent()
496         :m_clip(gRegion::invalidRegion()), m_old_clip(gRegion::invalidRegion())
497 {
498 }
499
500 eListboxPythonMultiContent::~eListboxPythonMultiContent()
501 {
502         Py_XDECREF(m_buildFunc);
503         Py_XDECREF(m_selectableFunc);
504 }
505
506 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
507 {
508         m_selection_clip = rect;
509         if (m_listbox)
510                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
511         if (m_clip.valid())
512                 m_clip |= rect;
513         else
514                 m_clip = rect;
515         if (update && m_listbox)
516                 m_listbox->entryChanged(m_cursor);
517 }
518
519 static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid, bool clear=true)
520 {
521         if (pbackColor)
522         {
523                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColor);
524                 painter.setBackgroundColor(gRGB(color));
525         }
526         else if (local_style)
527         {
528                 if (local_style && local_style->m_background_color_set)
529                         painter.setBackgroundColor(local_style->m_background_color);
530                 if (local_style->m_background && cursorValid)
531                 {
532                         if (local_style->m_transparent_background)
533                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
534                         else
535                                 painter.blit(local_style->m_background, offset, eRect(), 0);
536                         return;
537                 }
538                 else if (local_style->m_transparent_background)
539                         return;
540         }
541         if (clear)
542                 painter.clear();
543 }
544
545 static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid, bool clear=true)
546 {
547         if (pbackColorSelected)
548         {
549                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColorSelected);
550                 painter.setBackgroundColor(gRGB(color));
551         }
552         else if (local_style)
553         {
554                 if (local_style && local_style->m_background_color_selected_set)
555                         painter.setBackgroundColor(local_style->m_background_color_selected);
556                 if (local_style->m_background && cursorValid)
557                 {
558                         if (local_style->m_transparent_background)
559                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
560                         else
561                                 painter.blit(local_style->m_background, offset, eRect(), 0);
562                         return;
563                 }
564         }
565         if (clear)
566                 painter.clear();
567 }
568
569 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, bool clear=true)
570 {
571         if (selected && sel_clip.valid())
572         {
573                 gRegion part = rc - sel_clip;
574                 if (!part.empty())
575                 {
576                         painter.clip(part);
577                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
578                         clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
579                         painter.clippop();
580                         selected = 0;
581                 }
582                 part = rc & sel_clip;
583                 if (!part.empty())
584                 {
585                         painter.clip(part);
586                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
587                         clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
588                         painter.clippop();
589                         selected = 1;
590                 }
591         }
592         else if (selected)
593         {
594                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
595                 clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
596                 if (local_style && local_style->m_selection)
597                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
598         }
599         else
600         {
601                 style.setStyle(painter, eWindowStyle::styleListboxNormal);
602                 clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
603         }
604
605         if (selected)
606         {
607                 if (pforeColorSelected)
608                 {
609                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColorSelected);
610                         painter.setForegroundColor(gRGB(color));
611                 }
612                 /* if we have a local foreground color set, use that. */
613                 else if (local_style && local_style->m_foreground_color_selected_set)
614                         painter.setForegroundColor(local_style->m_foreground_color_selected);
615         }
616         else
617         {
618                 if (pforeColor)
619                 {
620                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColor);
621                         painter.setForegroundColor(gRGB(color));
622                 }
623                 /* if we have a local foreground color set, use that. */
624                 else if (local_style && local_style->m_foreground_color_set)
625                         painter.setForegroundColor(local_style->m_foreground_color);
626         }
627 }
628
629 static ePyObject lookupColor(ePyObject color, ePyObject data)
630 {
631         if (color == Py_None)
632                 return ePyObject();
633
634         if ((!color) && (!data))
635                 return color;
636
637         unsigned int icolor = PyInt_AsUnsignedLongMask(color);
638
639                 /* check if we have the "magic" template color */
640         if ((icolor & 0xFF000000) == 0xFF000000)
641         {
642                 int index = icolor & 0xFFFFFF;
643                 if (PyTuple_GetItem(data, index) == Py_None)
644                         return ePyObject();
645                 return PyTuple_GetItem(data, index);
646         }
647
648         if (color == Py_None)
649                 return ePyObject();
650
651         return color;
652 }
653
654 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
655 {
656         gRegion itemregion(eRect(offset, m_itemsize));
657         eListboxStyle *local_style = 0;
658         eRect sel_clip(m_selection_clip);
659         bool cursorValid = this->cursorValid();
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, cursorValid);
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                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
827                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
828                                 }
829
830                                 painter.setFont(m_font[fnt]);
831                                 painter.renderText(rect, string, flags);
832                                 painter.clippop();
833
834                                 // draw border
835                                 if (bwidth)
836                                 {
837                                         eRect rect(eRect(x, y, width, height));
838                                         painter.clip(rect);
839                                         if (pborderColor)
840                                         {
841                                                 unsigned int color = PyInt_AsUnsignedLongMask(pborderColor);
842                                                 painter.setForegroundColor(gRGB(color));
843                                         }
844
845                                         rect.setRect(x, y, width, bwidth);
846                                         painter.fill(rect);
847
848                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
849                                         painter.fill(rect);
850
851                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
852                                         painter.fill(rect);
853
854                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
855                                         painter.fill(rect);
856
857                                         painter.clippop();
858                                 }
859                                 break;
860                         }
861                         case TYPE_PROGRESS_PIXMAP: // Progress
862                         /*
863                                 (1, x, y, width, height, filled_percent, pixmap [, borderWidth, foreColor, backColor, backColorSelected] )
864                         */
865                         case TYPE_PROGRESS: // Progress
866                         {
867                         /*
868                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
869                         */
870                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
871                                                         py = PyTuple_GET_ITEM(item, 2),
872                                                         pwidth = PyTuple_GET_ITEM(item, 3),
873                                                         pheight = PyTuple_GET_ITEM(item, 4),
874                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
875                                                         ppixmap, pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
876                                 int idx = 6;
877                                 if (type == TYPE_PROGRESS)
878                                 {
879                                         if (!(px && py && pwidth && pheight && pfilled_perc))
880                                         {
881                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
882                                                 goto error_out;
883                                         }
884                                 }
885                                 else
886                                 {
887                                         ppixmap = PyTuple_GET_ITEM(item, idx++);
888                                         if (ppixmap == Py_None)
889                                                 continue;
890                                         if (!(px && py && pwidth && pheight && pfilled_perc, ppixmap))
891                                         {
892                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS_PIXMAP, x, y, width, height, filled percent, pixmap, [,border width, foreColor, backColor, backColorSelected]))");
893                                                 goto error_out;
894                                         }
895                                 }
896
897                                 if (size > idx)
898                                 {
899                                         pborderWidth = PyTuple_GET_ITEM(item, idx++);
900                                         if (pborderWidth == Py_None)
901                                                 pborderWidth = ePyObject();
902                                 }
903                                 if (size > idx)
904                                 {
905                                         pforeColor = PyTuple_GET_ITEM(item, idx++);
906                                         if (pforeColor == Py_None)
907                                                 pforeColor = ePyObject();
908                                 }
909                                 if (size > idx)
910                                 {
911                                         pforeColorSelected = PyTuple_GET_ITEM(item, idx++);
912                                         if (pforeColorSelected == Py_None)
913                                                 pforeColorSelected=ePyObject();
914                                 }
915                                 if (size > idx)
916                                 {
917                                         pbackColor = PyTuple_GET_ITEM(item, idx++);
918                                         if (pbackColor == Py_None)
919                                                 pbackColor=ePyObject();
920                                 }
921                                 if (size > idx)
922                                 {
923                                         pbackColorSelected = PyTuple_GET_ITEM(item, idx++);
924                                         if (pbackColorSelected == Py_None)
925                                                 pbackColorSelected=ePyObject();
926                                 }
927
928                                 int x = PyInt_AsLong(px) + offset.x();
929                                 int y = PyInt_AsLong(py) + offset.y();
930                                 int width = PyInt_AsLong(pwidth);
931                                 int height = PyInt_AsLong(pheight);
932                                 int filled = PyInt_AsLong(pfilled_perc);
933
934                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
935                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
936                                         
937                                                         /* don't do anything if percent out of range */
938                                 if ((filled < 0) || (filled > 100))
939                                         continue;
940
941                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
942
943                                 eRect rect(x, y, width, height);
944                                 painter.clip(rect);
945
946                                 {
947                                         gRegion rc(rect);
948                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
949                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
950                                 }
951
952                                 // border
953                                 rect.setRect(x, y, width, bwidth);
954                                 painter.fill(rect);
955
956                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
957                                 painter.fill(rect);
958
959                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
960                                 painter.fill(rect);
961
962                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
963                                 painter.fill(rect);
964
965                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
966
967                                 // progress
968                                 if (ppixmap)
969                                 {
970                                         ePtr<gPixmap> pixmap;
971                                         if (PyInt_Check(ppixmap) && data) /* if the pixmap is in fact a number, it refers to the data list */
972                                                 ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
973
974                                         if (SwigFromPython(pixmap, ppixmap))
975                                         {
976                                                 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
977                                                 painter.clippop();
978                                                 continue;
979                                         }
980                                         painter.blit(pixmap, rect.topLeft(), rect, 0);
981                                 }
982                                 else
983                                         painter.fill(rect);
984
985                                 painter.clippop();
986                                 break;
987                         }
988                         case TYPE_PIXMAP_ALPHABLEND:
989                         case TYPE_PIXMAP_ALPHATEST:
990                         case TYPE_PIXMAP: // pixmap
991                         {
992                         /*
993                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
994                         */
995
996                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
997                                                         py = PyTuple_GET_ITEM(item, 2),
998                                                         pwidth = PyTuple_GET_ITEM(item, 3),
999                                                         pheight = PyTuple_GET_ITEM(item, 4),
1000                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
1001                                                         pbackColor, pbackColorSelected;
1002
1003                                 if (!(px && py && pwidth && pheight && ppixmap))
1004                                 {
1005                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
1006                                         goto error_out;
1007                                 }
1008
1009                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
1010                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
1011
1012                                                         /* don't do anything if we have 'None' as pixmap */
1013                                 if (ppixmap == Py_None)
1014                                         continue;
1015
1016                                 int x = PyInt_AsLong(px) + offset.x();
1017                                 int y = PyInt_AsLong(py) + offset.y();
1018                                 int width = PyInt_AsLong(pwidth);
1019                                 int height = PyInt_AsLong(pheight);
1020                                 ePtr<gPixmap> pixmap;
1021                                 if (SwigFromPython(pixmap, ppixmap))
1022                                 {
1023                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
1024                                         goto error_out;
1025                                 }
1026
1027                                 if (size > 6)
1028                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
1029
1030                                 if (size > 7)
1031                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
1032
1033                                 eRect rect(x, y, width, height);
1034                                 painter.clip(rect);
1035
1036                                 {
1037                                         gRegion rc(rect);
1038                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
1039                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
1040                                 }
1041
1042                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : (type == TYPE_PIXMAP_ALPHABLEND) ? gPainter::BT_ALPHABLEND : 0);
1043                                 painter.clippop();
1044                                 break;
1045                         }
1046                         default:
1047                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1048                                 goto error_out;
1049                         }
1050                 }
1051         }
1052
1053         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1054                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1055
1056 error_out:
1057         if (buildfunc_ret)
1058                 Py_DECREF(buildfunc_ret);
1059
1060         painter.clippop();
1061 }
1062
1063 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1064 {
1065         Py_XDECREF(m_buildFunc);
1066         m_buildFunc=cb;
1067         Py_XINCREF(m_buildFunc);
1068 }
1069
1070 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1071 {
1072         Py_XDECREF(m_selectableFunc);
1073         m_selectableFunc=cb;
1074         Py_XINCREF(m_selectableFunc);
1075 }
1076
1077 int eListboxPythonMultiContent::currentCursorSelectable()
1078 {
1079         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1080         if (m_list && cursorValid())
1081         {
1082                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1083                 {
1084                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1085                         if (PyTuple_Check(args))
1086                         {
1087                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1088                                 if (ret)
1089                                 {
1090                                         bool retval = ret == Py_True;
1091                                         Py_DECREF(ret);
1092                                         return retval;
1093                                 }
1094                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1095                         }
1096                         else
1097                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1098                 }
1099                 else
1100                 {
1101                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1102                         if (PyList_Check(item))
1103                         {
1104                                 item = PyList_GET_ITEM(item, 0);
1105                                 if (item != Py_None)
1106                                         return 1;
1107                         } else if (PyTuple_Check(item))
1108                         {
1109                                 item = PyTuple_GET_ITEM(item, 0);
1110                                 if (item != Py_None)
1111                                         return 1;
1112                         }
1113                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1114                                 return 1;
1115                 }
1116         }
1117         return 0;
1118 }
1119
1120 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1121 {
1122         if (font)
1123                 m_font[fnt] = font;
1124         else
1125                 m_font.erase(fnt);
1126 }
1127
1128 void eListboxPythonMultiContent::setItemHeight(int height)
1129 {
1130         m_itemheight = height;
1131         if (m_listbox)
1132                 m_listbox->setItemHeight(height);
1133 }
1134
1135 void eListboxPythonMultiContent::setList(ePyObject list)
1136 {
1137         m_old_clip = m_clip = gRegion::invalidRegion();
1138         eListboxPythonStringContent::setList(list);
1139 }
1140
1141 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1142 {
1143         if (m_clip.valid())
1144         {
1145                 clip &= m_clip;
1146                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1147                         m_clip -= m_old_clip;
1148                 m_old_clip = m_clip;
1149         }
1150         else
1151                 m_old_clip = m_clip = gRegion::invalidRegion();
1152 }
1153
1154 void eListboxPythonMultiContent::entryRemoved(int idx)
1155 {
1156         if (m_listbox)
1157                 m_listbox->entryRemoved(idx);
1158 }
1159
1160 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1161 {
1162         m_template = tmplate;
1163 }