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