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