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