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