Merge branch 'master' of /home/tmbinc/enigma2-git
[enigma2.git] / lib / gui / elistboxcontent.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
4 #include <lib/python/python.h>
5
6 /*
7     The basic idea is to have an interface which gives all relevant list
8     processing functions, and can be used by the listbox to browse trough
9     the list.
10     
11     The listbox directly uses the implemented cursor. It tries hard to avoid
12     iterating trough the (possibly very large) list, so it should be O(1),
13     i.e. the performance should not be influenced by the size of the list.
14     
15     The list interface knows how to draw the current entry to a specified 
16     offset. Different interfaces can be used to adapt different lists,
17     pre-filter lists on the fly etc.
18     
19                 cursorSave/Restore is used to avoid re-iterating the list on redraw.
20                 The current selection is always selected as cursor position, the
21     cursor is then positioned to the start, and then iterated. This gives
22     at most 2x m_items_per_page cursor movements per redraw, indepenent
23     of the size of the list.
24     
25     Although cursorSet is provided, it should be only used when there is no
26     other way, as it involves iterating trough the list.
27  */
28
29 iListboxContent::~iListboxContent()
30 {
31 }
32
33 iListboxContent::iListboxContent(): m_listbox(0)
34 {
35 }
36
37 void iListboxContent::setListbox(eListbox *lb)
38 {
39         m_listbox = lb;
40         m_listbox->setItemHeight(getItemHeight());
41 }
42
43 int iListboxContent::currentCursorSelectable()
44 {
45         return 1;
46 }
47
48 //////////////////////////////////////
49
50 DEFINE_REF(eListboxPythonStringContent);
51
52 eListboxPythonStringContent::eListboxPythonStringContent(): m_itemheight(25)
53 {
54 }
55
56 eListboxPythonStringContent::~eListboxPythonStringContent()
57 {
58         Py_XDECREF(m_list);
59 }
60
61 void eListboxPythonStringContent::cursorHome()
62 {
63         m_cursor = 0;
64 }
65
66 void eListboxPythonStringContent::cursorEnd()
67 {
68         m_cursor = size();
69 }
70
71 int eListboxPythonStringContent::cursorMove(int count)
72 {
73         m_cursor += count;
74         
75         if (m_cursor < 0)
76                 cursorHome();
77         else if (m_cursor > size())
78                 cursorEnd();
79         return 0;
80 }
81
82 int eListboxPythonStringContent::cursorValid()
83 {
84         return m_cursor < size();
85 }
86
87 int eListboxPythonStringContent::cursorSet(int n)
88 {
89         m_cursor = n;
90         
91         if (m_cursor < 0)
92                 cursorHome();
93         else if (m_cursor > size())
94                 cursorEnd();
95         return 0;
96 }
97
98 int eListboxPythonStringContent::cursorGet()
99 {
100         return m_cursor;
101 }
102
103 int eListboxPythonStringContent::currentCursorSelectable()
104 {
105         if (m_list && cursorValid())
106         {
107                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
108                 if (!PyTuple_Check(item))
109                         return 1;
110                 if (PyTuple_Size(item) >= 2)
111                         return 1;
112         }
113         return 0;
114 }
115
116 void eListboxPythonStringContent::cursorSave()
117 {
118         m_saved_cursor = m_cursor;
119 }
120
121 void eListboxPythonStringContent::cursorRestore()
122 {
123         m_cursor = m_saved_cursor;
124 }
125
126 int eListboxPythonStringContent::size()
127 {
128         if (!m_list)
129                 return 0;
130         return PyList_Size(m_list);
131 }
132         
133 void eListboxPythonStringContent::setSize(const eSize &size)
134 {
135         m_itemsize = size;
136 }
137
138 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
139 {
140         ePtr<gFont> fnt = new gFont("Regular", 20);
141         painter.clip(eRect(offset, m_itemsize));
142         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
143
144         eListboxStyle *local_style = 0;
145         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                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
914
915                                 eRect rect(x, y, width, height);
916                                 painter.clip(rect);
917
918                                 {
919                                         gRegion rc(rect);
920                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
921                                 }
922
923                                 // border
924                                 rect.setRect(x, y, width, bwidth);
925                                 painter.fill(rect);
926
927                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
928                                 painter.fill(rect);
929
930                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
931                                 painter.fill(rect);
932
933                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
934                                 painter.fill(rect);
935
936                                 // progress
937                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
938                                 painter.fill(rect);
939
940                                 painter.clippop();
941
942                                 break;
943                         }
944                         case TYPE_PIXMAP_ALPHATEST:
945                         case TYPE_PIXMAP: // pixmap
946                         {
947                         /*
948                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
949                         */
950
951                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
952                                                         py = PyTuple_GET_ITEM(item, 2),
953                                                         pwidth = PyTuple_GET_ITEM(item, 3),
954                                                         pheight = PyTuple_GET_ITEM(item, 4),
955                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
956                                                         pbackColor, pbackColorSelected;
957
958                                 if (!(px && py && pwidth && pheight && ppixmap))
959                                 {
960                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
961                                         goto error_out;
962                                 }
963
964                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
965                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
966
967                                                         /* don't do anything if we have 'None' as pixmap */
968                                 if (ppixmap == Py_None)
969                                         continue;
970
971                                 int x = PyInt_AsLong(px) + offset.x();
972                                 int y = PyInt_AsLong(py) + offset.y();
973                                 int width = PyInt_AsLong(pwidth);
974                                 int height = PyInt_AsLong(pheight);
975                                 ePtr<gPixmap> pixmap;
976                                 if (SwigFromPython(pixmap, ppixmap))
977                                 {
978                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
979                                         goto error_out;
980                                 }
981
982                                 if (size > 6)
983                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
984
985                                 if (size > 7)
986                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
987
988                                 eRect rect(x, y, width, height);
989                                 painter.clip(rect);
990
991                                 {
992                                         gRegion rc(rect);
993                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
994                                 }
995
996                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
997                                 painter.clippop();
998                                 break;
999                         }
1000                         default:
1001                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1002                                 goto error_out;
1003                         }
1004                 }
1005         }
1006
1007         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1008                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1009
1010 error_out:
1011         if (buildfunc_ret)
1012                 Py_DECREF(buildfunc_ret);
1013
1014         painter.clippop();
1015 }
1016
1017 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1018 {
1019         Py_XDECREF(m_buildFunc);
1020         m_buildFunc=cb;
1021         Py_XINCREF(m_buildFunc);
1022 }
1023
1024 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1025 {
1026         Py_XDECREF(m_selectableFunc);
1027         m_selectableFunc=cb;
1028         Py_XINCREF(m_selectableFunc);
1029 }
1030
1031 int eListboxPythonMultiContent::currentCursorSelectable()
1032 {
1033         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1034         if (m_list && cursorValid())
1035         {
1036                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1037                 {
1038                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1039                         if (PyTuple_Check(args))
1040                         {
1041                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1042                                 if (ret)
1043                                 {
1044                                         bool retval = ret == Py_True;
1045                                         Py_DECREF(ret);
1046                                         return retval;
1047                                 }
1048                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1049                         }
1050                         else
1051                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1052                 }
1053                 else
1054                 {
1055                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1056                         if (PyList_Check(item))
1057                         {
1058                                 item = PyList_GET_ITEM(item, 0);
1059                                 if (item != Py_None)
1060                                         return 1;
1061                         } else if (PyTuple_Check(item))
1062                         {
1063                                 item = PyTuple_GET_ITEM(item, 0);
1064                                 if (item != Py_None)
1065                                         return 1;
1066                         }
1067                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1068                                 return 1;
1069                 }
1070         }
1071         return 0;
1072 }
1073
1074 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1075 {
1076         if (font)
1077                 m_font[fnt] = font;
1078         else
1079                 m_font.erase(fnt);
1080 }
1081
1082 void eListboxPythonMultiContent::setItemHeight(int height)
1083 {
1084         m_itemheight = height;
1085         if (m_listbox)
1086                 m_listbox->setItemHeight(height);
1087 }
1088
1089 void eListboxPythonMultiContent::setList(ePyObject list)
1090 {
1091         m_old_clip = m_clip = gRegion::invalidRegion();
1092         eListboxPythonStringContent::setList(list);
1093 }
1094
1095 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1096 {
1097         if (m_clip.valid())
1098         {
1099                 clip &= m_clip;
1100                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1101                         m_clip -= m_old_clip;
1102                 m_old_clip = m_clip;
1103         }
1104         else
1105                 m_old_clip = m_clip = gRegion::invalidRegion();
1106 }
1107
1108 void eListboxPythonMultiContent::entryRemoved(int idx)
1109 {
1110         if (m_listbox)
1111                 m_listbox->entryRemoved(idx);
1112 }
1113
1114 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1115 {
1116         m_template = tmplate;
1117 }