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