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