Merge branch 'master' into obi/master
[enigma2.git] / lib / gui / elistboxcontent.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
4 #include <lib/python/python.h>
5
6 /*
7     The basic idea is to have an interface which gives all relevant list
8     processing functions, and can be used by the listbox to browse trough
9     the list.
10     
11     The listbox directly uses the implemented cursor. It tries hard to avoid
12     iterating trough the (possibly very large) list, so it should be O(1),
13     i.e. the performance should not be influenced by the size of the list.
14     
15     The list interface knows how to draw the current entry to a specified 
16     offset. Different interfaces can be used to adapt different lists,
17     pre-filter lists on the fly etc.
18     
19                 cursorSave/Restore is used to avoid re-iterating the list on redraw.
20                 The current selection is always selected as cursor position, the
21     cursor is then positioned to the start, and then iterated. This gives
22     at most 2x m_items_per_page cursor movements per redraw, indepenent
23     of the size of the list.
24     
25     Although cursorSet is provided, it should be only used when there is no
26     other way, as it involves iterating trough the list.
27  */
28
29 iListboxContent::~iListboxContent()
30 {
31 }
32
33 iListboxContent::iListboxContent(): m_listbox(0)
34 {
35 }
36
37 void iListboxContent::setListbox(eListbox *lb)
38 {
39         m_listbox = lb;
40         m_listbox->setItemHeight(getItemHeight());
41 }
42
43 int iListboxContent::currentCursorSelectable()
44 {
45         return 1;
46 }
47
48 //////////////////////////////////////
49
50 DEFINE_REF(eListboxPythonStringContent);
51
52 eListboxPythonStringContent::eListboxPythonStringContent()
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         Py_XDECREF(m_template);
505 }
506
507 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
508 {
509         m_selection_clip = rect;
510         if (m_listbox)
511                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
512         if (m_clip.valid())
513                 m_clip |= rect;
514         else
515                 m_clip = rect;
516         if (update && m_listbox)
517                 m_listbox->entryChanged(m_cursor);
518 }
519
520 static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid, bool clear=true)
521 {
522         if (pbackColor)
523         {
524                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColor);
525                 painter.setBackgroundColor(gRGB(color));
526         }
527         else if (local_style)
528         {
529                 if (local_style && local_style->m_background_color_set)
530                         painter.setBackgroundColor(local_style->m_background_color);
531                 if (local_style->m_background && cursorValid)
532                 {
533                         if (local_style->m_transparent_background)
534                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
535                         else
536                                 painter.blit(local_style->m_background, offset, eRect(), 0);
537                         return;
538                 }
539                 else if (local_style->m_transparent_background)
540                         return;
541         }
542         if (clear)
543                 painter.clear();
544 }
545
546 static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid, bool clear=true)
547 {
548         if (pbackColorSelected)
549         {
550                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColorSelected);
551                 painter.setBackgroundColor(gRGB(color));
552         }
553         else if (local_style)
554         {
555                 if (local_style && local_style->m_background_color_selected_set)
556                         painter.setBackgroundColor(local_style->m_background_color_selected);
557                 if (local_style->m_background && cursorValid)
558                 {
559                         if (local_style->m_transparent_background)
560                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
561                         else
562                                 painter.blit(local_style->m_background, offset, eRect(), 0);
563                         return;
564                 }
565         }
566         if (clear)
567                 painter.clear();
568 }
569
570 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)
571 {
572         if (selected && sel_clip.valid())
573         {
574                 gRegion part = rc - sel_clip;
575                 if (!part.empty())
576                 {
577                         painter.clip(part);
578                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
579                         clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
580                         painter.clippop();
581                         selected = 0;
582                 }
583                 part = rc & sel_clip;
584                 if (!part.empty())
585                 {
586                         painter.clip(part);
587                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
588                         clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
589                         painter.clippop();
590                         selected = 1;
591                 }
592         }
593         else if (selected)
594         {
595                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
596                 clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
597                 if (local_style && local_style->m_selection)
598                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
599         }
600         else
601         {
602                 style.setStyle(painter, eWindowStyle::styleListboxNormal);
603                 clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
604         }
605
606         if (selected)
607         {
608                 if (pforeColorSelected)
609                 {
610                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColorSelected);
611                         painter.setForegroundColor(gRGB(color));
612                 }
613                 /* if we have a local foreground color set, use that. */
614                 else if (local_style && local_style->m_foreground_color_selected_set)
615                         painter.setForegroundColor(local_style->m_foreground_color_selected);
616         }
617         else
618         {
619                 if (pforeColor)
620                 {
621                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColor);
622                         painter.setForegroundColor(gRGB(color));
623                 }
624                 /* if we have a local foreground color set, use that. */
625                 else if (local_style && local_style->m_foreground_color_set)
626                         painter.setForegroundColor(local_style->m_foreground_color);
627         }
628 }
629
630 static ePyObject lookupColor(ePyObject color, ePyObject data)
631 {
632         if (color == Py_None)
633                 return ePyObject();
634
635         if ((!color) && (!data))
636                 return color;
637
638         unsigned int icolor = PyInt_AsUnsignedLongMask(color);
639
640                 /* check if we have the "magic" template color */
641         if ((icolor & 0xFF000000) == 0xFF000000)
642         {
643                 int index = icolor & 0xFFFFFF;
644                 if (PyTuple_GetItem(data, index) == Py_None)
645                         return ePyObject();
646                 return PyTuple_GetItem(data, index);
647         }
648
649         if (color == Py_None)
650                 return ePyObject();
651
652         return color;
653 }
654
655 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
656 {
657         gRegion itemregion(eRect(offset, m_itemsize));
658         eListboxStyle *local_style = 0;
659         eRect sel_clip(m_selection_clip);
660         bool cursorValid = this->cursorValid();
661         if (sel_clip.valid())
662                 sel_clip.moveBy(offset);
663
664                 /* get local listbox style, if present */
665         if (m_listbox)
666                 local_style = m_listbox->getLocalStyle();
667
668         painter.clip(itemregion);
669         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip, offset, cursorValid);
670
671         ePyObject items, buildfunc_ret;
672
673         if (m_list && cursorValid)
674         {
675                         /* a multicontent list can be used in two ways:
676                                 either each item is a list of (TYPE,...)-tuples,
677                                 or there is a template defined, which is a list of (TYPE,...)-tuples,
678                                 and the list is an unformatted tuple. The template then references items from the list.
679                         */
680                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
681
682                 if (m_buildFunc)
683                 {
684                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
685                         {
686                                 if (PyTuple_Check(items))
687                                         buildfunc_ret = items = PyObject_CallObject(m_buildFunc, items);
688                                 else
689                                         eDebug("items is no tuple");
690                         }
691                         else
692                                 eDebug("buildfunc is not callable");
693                 }
694
695                 if (!items)
696                 {
697                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
698                         goto error_out;
699                 }
700
701                 if (!m_template)
702                 {
703                         if (!PyList_Check(items))
704                         {
705                                 eDebug("eListboxPythonMultiContent: list entry %d is not a list (non-templated)", m_cursor);
706                                 goto error_out;
707                         }
708                 } else
709                 {
710                         if (!PyTuple_Check(items))
711                         {
712                                 eDebug("eListboxPythonMultiContent: list entry %d is not a tuple (templated)", m_cursor);
713                                 goto error_out;
714                         }
715                 }
716
717                 ePyObject data;
718
719                         /* if we have a template, use the template for the actual formatting. 
720                                 we will later detect that "data" is present, and refer to that, instead
721                                 of the immediate value. */
722                 int start = 1;
723                 if (m_template)
724                 {
725                         data = items;
726                         items = m_template;
727                         start = 0;
728                 }
729
730                 int size = PyList_Size(items);
731                 for (int i = start; i < size; ++i)
732                 {
733                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
734
735                         if (!item)
736                         {
737                                 eDebug("eListboxPythonMultiContent: ?");
738                                 goto error_out;
739                         }
740
741                         if (!PyTuple_Check(item))
742                         {
743                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
744                                 goto error_out;
745                         }
746
747                         int size = PyTuple_Size(item);
748
749                         if (!size)
750                         {
751                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
752                                 goto error_out;
753                         }
754
755                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
756
757                         switch (type)
758                         {
759                         case TYPE_TEXT: // text
760                         {
761                         /*
762                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
763                         */
764                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
765                                                         py = PyTuple_GET_ITEM(item, 2),
766                                                         pwidth = PyTuple_GET_ITEM(item, 3),
767                                                         pheight = PyTuple_GET_ITEM(item, 4),
768                                                         pfnt = PyTuple_GET_ITEM(item, 5),
769                                                         pflags = PyTuple_GET_ITEM(item, 6),
770                                                         pstring = PyTuple_GET_ITEM(item, 7),
771                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
772
773                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
774                                 {
775                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
776                                         goto error_out;
777                                 }
778
779                                 if (size > 8)
780                                         pforeColor = lookupColor(PyTuple_GET_ITEM(item, 8), data);
781
782                                 if (size > 9)
783                                         pforeColorSelected = lookupColor(PyTuple_GET_ITEM(item, 9), data);
784
785                                 if (size > 10)
786                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 10), data);
787
788                                 if (size > 11)
789                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 11), data);
790
791                                 if (size > 12)
792                                 {
793                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
794                                         if (pborderWidth == Py_None)
795                                                 pborderWidth=ePyObject();
796                                 }
797                                 if (size > 13)
798                                         pborderColor = lookupColor(PyTuple_GET_ITEM(item, 13), data);
799
800                                 if (PyInt_Check(pstring) && data) /* if the string is in fact a number, it refers to the 'data' list. */
801                                         pstring = PyTuple_GetItem(data, PyInt_AsLong(pstring));
802
803                                                         /* don't do anything if we have 'None' as string */
804                                 if (pstring == Py_None)
805                                         continue;
806
807                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
808                                 int x = PyInt_AsLong(px) + offset.x();
809                                 int y = PyInt_AsLong(py) + offset.y();
810                                 int width = PyInt_AsLong(pwidth);
811                                 int height = PyInt_AsLong(pheight);
812                                 int flags = PyInt_AsLong(pflags);
813                                 int fnt = PyInt_AsLong(pfnt);
814                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
815
816                                 if (m_font.find(fnt) == m_font.end())
817                                 {
818                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
819                                         goto error_out;
820                                 }
821
822                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
823                                 painter.clip(rect);
824
825                                 {
826                                         gRegion rc(rect);
827                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
828                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
829                                 }
830
831                                 painter.setFont(m_font[fnt]);
832                                 painter.renderText(rect, string, flags);
833                                 painter.clippop();
834
835                                 // draw border
836                                 if (bwidth)
837                                 {
838                                         eRect rect(eRect(x, y, width, height));
839                                         painter.clip(rect);
840                                         if (pborderColor)
841                                         {
842                                                 unsigned int color = PyInt_AsUnsignedLongMask(pborderColor);
843                                                 painter.setForegroundColor(gRGB(color));
844                                         }
845
846                                         rect.setRect(x, y, width, bwidth);
847                                         painter.fill(rect);
848
849                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
850                                         painter.fill(rect);
851
852                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
853                                         painter.fill(rect);
854
855                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
856                                         painter.fill(rect);
857
858                                         painter.clippop();
859                                 }
860                                 break;
861                         }
862                         case TYPE_PROGRESS_PIXMAP: // Progress
863                         /*
864                                 (1, x, y, width, height, filled_percent, pixmap [, borderWidth, foreColor, backColor, backColorSelected] )
865                         */
866                         case TYPE_PROGRESS: // Progress
867                         {
868                         /*
869                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
870                         */
871                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
872                                                         py = PyTuple_GET_ITEM(item, 2),
873                                                         pwidth = PyTuple_GET_ITEM(item, 3),
874                                                         pheight = PyTuple_GET_ITEM(item, 4),
875                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
876                                                         ppixmap, pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
877                                 int idx = 6;
878                                 if (type == TYPE_PROGRESS)
879                                 {
880                                         if (!(px && py && pwidth && pheight && pfilled_perc))
881                                         {
882                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
883                                                 goto error_out;
884                                         }
885                                 }
886                                 else
887                                 {
888                                         ppixmap = PyTuple_GET_ITEM(item, idx++);
889                                         if (ppixmap == Py_None)
890                                                 continue;
891                                         if (!(px && py && pwidth && pheight && pfilled_perc, ppixmap))
892                                         {
893                                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS_PIXMAP, x, y, width, height, filled percent, pixmap, [,border width, foreColor, backColor, backColorSelected]))");
894                                                 goto error_out;
895                                         }
896                                 }
897
898                                 if (size > idx)
899                                 {
900                                         pborderWidth = PyTuple_GET_ITEM(item, idx++);
901                                         if (pborderWidth == Py_None)
902                                                 pborderWidth = ePyObject();
903                                 }
904                                 if (size > idx)
905                                 {
906                                         pforeColor = PyTuple_GET_ITEM(item, idx++);
907                                         if (pforeColor == Py_None)
908                                                 pforeColor = ePyObject();
909                                 }
910                                 if (size > idx)
911                                 {
912                                         pforeColorSelected = PyTuple_GET_ITEM(item, idx++);
913                                         if (pforeColorSelected == Py_None)
914                                                 pforeColorSelected=ePyObject();
915                                 }
916                                 if (size > idx)
917                                 {
918                                         pbackColor = PyTuple_GET_ITEM(item, idx++);
919                                         if (pbackColor == Py_None)
920                                                 pbackColor=ePyObject();
921                                 }
922                                 if (size > idx)
923                                 {
924                                         pbackColorSelected = PyTuple_GET_ITEM(item, idx++);
925                                         if (pbackColorSelected == Py_None)
926                                                 pbackColorSelected=ePyObject();
927                                 }
928
929                                 int x = PyInt_AsLong(px) + offset.x();
930                                 int y = PyInt_AsLong(py) + offset.y();
931                                 int width = PyInt_AsLong(pwidth);
932                                 int height = PyInt_AsLong(pheight);
933                                 int filled = PyInt_AsLong(pfilled_perc);
934
935                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
936                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
937                                         
938                                                         /* don't do anything if percent out of range */
939                                 if ((filled < 0) || (filled > 100))
940                                         continue;
941
942                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
943
944                                 eRect rect(x, y, width, height);
945                                 painter.clip(rect);
946
947                                 {
948                                         gRegion rc(rect);
949                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
950                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
951                                 }
952
953                                 // border
954                                 rect.setRect(x, y, width, bwidth);
955                                 painter.fill(rect);
956
957                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
958                                 painter.fill(rect);
959
960                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
961                                 painter.fill(rect);
962
963                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
964                                 painter.fill(rect);
965
966                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
967
968                                 // progress
969                                 if (ppixmap)
970                                 {
971                                         ePtr<gPixmap> pixmap;
972                                         if (PyInt_Check(ppixmap) && data) /* if the pixmap is in fact a number, it refers to the data list */
973                                                 ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
974
975                                         if (SwigFromPython(pixmap, ppixmap))
976                                         {
977                                                 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
978                                                 painter.clippop();
979                                                 continue;
980                                         }
981                                         painter.blit(pixmap, rect.topLeft(), rect, 0);
982                                 }
983                                 else
984                                         painter.fill(rect);
985
986                                 painter.clippop();
987                                 break;
988                         }
989                         case TYPE_PIXMAP_ALPHABLEND:
990                         case TYPE_PIXMAP_ALPHATEST:
991                         case TYPE_PIXMAP: // pixmap
992                         {
993                         /*
994                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
995                         */
996
997                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
998                                                         py = PyTuple_GET_ITEM(item, 2),
999                                                         pwidth = PyTuple_GET_ITEM(item, 3),
1000                                                         pheight = PyTuple_GET_ITEM(item, 4),
1001                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
1002                                                         pbackColor, pbackColorSelected;
1003
1004                                 if (!(px && py && pwidth && pheight && ppixmap))
1005                                 {
1006                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
1007                                         goto error_out;
1008                                 }
1009
1010                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
1011                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
1012
1013                                                         /* don't do anything if we have 'None' as pixmap */
1014                                 if (ppixmap == Py_None)
1015                                         continue;
1016
1017                                 int x = PyInt_AsLong(px) + offset.x();
1018                                 int y = PyInt_AsLong(py) + offset.y();
1019                                 int width = PyInt_AsLong(pwidth);
1020                                 int height = PyInt_AsLong(pheight);
1021                                 ePtr<gPixmap> pixmap;
1022                                 if (SwigFromPython(pixmap, ppixmap))
1023                                 {
1024                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
1025                                         goto error_out;
1026                                 }
1027
1028                                 if (size > 6)
1029                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
1030
1031                                 if (size > 7)
1032                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
1033
1034                                 eRect rect(x, y, width, height);
1035                                 painter.clip(rect);
1036
1037                                 {
1038                                         gRegion rc(rect);
1039                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
1040                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
1041                                 }
1042
1043                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : (type == TYPE_PIXMAP_ALPHABLEND) ? gPainter::BT_ALPHABLEND : 0);
1044                                 painter.clippop();
1045                                 break;
1046                         }
1047                         default:
1048                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1049                                 goto error_out;
1050                         }
1051                 }
1052         }
1053
1054         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1055                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1056
1057 error_out:
1058         if (buildfunc_ret)
1059                 Py_DECREF(buildfunc_ret);
1060
1061         painter.clippop();
1062 }
1063
1064 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1065 {
1066         Py_XDECREF(m_buildFunc);
1067         m_buildFunc=cb;
1068         Py_XINCREF(m_buildFunc);
1069 }
1070
1071 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1072 {
1073         Py_XDECREF(m_selectableFunc);
1074         m_selectableFunc=cb;
1075         Py_XINCREF(m_selectableFunc);
1076 }
1077
1078 int eListboxPythonMultiContent::currentCursorSelectable()
1079 {
1080         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1081         if (m_list && cursorValid())
1082         {
1083                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1084                 {
1085                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1086                         if (PyTuple_Check(args))
1087                         {
1088                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1089                                 if (ret)
1090                                 {
1091                                         bool retval = ret == Py_True;
1092                                         Py_DECREF(ret);
1093                                         return retval;
1094                                 }
1095                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1096                         }
1097                         else
1098                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1099                 }
1100                 else
1101                 {
1102                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1103                         if (PyList_Check(item))
1104                         {
1105                                 item = PyList_GET_ITEM(item, 0);
1106                                 if (item != Py_None)
1107                                         return 1;
1108                         } else if (PyTuple_Check(item))
1109                         {
1110                                 item = PyTuple_GET_ITEM(item, 0);
1111                                 if (item != Py_None)
1112                                         return 1;
1113                         }
1114                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1115                                 return 1;
1116                 }
1117         }
1118         return 0;
1119 }
1120
1121 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1122 {
1123         if (font)
1124                 m_font[fnt] = font;
1125         else
1126                 m_font.erase(fnt);
1127 }
1128
1129 void eListboxPythonMultiContent::setItemHeight(int height)
1130 {
1131         m_itemheight = height;
1132         if (m_listbox)
1133                 m_listbox->setItemHeight(height);
1134 }
1135
1136 void eListboxPythonMultiContent::setList(ePyObject list)
1137 {
1138         m_old_clip = m_clip = gRegion::invalidRegion();
1139         eListboxPythonStringContent::setList(list);
1140 }
1141
1142 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1143 {
1144         if (m_clip.valid())
1145         {
1146                 clip &= m_clip;
1147                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1148                         m_clip -= m_old_clip;
1149                 m_old_clip = m_clip;
1150         }
1151         else
1152                 m_old_clip = m_clip = gRegion::invalidRegion();
1153 }
1154
1155 void eListboxPythonMultiContent::entryRemoved(int idx)
1156 {
1157         if (m_listbox)
1158                 m_listbox->entryRemoved(idx);
1159 }
1160
1161 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1162 {
1163         Py_XDECREF(m_template);
1164         m_template = tmplate;
1165         Py_XINCREF(m_template);
1166 }