add templated colors
[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                                 int color = PyInt_AsLong(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                                 int color = PyInt_AsLong(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                                 int color = PyInt_AsLong(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                                 int color = PyInt_AsLong(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                         int color = PyInt_AsLong(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                         int color = PyInt_AsLong(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_AsLong(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                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
783                                 int x = PyInt_AsLong(px) + offset.x();
784                                 int y = PyInt_AsLong(py) + offset.y();
785                                 int width = PyInt_AsLong(pwidth);
786                                 int height = PyInt_AsLong(pheight);
787                                 int flags = PyInt_AsLong(pflags);
788                                 int fnt = PyInt_AsLong(pfnt);
789                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
790
791                                 if (m_font.find(fnt) == m_font.end())
792                                 {
793                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
794                                         goto error_out;
795                                 }
796
797                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
798                                 painter.clip(rect);
799
800                                 {
801                                         gRegion rc(rect);
802                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
803                                 }
804
805                                 painter.setFont(m_font[fnt]);
806                                 painter.renderText(rect, string, flags);
807                                 painter.clippop();
808
809                                 // draw border
810                                 if (bwidth)
811                                 {
812                                         eRect rect(eRect(x, y, width, height));
813                                         painter.clip(rect);
814                                         if (pborderColor)
815                                         {
816                                                 int color = PyInt_AsLong(pborderColor);
817                                                 painter.setForegroundColor(gRGB(color));
818                                         }
819
820                                         rect.setRect(x, y, width, bwidth);
821                                         painter.fill(rect);
822
823                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
824                                         painter.fill(rect);
825
826                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
827                                         painter.fill(rect);
828
829                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
830                                         painter.fill(rect);
831
832                                         painter.clippop();
833                                 }
834                                 break;
835                         }
836                         case TYPE_PROGRESS: // Progress
837                         {
838                         /*
839                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
840                         */
841                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
842                                                         py = PyTuple_GET_ITEM(item, 2),
843                                                         pwidth = PyTuple_GET_ITEM(item, 3),
844                                                         pheight = PyTuple_GET_ITEM(item, 4),
845                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
846                                                         pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
847
848                                 if (!(px && py && pwidth && pheight && pfilled_perc))
849                                 {
850                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
851                                         goto error_out;
852                                 }
853
854                                 if (size > 6)
855                                 {
856                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
857                                         if (pborderWidth == Py_None)
858                                                 pborderWidth = ePyObject();
859                                 }
860                                 if (size > 7)
861                                 {
862                                         pforeColor = PyTuple_GET_ITEM(item, 7);
863                                         if (pforeColor == Py_None)
864                                                 pforeColor = ePyObject();
865                                 }
866                                 if (size > 8)
867                                 {
868                                         pforeColorSelected = PyTuple_GET_ITEM(item, 8);
869                                         if (pforeColorSelected == Py_None)
870                                                 pforeColorSelected=ePyObject();
871                                 }
872                                 if (size > 9)
873                                 {
874                                         pbackColor = PyTuple_GET_ITEM(item, 9);
875                                         if (pbackColor == Py_None)
876                                                 pbackColor=ePyObject();
877                                 }
878                                 if (size > 10)
879                                 {
880                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
881                                         if (pbackColorSelected == Py_None)
882                                                 pbackColorSelected=ePyObject();
883                                 }
884
885                                 int x = PyInt_AsLong(px) + offset.x();
886                                 int y = PyInt_AsLong(py) + offset.y();
887                                 int width = PyInt_AsLong(pwidth);
888                                 int height = PyInt_AsLong(pheight);
889                                 int filled = PyInt_AsLong(pfilled_perc);
890
891                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
892                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
893
894                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
895
896                                 eRect rect(x, y, width, height);
897                                 painter.clip(rect);
898
899                                 {
900                                         gRegion rc(rect);
901                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
902                                 }
903
904                                 // border
905                                 rect.setRect(x, y, width, bwidth);
906                                 painter.fill(rect);
907
908                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
909                                 painter.fill(rect);
910
911                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
912                                 painter.fill(rect);
913
914                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
915                                 painter.fill(rect);
916
917                                 // progress
918                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
919                                 painter.fill(rect);
920
921                                 painter.clippop();
922
923                                 break;
924                         }
925                         case TYPE_PIXMAP_ALPHATEST:
926                         case TYPE_PIXMAP: // pixmap
927                         {
928                         /*
929                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
930                         */
931
932                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
933                                                         py = PyTuple_GET_ITEM(item, 2),
934                                                         pwidth = PyTuple_GET_ITEM(item, 3),
935                                                         pheight = PyTuple_GET_ITEM(item, 4),
936                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
937                                                         pbackColor, pbackColorSelected;
938
939                                 if (!(px && py && pwidth && pheight && ppixmap))
940                                 {
941                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
942                                         goto error_out;
943                                 }
944
945                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
946                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
947
948                                 int x = PyInt_AsLong(px) + offset.x();
949                                 int y = PyInt_AsLong(py) + offset.y();
950                                 int width = PyInt_AsLong(pwidth);
951                                 int height = PyInt_AsLong(pheight);
952                                 ePtr<gPixmap> pixmap;
953                                 if (SwigFromPython(pixmap, ppixmap))
954                                 {
955                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
956                                         goto error_out;
957                                 }
958
959                                 if (size > 6)
960                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
961
962                                 if (size > 7)
963                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
964
965                                 eRect rect(x, y, width, height);
966                                 painter.clip(rect);
967
968                                 {
969                                         gRegion rc(rect);
970                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
971                                 }
972
973                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
974                                 painter.clippop();
975                                 break;
976                         }
977                         default:
978                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
979                                 goto error_out;
980                         }
981                 }
982         }
983
984         if (selected)
985                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
986
987 error_out:
988         if (buildfunc_ret)
989                 Py_DECREF(buildfunc_ret);
990
991         painter.clippop();
992 }
993
994 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
995 {
996         Py_XDECREF(m_buildFunc);
997         m_buildFunc=cb;
998         Py_XINCREF(m_buildFunc);
999 }
1000
1001 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1002 {
1003         Py_XDECREF(m_selectableFunc);
1004         m_selectableFunc=cb;
1005         Py_XINCREF(m_selectableFunc);
1006 }
1007
1008 int eListboxPythonMultiContent::currentCursorSelectable()
1009 {
1010         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1011         if (m_list && cursorValid())
1012         {
1013                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1014                 {
1015                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1016                         if (PyTuple_Check(args))
1017                         {
1018                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1019                                 if (ret)
1020                                 {
1021                                         bool retval = ret == Py_True;
1022                                         Py_DECREF(ret);
1023                                         return ret;
1024                                 }
1025                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1026                         }
1027                         else
1028                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1029                 }
1030                 else
1031                 {
1032                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1033                         if (PyList_Check(item))
1034                         {
1035                                 item = PyList_GET_ITEM(item, 0);
1036                                 if (item != Py_None)
1037                                         return 1;
1038                         } else if (PyTuple_Check(item))
1039                         {
1040                                 item = PyTuple_GET_ITEM(item, 0);
1041                                 if (item != Py_None)
1042                                         return 1;
1043                         }
1044                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1045                                 return 1;
1046                 }
1047         }
1048         return 0;
1049 }
1050
1051 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1052 {
1053         if (font)
1054                 m_font[fnt] = font;
1055         else
1056                 m_font.erase(fnt);
1057 }
1058
1059 void eListboxPythonMultiContent::setItemHeight(int height)
1060 {
1061         m_itemheight = height;
1062         if (m_listbox)
1063                 m_listbox->setItemHeight(height);
1064 }
1065
1066 void eListboxPythonMultiContent::setList(ePyObject list)
1067 {
1068         m_old_clip = m_clip = gRegion::invalidRegion();
1069         eListboxPythonStringContent::setList(list);
1070 }
1071
1072 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1073 {
1074         if (m_clip.valid())
1075         {
1076                 clip &= m_clip;
1077                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1078                         m_clip -= m_old_clip;
1079                 m_old_clip = m_clip;
1080         }
1081         else
1082                 m_old_clip = m_clip = gRegion::invalidRegion();
1083 }
1084
1085 void eListboxPythonMultiContent::entryRemoved(int idx)
1086 {
1087         if (m_listbox)
1088                 m_listbox->entryRemoved(idx);
1089 }
1090
1091 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1092 {
1093         m_template = tmplate;
1094 }