Merge commit 'origin/translations' into experimental
[vuplus_dvbapp] / 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, bool clear=true)
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         if (clear)
540                 painter.clear();
541 }
542
543 static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid, bool clear=true)
544 {
545         if (pbackColorSelected)
546         {
547                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColorSelected);
548                 painter.setBackgroundColor(gRGB(color));
549         }
550         else if (local_style)
551         {
552                 if (local_style && local_style->m_background_color_selected_set)
553                         painter.setBackgroundColor(local_style->m_background_color_selected);
554                 if (local_style->m_background && cursorValid)
555                 {
556                         if (local_style->m_transparent_background)
557                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
558                         else
559                                 painter.blit(local_style->m_background, offset, eRect(), 0);
560                         return;
561                 }
562         }
563         if (clear)
564                 painter.clear();
565 }
566
567 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, bool clear=true)
568 {
569         if (selected && sel_clip.valid())
570         {
571                 gRegion part = rc - sel_clip;
572                 if (!part.empty())
573                 {
574                         painter.clip(part);
575                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
576                         clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
577                         painter.clippop();
578                         selected = 0;
579                 }
580                 part = rc & sel_clip;
581                 if (!part.empty())
582                 {
583                         painter.clip(part);
584                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
585                         clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
586                         painter.clippop();
587                         selected = 1;
588                 }
589         }
590         else if (selected)
591         {
592                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
593                 clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear);
594                 if (local_style && local_style->m_selection)
595                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
596         }
597         else
598         {
599                 style.setStyle(painter, eWindowStyle::styleListboxNormal);
600                 clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear);
601         }
602
603         if (selected)
604         {
605                 if (pforeColorSelected)
606                 {
607                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColorSelected);
608                         painter.setForegroundColor(gRGB(color));
609                 }
610                 /* if we have a local foreground color set, use that. */
611                 else if (local_style && local_style->m_foreground_color_selected_set)
612                         painter.setForegroundColor(local_style->m_foreground_color_selected);
613         }
614         else
615         {
616                 if (pforeColor)
617                 {
618                         unsigned int color = PyInt_AsUnsignedLongMask(pforeColor);
619                         painter.setForegroundColor(gRGB(color));
620                 }
621                 /* if we have a local foreground color set, use that. */
622                 else if (local_style && local_style->m_foreground_color_set)
623                         painter.setForegroundColor(local_style->m_foreground_color);
624         }
625 }
626
627 static ePyObject lookupColor(ePyObject color, ePyObject data)
628 {
629         if (color == Py_None)
630                 return ePyObject();
631
632         if ((!color) && (!data))
633                 return color;
634
635         unsigned int icolor = PyInt_AsUnsignedLongMask(color);
636
637                 /* check if we have the "magic" template color */
638         if ((icolor & 0xFF000000) == 0xFF000000)
639         {
640                 int index = icolor & 0xFFFFFF;
641                 eDebug("[eListboxPythonMultiContent] template color index: %d", index);
642                 return PyTuple_GetItem(data, index);
643         }
644
645         if (color == Py_None)
646                 return ePyObject();
647
648         return color;
649 }
650
651 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
652 {
653         gRegion itemregion(eRect(offset, m_itemsize));
654         eListboxStyle *local_style = 0;
655         eRect sel_clip(m_selection_clip);
656         bool cursorValid = this->cursorValid();
657         if (sel_clip.valid())
658                 sel_clip.moveBy(offset);
659
660                 /* get local listbox style, if present */
661         if (m_listbox)
662                 local_style = m_listbox->getLocalStyle();
663
664         painter.clip(itemregion);
665         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip, offset, cursorValid);
666
667         ePyObject items, buildfunc_ret;
668
669         if (m_list && cursorValid)
670         {
671                         /* a multicontent list can be used in two ways:
672                                 either each item is a list of (TYPE,...)-tuples,
673                                 or there is a template defined, which is a list of (TYPE,...)-tuples,
674                                 and the list is an unformatted tuple. The template then references items from the list.
675                         */
676                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
677
678                 if (m_buildFunc)
679                 {
680                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
681                         {
682                                 if (PyTuple_Check(items))
683                                         buildfunc_ret = items = PyObject_CallObject(m_buildFunc, items);
684                                 else
685                                         eDebug("items is no tuple");
686                         }
687                         else
688                                 eDebug("buildfunc is not callable");
689                 }
690
691                 if (!items)
692                 {
693                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
694                         goto error_out;
695                 }
696
697                 if (!m_template)
698                 {
699                         if (!PyList_Check(items))
700                         {
701                                 eDebug("eListboxPythonMultiContent: list entry %d is not a list (non-templated)", m_cursor);
702                                 goto error_out;
703                         }
704                 } else
705                 {
706                         if (!PyTuple_Check(items))
707                         {
708                                 eDebug("eListboxPythonMultiContent: list entry %d is not a tuple (templated)", m_cursor);
709                                 goto error_out;
710                         }
711                 }
712
713                 ePyObject data;
714
715                         /* if we have a template, use the template for the actual formatting. 
716                                 we will later detect that "data" is present, and refer to that, instead
717                                 of the immediate value. */
718                 int start = 1;
719                 if (m_template)
720                 {
721                         data = items;
722                         items = m_template;
723                         start = 0;
724                 }
725
726                 int size = PyList_Size(items);
727                 for (int i = start; i < size; ++i)
728                 {
729                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
730
731                         if (!item)
732                         {
733                                 eDebug("eListboxPythonMultiContent: ?");
734                                 goto error_out;
735                         }
736
737                         if (!PyTuple_Check(item))
738                         {
739                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
740                                 goto error_out;
741                         }
742
743                         int size = PyTuple_Size(item);
744
745                         if (!size)
746                         {
747                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
748                                 goto error_out;
749                         }
750
751                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
752
753                         switch (type)
754                         {
755                         case TYPE_TEXT: // text
756                         {
757                         /*
758                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
759                         */
760                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
761                                                         py = PyTuple_GET_ITEM(item, 2),
762                                                         pwidth = PyTuple_GET_ITEM(item, 3),
763                                                         pheight = PyTuple_GET_ITEM(item, 4),
764                                                         pfnt = PyTuple_GET_ITEM(item, 5),
765                                                         pflags = PyTuple_GET_ITEM(item, 6),
766                                                         pstring = PyTuple_GET_ITEM(item, 7),
767                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
768
769                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
770                                 {
771                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
772                                         goto error_out;
773                                 }
774
775                                 if (size > 8)
776                                         pforeColor = lookupColor(PyTuple_GET_ITEM(item, 8), data);
777
778                                 if (size > 9)
779                                         pforeColorSelected = lookupColor(PyTuple_GET_ITEM(item, 9), data);
780
781                                 if (size > 10)
782                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 10), data);
783
784                                 if (size > 11)
785                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 11), data);
786
787                                 if (size > 12)
788                                 {
789                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
790                                         if (pborderWidth == Py_None)
791                                                 pborderWidth=ePyObject();
792                                 }
793                                 if (size > 13)
794                                         pborderColor = lookupColor(PyTuple_GET_ITEM(item, 13), data);
795
796                                 if (PyInt_Check(pstring) && data) /* if the string is in fact a number, it refers to the 'data' list. */
797                                         pstring = PyTuple_GetItem(data, PyInt_AsLong(pstring));
798
799                                                         /* don't do anything if we have 'None' as string */
800                                 if (pstring == Py_None)
801                                         continue;
802
803                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
804                                 int x = PyInt_AsLong(px) + offset.x();
805                                 int y = PyInt_AsLong(py) + offset.y();
806                                 int width = PyInt_AsLong(pwidth);
807                                 int height = PyInt_AsLong(pheight);
808                                 int flags = PyInt_AsLong(pflags);
809                                 int fnt = PyInt_AsLong(pfnt);
810                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
811
812                                 if (m_font.find(fnt) == m_font.end())
813                                 {
814                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
815                                         goto error_out;
816                                 }
817
818                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
819                                 painter.clip(rect);
820
821                                 {
822                                         gRegion rc(rect);
823                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
824                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
825                                 }
826
827                                 painter.setFont(m_font[fnt]);
828                                 painter.renderText(rect, string, flags);
829                                 painter.clippop();
830
831                                 // draw border
832                                 if (bwidth)
833                                 {
834                                         eRect rect(eRect(x, y, width, height));
835                                         painter.clip(rect);
836                                         if (pborderColor)
837                                         {
838                                                 unsigned int color = PyInt_AsUnsignedLongMask(pborderColor);
839                                                 painter.setForegroundColor(gRGB(color));
840                                         }
841
842                                         rect.setRect(x, y, width, bwidth);
843                                         painter.fill(rect);
844
845                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
846                                         painter.fill(rect);
847
848                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
849                                         painter.fill(rect);
850
851                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
852                                         painter.fill(rect);
853
854                                         painter.clippop();
855                                 }
856                                 break;
857                         }
858                         case TYPE_PROGRESS: // Progress
859                         {
860                         /*
861                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
862                         */
863                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
864                                                         py = PyTuple_GET_ITEM(item, 2),
865                                                         pwidth = PyTuple_GET_ITEM(item, 3),
866                                                         pheight = PyTuple_GET_ITEM(item, 4),
867                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
868                                                         pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
869
870                                 if (!(px && py && pwidth && pheight && pfilled_perc))
871                                 {
872                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
873                                         goto error_out;
874                                 }
875
876                                 if (size > 6)
877                                 {
878                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
879                                         if (pborderWidth == Py_None)
880                                                 pborderWidth = ePyObject();
881                                 }
882                                 if (size > 7)
883                                 {
884                                         pforeColor = PyTuple_GET_ITEM(item, 7);
885                                         if (pforeColor == Py_None)
886                                                 pforeColor = ePyObject();
887                                 }
888                                 if (size > 8)
889                                 {
890                                         pforeColorSelected = PyTuple_GET_ITEM(item, 8);
891                                         if (pforeColorSelected == Py_None)
892                                                 pforeColorSelected=ePyObject();
893                                 }
894                                 if (size > 9)
895                                 {
896                                         pbackColor = PyTuple_GET_ITEM(item, 9);
897                                         if (pbackColor == Py_None)
898                                                 pbackColor=ePyObject();
899                                 }
900                                 if (size > 10)
901                                 {
902                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
903                                         if (pbackColorSelected == Py_None)
904                                                 pbackColorSelected=ePyObject();
905                                 }
906
907                                 int x = PyInt_AsLong(px) + offset.x();
908                                 int y = PyInt_AsLong(py) + offset.y();
909                                 int width = PyInt_AsLong(pwidth);
910                                 int height = PyInt_AsLong(pheight);
911                                 int filled = PyInt_AsLong(pfilled_perc);
912
913                                 if ((filled < 0) && data) /* if the string is in a negative number, it refers to the 'data' list. */
914                                         filled = PyInt_AsLong(PyTuple_GetItem(data, -filled));
915                                         
916                                                         /* don't do anything if percent out of range */
917                                 if ((filled < 0) || (filled > 100))
918                                         continue;
919
920                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
921
922                                 eRect rect(x, y, width, height);
923                                 painter.clip(rect);
924
925                                 {
926                                         gRegion rc(rect);
927                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
928                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
929                                 }
930
931                                 // border
932                                 rect.setRect(x, y, width, bwidth);
933                                 painter.fill(rect);
934
935                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
936                                 painter.fill(rect);
937
938                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
939                                 painter.fill(rect);
940
941                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
942                                 painter.fill(rect);
943
944                                 // progress
945                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
946                                 painter.fill(rect);
947
948                                 painter.clippop();
949
950                                 break;
951                         }
952                         case TYPE_PIXMAP_ALPHABLEND:
953                         case TYPE_PIXMAP_ALPHATEST:
954                         case TYPE_PIXMAP: // pixmap
955                         {
956                         /*
957                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
958                         */
959
960                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
961                                                         py = PyTuple_GET_ITEM(item, 2),
962                                                         pwidth = PyTuple_GET_ITEM(item, 3),
963                                                         pheight = PyTuple_GET_ITEM(item, 4),
964                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
965                                                         pbackColor, pbackColorSelected;
966
967                                 if (!(px && py && pwidth && pheight && ppixmap))
968                                 {
969                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
970                                         goto error_out;
971                                 }
972
973                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
974                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
975
976                                                         /* don't do anything if we have 'None' as pixmap */
977                                 if (ppixmap == Py_None)
978                                         continue;
979
980                                 int x = PyInt_AsLong(px) + offset.x();
981                                 int y = PyInt_AsLong(py) + offset.y();
982                                 int width = PyInt_AsLong(pwidth);
983                                 int height = PyInt_AsLong(pheight);
984                                 ePtr<gPixmap> pixmap;
985                                 if (SwigFromPython(pixmap, ppixmap))
986                                 {
987                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
988                                         goto error_out;
989                                 }
990
991                                 if (size > 6)
992                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
993
994                                 if (size > 7)
995                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
996
997                                 eRect rect(x, y, width, height);
998                                 painter.clip(rect);
999
1000                                 {
1001                                         gRegion rc(rect);
1002                                         bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor);
1003                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear);
1004                                 }
1005
1006                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : (type == TYPE_PIXMAP_ALPHABLEND) ? gPainter::BT_ALPHABLEND : 0);
1007                                 painter.clippop();
1008                                 break;
1009                         }
1010                         default:
1011                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1012                                 goto error_out;
1013                         }
1014                 }
1015         }
1016
1017         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1018                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1019
1020 error_out:
1021         if (buildfunc_ret)
1022                 Py_DECREF(buildfunc_ret);
1023
1024         painter.clippop();
1025 }
1026
1027 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1028 {
1029         Py_XDECREF(m_buildFunc);
1030         m_buildFunc=cb;
1031         Py_XINCREF(m_buildFunc);
1032 }
1033
1034 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1035 {
1036         Py_XDECREF(m_selectableFunc);
1037         m_selectableFunc=cb;
1038         Py_XINCREF(m_selectableFunc);
1039 }
1040
1041 int eListboxPythonMultiContent::currentCursorSelectable()
1042 {
1043         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1044         if (m_list && cursorValid())
1045         {
1046                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1047                 {
1048                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1049                         if (PyTuple_Check(args))
1050                         {
1051                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1052                                 if (ret)
1053                                 {
1054                                         bool retval = ret == Py_True;
1055                                         Py_DECREF(ret);
1056                                         return retval;
1057                                 }
1058                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1059                         }
1060                         else
1061                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1062                 }
1063                 else
1064                 {
1065                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1066                         if (PyList_Check(item))
1067                         {
1068                                 item = PyList_GET_ITEM(item, 0);
1069                                 if (item != Py_None)
1070                                         return 1;
1071                         } else if (PyTuple_Check(item))
1072                         {
1073                                 item = PyTuple_GET_ITEM(item, 0);
1074                                 if (item != Py_None)
1075                                         return 1;
1076                         }
1077                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1078                                 return 1;
1079                 }
1080         }
1081         return 0;
1082 }
1083
1084 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1085 {
1086         if (font)
1087                 m_font[fnt] = font;
1088         else
1089                 m_font.erase(fnt);
1090 }
1091
1092 void eListboxPythonMultiContent::setItemHeight(int height)
1093 {
1094         m_itemheight = height;
1095         if (m_listbox)
1096                 m_listbox->setItemHeight(height);
1097 }
1098
1099 void eListboxPythonMultiContent::setList(ePyObject list)
1100 {
1101         m_old_clip = m_clip = gRegion::invalidRegion();
1102         eListboxPythonStringContent::setList(list);
1103 }
1104
1105 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1106 {
1107         if (m_clip.valid())
1108         {
1109                 clip &= m_clip;
1110                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1111                         m_clip -= m_old_clip;
1112                 m_old_clip = m_clip;
1113         }
1114         else
1115                 m_old_clip = m_clip = gRegion::invalidRegion();
1116 }
1117
1118 void eListboxPythonMultiContent::entryRemoved(int idx)
1119 {
1120         if (m_listbox)
1121                 m_listbox->entryRemoved(idx);
1122 }
1123
1124 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1125 {
1126         m_template = tmplate;
1127 }