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