Merge branch 'bug_530_add_dm800se_support' into experimental
[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                 eDebug("[eListboxPythonMultiContent] template color index: %d", index);
644                 return PyTuple_GetItem(data, index);
645         }
646
647         if (color == Py_None)
648                 return ePyObject();
649
650         return color;
651 }
652
653 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
654 {
655         gRegion itemregion(eRect(offset, m_itemsize));
656         eListboxStyle *local_style = 0;
657         eRect sel_clip(m_selection_clip);
658         bool cursorValid = this->cursorValid();
659         if (sel_clip.valid())
660                 sel_clip.moveBy(offset);
661
662                 /* get local listbox style, if present */
663         if (m_listbox)
664                 local_style = m_listbox->getLocalStyle();
665
666         painter.clip(itemregion);
667         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip, offset, cursorValid);
668
669         ePyObject items, buildfunc_ret;
670
671         if (m_list && cursorValid)
672         {
673                         /* a multicontent list can be used in two ways:
674                                 either each item is a list of (TYPE,...)-tuples,
675                                 or there is a template defined, which is a list of (TYPE,...)-tuples,
676                                 and the list is an unformatted tuple. The template then references items from the list.
677                         */
678                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
679
680                 if (m_buildFunc)
681                 {
682                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
683                         {
684                                 if (PyTuple_Check(items))
685                                         buildfunc_ret = items = PyObject_CallObject(m_buildFunc, items);
686                                 else
687                                         eDebug("items is no tuple");
688                         }
689                         else
690                                 eDebug("buildfunc is not callable");
691                 }
692
693                 if (!items)
694                 {
695                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
696                         goto error_out;
697                 }
698
699                 if (!m_template)
700                 {
701                         if (!PyList_Check(items))
702                         {
703                                 eDebug("eListboxPythonMultiContent: list entry %d is not a list (non-templated)", m_cursor);
704                                 goto error_out;
705                         }
706                 } else
707                 {
708                         if (!PyTuple_Check(items))
709                         {
710                                 eDebug("eListboxPythonMultiContent: list entry %d is not a tuple (templated)", m_cursor);
711                                 goto error_out;
712                         }
713                 }
714
715                 ePyObject data;
716
717                         /* if we have a template, use the template for the actual formatting. 
718                                 we will later detect that "data" is present, and refer to that, instead
719                                 of the immediate value. */
720                 int start = 1;
721                 if (m_template)
722                 {
723                         data = items;
724                         items = m_template;
725                         start = 0;
726                 }
727
728                 int size = PyList_Size(items);
729                 for (int i = start; i < size; ++i)
730                 {
731                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
732
733                         if (!item)
734                         {
735                                 eDebug("eListboxPythonMultiContent: ?");
736                                 goto error_out;
737                         }
738
739                         if (!PyTuple_Check(item))
740                         {
741                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
742                                 goto error_out;
743                         }
744
745                         int size = PyTuple_Size(item);
746
747                         if (!size)
748                         {
749                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
750                                 goto error_out;
751                         }
752
753                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
754
755                         switch (type)
756                         {
757                         case TYPE_TEXT: // text
758                         {
759                         /*
760                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
761                         */
762                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
763                                                         py = PyTuple_GET_ITEM(item, 2),
764                                                         pwidth = PyTuple_GET_ITEM(item, 3),
765                                                         pheight = PyTuple_GET_ITEM(item, 4),
766                                                         pfnt = PyTuple_GET_ITEM(item, 5),
767                                                         pflags = PyTuple_GET_ITEM(item, 6),
768                                                         pstring = PyTuple_GET_ITEM(item, 7),
769                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
770
771                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
772                                 {
773                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
774                                         goto error_out;
775                                 }
776
777                                 if (size > 8)
778                                         pforeColor = lookupColor(PyTuple_GET_ITEM(item, 8), data);
779
780                                 if (size > 9)
781                                         pforeColorSelected = lookupColor(PyTuple_GET_ITEM(item, 9), data);
782
783                                 if (size > 10)
784                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 10), data);
785
786                                 if (size > 11)
787                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 11), data);
788
789                                 if (size > 12)
790                                 {
791                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
792                                         if (pborderWidth == Py_None)
793                                                 pborderWidth=ePyObject();
794                                 }
795                                 if (size > 13)
796                                         pborderColor = lookupColor(PyTuple_GET_ITEM(item, 13), data);
797
798                                 if (PyInt_Check(pstring) && data) /* if the string is in fact a number, it refers to the 'data' list. */
799                                         pstring = PyTuple_GetItem(data, PyInt_AsLong(pstring));
800
801                                                         /* don't do anything if we have 'None' as string */
802                                 if (pstring == Py_None)
803                                         continue;
804
805                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
806                                 int x = PyInt_AsLong(px) + offset.x();
807                                 int y = PyInt_AsLong(py) + offset.y();
808                                 int width = PyInt_AsLong(pwidth);
809                                 int height = PyInt_AsLong(pheight);
810                                 int flags = PyInt_AsLong(pflags);
811                                 int fnt = PyInt_AsLong(pfnt);
812                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
813
814                                 if (m_font.find(fnt) == m_font.end())
815                                 {
816                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
817                                         goto error_out;
818                                 }
819
820                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
821                                 painter.clip(rect);
822
823                                 {
824                                         gRegion rc(rect);
825                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
826                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
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_PIXMAP: // Progress
861                         /*
862                                 (1, x, y, width, height, filled_percent, pixmap [, borderWidth, foreColor, backColor, backColorSelected] )
863                         */
864                         case TYPE_PROGRESS: // Progress
865                         {
866                         /*
867                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
868                         */
869                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
870                                                         py = PyTuple_GET_ITEM(item, 2),
871                                                         pwidth = PyTuple_GET_ITEM(item, 3),
872                                                         pheight = PyTuple_GET_ITEM(item, 4),
873                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
874                                                         ppixmap, pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
875                                 int idx = 6;
876                                 if (type == TYPE_PROGRESS)
877                                 {
878                                         if (!(px && py && pwidth && pheight && pfilled_perc))
879                                         {
880                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
881                                                 goto error_out;
882                                         }
883                                 }
884                                 else
885                                 {
886                                         ppixmap = PyTuple_GET_ITEM(item, idx++);
887                                         if (ppixmap == Py_None)
888                                                 continue;
889                                         if (!(px && py && pwidth && pheight && pfilled_perc, ppixmap))
890                                         {
891                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS_PIXMAP, x, y, width, height, filled percent, pixmap, [,border width, foreColor, backColor, backColorSelected]))");
892                                                 goto error_out;
893                                         }
894                                 }
895
896                                 if (size > idx)
897                                 {
898                                         pborderWidth = PyTuple_GET_ITEM(item, idx++);
899                                         if (pborderWidth == Py_None)
900                                                 pborderWidth = ePyObject();
901                                 }
902                                 if (size > idx)
903                                 {
904                                         pforeColor = PyTuple_GET_ITEM(item, idx++);
905                                         if (pforeColor == Py_None)
906                                                 pforeColor = ePyObject();
907                                 }
908                                 if (size > idx)
909                                 {
910                                         pforeColorSelected = PyTuple_GET_ITEM(item, idx++);
911                                         if (pforeColorSelected == Py_None)
912                                                 pforeColorSelected=ePyObject();
913                                 }
914                                 if (size > idx)
915                                 {
916                                         pbackColor = PyTuple_GET_ITEM(item, idx++);
917                                         if (pbackColor == Py_None)
918                                                 pbackColor=ePyObject();
919                                 }
920                                 if (size > idx)
921                                 {
922                                         pbackColorSelected = PyTuple_GET_ITEM(item, idx++);
923                                         if (pbackColorSelected == Py_None)
924                                                 pbackColorSelected=ePyObject();
925                                 }
926
927                                 int x = PyInt_AsLong(px) + offset.x();
928                                 int y = PyInt_AsLong(py) + offset.y();
929                                 int width = PyInt_AsLong(pwidth);
930                                 int height = PyInt_AsLong(pheight);
931                                 int filled = PyInt_AsLong(pfilled_perc);
932
933                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
934                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
935                                         
936                                                         /* don't do anything if percent out of range */
937                                 if ((filled < 0) || (filled > 100))
938                                         continue;
939
940                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
941
942                                 eRect rect(x, y, width, height);
943                                 painter.clip(rect);
944
945                                 {
946                                         gRegion rc(rect);
947                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
948                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
949                                 }
950
951                                 // border
952                                 rect.setRect(x, y, width, bwidth);
953                                 painter.fill(rect);
954
955                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
956                                 painter.fill(rect);
957
958                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
959                                 painter.fill(rect);
960
961                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
962                                 painter.fill(rect);
963
964                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
965
966                                 // progress
967                                 if (ppixmap)
968                                 {
969                                         ePtr<gPixmap> pixmap;
970                                         if (SwigFromPython(pixmap, ppixmap))
971                                         {
972                                                 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
973                                                 painter.clippop();
974                                                 continue;
975                                         }
976                                         painter.blit(pixmap, rect.topLeft(), rect, 0);
977                                 }
978                                 else
979                                         painter.fill(rect);
980
981                                 painter.clippop();
982                                 break;
983                         }
984                         case TYPE_PIXMAP_ALPHABLEND:
985                         case TYPE_PIXMAP_ALPHATEST:
986                         case TYPE_PIXMAP: // pixmap
987                         {
988                         /*
989                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
990                         */
991
992                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
993                                                         py = PyTuple_GET_ITEM(item, 2),
994                                                         pwidth = PyTuple_GET_ITEM(item, 3),
995                                                         pheight = PyTuple_GET_ITEM(item, 4),
996                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
997                                                         pbackColor, pbackColorSelected;
998
999                                 if (!(px && py && pwidth && pheight && ppixmap))
1000                                 {
1001                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
1002                                         goto error_out;
1003                                 }
1004
1005                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
1006                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
1007
1008                                                         /* don't do anything if we have 'None' as pixmap */
1009                                 if (ppixmap == Py_None)
1010                                         continue;
1011
1012                                 int x = PyInt_AsLong(px) + offset.x();
1013                                 int y = PyInt_AsLong(py) + offset.y();
1014                                 int width = PyInt_AsLong(pwidth);
1015                                 int height = PyInt_AsLong(pheight);
1016                                 ePtr<gPixmap> pixmap;
1017                                 if (SwigFromPython(pixmap, ppixmap))
1018                                 {
1019                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
1020                                         goto error_out;
1021                                 }
1022
1023                                 if (size > 6)
1024                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
1025
1026                                 if (size > 7)
1027                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
1028
1029                                 eRect rect(x, y, width, height);
1030                                 painter.clip(rect);
1031
1032                                 {
1033                                         gRegion rc(rect);
1034                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
1035                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
1036                                 }
1037
1038                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : (type == TYPE_PIXMAP_ALPHABLEND) ? gPainter::BT_ALPHABLEND : 0);
1039                                 painter.clippop();
1040                                 break;
1041                         }
1042                         default:
1043                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1044                                 goto error_out;
1045                         }
1046                 }
1047         }
1048
1049         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1050                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1051
1052 error_out:
1053         if (buildfunc_ret)
1054                 Py_DECREF(buildfunc_ret);
1055
1056         painter.clippop();
1057 }
1058
1059 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1060 {
1061         Py_XDECREF(m_buildFunc);
1062         m_buildFunc=cb;
1063         Py_XINCREF(m_buildFunc);
1064 }
1065
1066 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1067 {
1068         Py_XDECREF(m_selectableFunc);
1069         m_selectableFunc=cb;
1070         Py_XINCREF(m_selectableFunc);
1071 }
1072
1073 int eListboxPythonMultiContent::currentCursorSelectable()
1074 {
1075         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1076         if (m_list && cursorValid())
1077         {
1078                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1079                 {
1080                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1081                         if (PyTuple_Check(args))
1082                         {
1083                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1084                                 if (ret)
1085                                 {
1086                                         bool retval = ret == Py_True;
1087                                         Py_DECREF(ret);
1088                                         return retval;
1089                                 }
1090                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1091                         }
1092                         else
1093                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1094                 }
1095                 else
1096                 {
1097                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1098                         if (PyList_Check(item))
1099                         {
1100                                 item = PyList_GET_ITEM(item, 0);
1101                                 if (item != Py_None)
1102                                         return 1;
1103                         } else if (PyTuple_Check(item))
1104                         {
1105                                 item = PyTuple_GET_ITEM(item, 0);
1106                                 if (item != Py_None)
1107                                         return 1;
1108                         }
1109                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1110                                 return 1;
1111                 }
1112         }
1113         return 0;
1114 }
1115
1116 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1117 {
1118         if (font)
1119                 m_font[fnt] = font;
1120         else
1121                 m_font.erase(fnt);
1122 }
1123
1124 void eListboxPythonMultiContent::setItemHeight(int height)
1125 {
1126         m_itemheight = height;
1127         if (m_listbox)
1128                 m_listbox->setItemHeight(height);
1129 }
1130
1131 void eListboxPythonMultiContent::setList(ePyObject list)
1132 {
1133         m_old_clip = m_clip = gRegion::invalidRegion();
1134         eListboxPythonStringContent::setList(list);
1135 }
1136
1137 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1138 {
1139         if (m_clip.valid())
1140         {
1141                 clip &= m_clip;
1142                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1143                         m_clip -= m_old_clip;
1144                 m_old_clip = m_clip;
1145         }
1146         else
1147                 m_old_clip = m_clip = gRegion::invalidRegion();
1148 }
1149
1150 void eListboxPythonMultiContent::entryRemoved(int idx)
1151 {
1152         if (m_listbox)
1153                 m_listbox->entryRemoved(idx);
1154 }
1155
1156 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1157 {
1158         m_template = tmplate;
1159 }