elistboxcontent.cpp: revert "cleanup" part of the commit "elistboxcontent.cpp: code...
[enigma2.git] / lib / gui / elistboxcontent.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
4 #include <lib/python/python.h>
5
6 /*
7     The basic idea is to have an interface which gives all relevant list
8     processing functions, and can be used by the listbox to browse trough
9     the list.
10     
11     The listbox directly uses the implemented cursor. It tries hard to avoid
12     iterating trough the (possibly very large) list, so it should be O(1),
13     i.e. the performance should not be influenced by the size of the list.
14     
15     The list interface knows how to draw the current entry to a specified 
16     offset. Different interfaces can be used to adapt different lists,
17     pre-filter lists on the fly etc.
18     
19                 cursorSave/Restore is used to avoid re-iterating the list on redraw.
20                 The current selection is always selected as cursor position, the
21     cursor is then positioned to the start, and then iterated. This gives
22     at most 2x m_items_per_page cursor movements per redraw, indepenent
23     of the size of the list.
24     
25     Although cursorSet is provided, it should be only used when there is no
26     other way, as it involves iterating trough the list.
27  */
28
29 iListboxContent::~iListboxContent()
30 {
31 }
32
33 iListboxContent::iListboxContent(): m_listbox(0)
34 {
35 }
36
37 void iListboxContent::setListbox(eListbox *lb)
38 {
39         m_listbox = lb;
40         m_listbox->setItemHeight(getItemHeight());
41 }
42
43 int iListboxContent::currentCursorSelectable()
44 {
45         return 1;
46 }
47
48 //////////////////////////////////////
49
50 DEFINE_REF(eListboxPythonStringContent);
51
52 eListboxPythonStringContent::eListboxPythonStringContent(): m_itemheight(25)
53 {
54 }
55
56 eListboxPythonStringContent::~eListboxPythonStringContent()
57 {
58         Py_XDECREF(m_list);
59 }
60
61 void eListboxPythonStringContent::cursorHome()
62 {
63         m_cursor = 0;
64 }
65
66 void eListboxPythonStringContent::cursorEnd()
67 {
68         m_cursor = size();
69 }
70
71 int eListboxPythonStringContent::cursorMove(int count)
72 {
73         m_cursor += count;
74         
75         if (m_cursor < 0)
76                 cursorHome();
77         else if (m_cursor > size())
78                 cursorEnd();
79         return 0;
80 }
81
82 int eListboxPythonStringContent::cursorValid()
83 {
84         return m_cursor < size();
85 }
86
87 int eListboxPythonStringContent::cursorSet(int n)
88 {
89         m_cursor = n;
90         
91         if (m_cursor < 0)
92                 cursorHome();
93         else if (m_cursor > size())
94                 cursorEnd();
95         return 0;
96 }
97
98 int eListboxPythonStringContent::cursorGet()
99 {
100         return m_cursor;
101 }
102
103 int eListboxPythonStringContent::currentCursorSelectable()
104 {
105         if (m_list && cursorValid())
106         {
107                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
108                 if (!PyTuple_Check(item))
109                         return 1;
110                 if (PyTuple_Size(item) >= 2)
111                         return 1;
112         }
113         return 0;
114 }
115
116 void eListboxPythonStringContent::cursorSave()
117 {
118         m_saved_cursor = m_cursor;
119 }
120
121 void eListboxPythonStringContent::cursorRestore()
122 {
123         m_cursor = m_saved_cursor;
124 }
125
126 int eListboxPythonStringContent::size()
127 {
128         if (!m_list)
129                 return 0;
130         return PyList_Size(m_list);
131 }
132         
133 void eListboxPythonStringContent::setSize(const eSize &size)
134 {
135         m_itemsize = size;
136 }
137
138 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
139 {
140         ePtr<gFont> fnt = new gFont("Regular", 20);
141         painter.clip(eRect(offset, m_itemsize));
142         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
143
144         eListboxStyle *local_style = 0;
145         bool cursorValid = this->cursorValid();
146
147                 /* get local listbox style, if present */
148         if (m_listbox)
149                 local_style = m_listbox->getLocalStyle();
150
151         if (local_style)
152         {
153                 if (selected)
154                 {
155                         /* if we have a local background color set, use that. */
156                         if (local_style->m_background_color_selected_set)
157                                 painter.setBackgroundColor(local_style->m_background_color_selected);
158                         /* same for foreground */
159                         if (local_style->m_foreground_color_selected_set)
160                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
161                 }
162                 else
163                 {
164                         /* if we have a local background color set, use that. */
165                         if (local_style->m_background_color_set)
166                                 painter.setBackgroundColor(local_style->m_background_color);
167                         /* same for foreground */
168                         if (local_style->m_foreground_color_set)
169                                 painter.setForegroundColor(local_style->m_foreground_color);
170                 }
171         }
172
173         /* if we have no transparent background */
174         if (!local_style || !local_style->m_transparent_background)
175         {
176                         /* blit background picture, if available (otherwise, clear only) */
177                 if (local_style && local_style->m_background && cursorValid)
178                         painter.blit(local_style->m_background, offset, eRect(), 0);
179                 else
180                         painter.clear();
181         } else
182         {
183                 if (local_style->m_background && cursorValid)
184                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
185                 else if (selected && !local_style->m_selection)
186                         painter.clear();
187         }
188
189         if (m_list && cursorValid)
190         {
191                 int gray = 0;
192                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
193                 painter.setFont(fnt);
194
195                         /* the user can supply tuples, in this case the first one will be displayed. */
196                 if (PyTuple_Check(item))
197                 {
198                         if (PyTuple_Size(item) == 1)
199                                 gray = 1;
200                         item = PyTuple_GET_ITEM(item, 0);
201                 }
202
203                 if (selected && local_style && local_style->m_selection)
204                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
205
206                 if (item == Py_None)
207                 {
208                                 /* seperator */
209                         int half_height = m_itemsize.height() / 2;
210                         painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
211                 } else
212                 {
213                         const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
214                         ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
215                         if (gray)
216                                 painter.setForegroundColor(gRGB(0x808080));
217                         painter.renderText(eRect(text_offset, m_itemsize), string);
218                 }
219
220                 if (selected && (!local_style || !local_style->m_selection))
221                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
222         }
223
224         painter.clippop();
225 }
226
227 void eListboxPythonStringContent::setList(ePyObject list)
228 {
229         Py_XDECREF(m_list);
230         if (!PyList_Check(list))
231         {
232                 m_list = ePyObject();
233         } else
234         {
235                 m_list = list;
236                 Py_INCREF(m_list);
237         }
238
239         if (m_listbox)
240                 m_listbox->entryReset(false);
241 }
242
243 PyObject *eListboxPythonStringContent::getCurrentSelection()
244 {
245         if (!(m_list && cursorValid()))
246                 Py_RETURN_NONE;
247
248         ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
249         Py_XINCREF(r);
250         return r;
251 }
252
253 void eListboxPythonStringContent::invalidateEntry(int index)
254 {
255         if (m_listbox)
256                 m_listbox->entryChanged(index);
257 }
258
259 void eListboxPythonStringContent::invalidate()
260 {
261         if (m_listbox)
262         {
263                 int s = size();
264                 if ( m_cursor >= s )
265                         m_listbox->moveSelectionTo(s?s-1:0);
266                 else
267                         m_listbox->invalidate();
268         }
269 }
270
271 //////////////////////////////////////
272
273 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
274 {
275         ePtr<gFont> fnt = new gFont("Regular", 20);
276         ePtr<gFont> fnt2 = new gFont("Regular", 16);
277         eRect itemrect(offset, m_itemsize);
278         eListboxStyle *local_style = 0;
279         bool cursorValid = this->cursorValid();
280
281         painter.clip(itemrect);
282         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
283
284                 /* get local listbox style, if present */
285         if (m_listbox)
286                 local_style = m_listbox->getLocalStyle();
287
288         if (local_style)
289         {
290                 if (selected)
291                 {
292                         /* if we have a local background color set, use that. */
293                         if (local_style->m_background_color_selected_set)
294                                 painter.setBackgroundColor(local_style->m_background_color_selected);
295                         /* same for foreground */
296                         if (local_style->m_foreground_color_selected_set)
297                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
298                 }
299                 else
300                 {
301                         /* if we have a local background color set, use that. */
302                         if (local_style->m_background_color_set)
303                                 painter.setBackgroundColor(local_style->m_background_color);
304                         /* same for foreground */
305                         if (local_style->m_foreground_color_set)
306                                 painter.setForegroundColor(local_style->m_foreground_color);
307                 }
308         }
309
310         if (!local_style || !local_style->m_transparent_background)
311                 /* if we have no transparent background */
312         {
313                 /* blit background picture, if available (otherwise, clear only) */
314                 if (local_style && local_style->m_background && cursorValid)
315                         painter.blit(local_style->m_background, offset, eRect(), 0);
316                 else
317                         painter.clear();
318         } else
319         {
320                 if (local_style->m_background && cursorValid)
321                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
322                 else if (selected && !local_style->m_selection)
323                         painter.clear();
324         }
325
326         if (m_list && cursorValid)
327         {
328                         /* get current list item */
329                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
330                 ePyObject text, value;
331                 painter.setFont(fnt);
332
333                 if (selected && local_style && local_style->m_selection)
334                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
335
336                         /* the first tuple element is a string for the left side.
337                            the second one will be called, and the result shall be an tuple.
338
339                            of this tuple,
340                            the first one is the type (string).
341                            the second one is the value. */
342                 if (PyTuple_Check(item))
343                 {
344                                 /* handle left part. get item from tuple, convert to string, display. */
345
346                         text = PyTuple_GET_ITEM(item, 0);
347                         text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
348                         const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
349                         eSize item_left = eSize(m_seperation, m_itemsize.height());
350                         eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
351                         painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
352                         Py_XDECREF(text);
353
354                                 /* when we have no label, align value to the left. (FIXME:
355                                    don't we want to specifiy this individually?) */
356                         int value_alignment_left = !*string;
357
358                                 /* now, handle the value. get 2nd part from tuple*/
359                         value = PyTuple_GET_ITEM(item, 1);
360                         if (value)
361                         {
362                                 ePyObject args = PyTuple_New(1);
363                                 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
364
365                                         /* CallObject will call __call__ which should return the value tuple */
366                                 value = PyObject_CallObject(value, args);
367
368                                 if (PyErr_Occurred())
369                                         PyErr_Print();
370
371                                 Py_DECREF(args);
372                                         /* the PyInt was stolen. */
373                         }
374
375                                 /*  check if this is really a tuple */
376                         if (value && PyTuple_Check(value))
377                         {
378                                         /* convert type to string */
379                                 ePyObject type = PyTuple_GET_ITEM(value, 0);
380                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
381
382                                 if (atype)
383                                 {
384                                         if (!strcmp(atype, "text"))
385                                         {
386                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
387                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
388                                                 painter.setFont(fnt2);
389                                                 if (value_alignment_left)
390                                                         painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
391                                                 else
392                                                         painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
393
394                                                         /* pvalue is borrowed */
395                                         } else if (!strcmp(atype, "slider"))
396                                         {
397                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
398                                                 ePyObject psize = PyTuple_GET_ITEM(value, 2);
399
400                                                         /* convert value to Long. fallback to -1 on error. */
401                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
402                                                 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
403
404                                                         /* calc. slider length */
405                                                 int width = item_right.width() * value / size;
406                                                 int height = item_right.height();
407
408
409                                                         /* draw slider */
410                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
411                                                 //hack - make it customizable
412                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
413
414                                                         /* pvalue is borrowed */
415                                         } else if (!strcmp(atype, "mtext"))
416                                         {
417                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
418                                                 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
419                                                 int xoffs = value_alignment_left ? 0 : m_seperation;
420                                                 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
421                                                 para->setFont(fnt2);
422                                                 para->renderString(text, 0);
423                                                 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
424                                                 int glyphs = para->size();
425
426                                                 ePyObject plist;
427
428                                                 if (PyTuple_Size(value) >= 3)
429                                                         plist = PyTuple_GET_ITEM(value, 2);
430
431                                                 int entries = 0;
432
433                                                 if (plist && PyList_Check(plist))
434                                                         entries = PyList_Size(plist);
435
436                                                 int left=0, right=0, last=-1;
437                                                 eRect bbox;
438                                                 for (int i = 0; i < entries; ++i)
439                                                 {
440                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
441                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
442
443                                                         if ((num < 0) || (num >= glyphs))
444                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
445                                                         else
446                                                         {
447                                                                 if (last+1 != num && last != -1) {
448                                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
449                                                                         painter.fill(bbox);
450                                                                 }
451                                                                 para->setGlyphFlag(num, GS_INVERT);
452                                                                 bbox = para->getGlyphBBox(num);
453                                                                 if (last+1 != num || last == -1)
454                                                                         left = bbox.left();
455                                                                 right = bbox.left() + bbox.width();
456                                                                 last = num;
457                                                         }
458                                                                 /* entry is borrowed */
459                                                 }
460                                                 if (last != -1) {
461                                                         bbox = eRect(left, offset.y(), right-left, m_itemsize.height());
462                                                         painter.fill(bbox);
463                                                 }
464                                                 painter.renderPara(para, ePoint(0, 0));
465                                                         /* pvalue is borrowed */
466                                                         /* plist is 0 or borrowed */
467                                         }
468                                 }
469                                         /* type is borrowed */
470                         } else
471                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
472                         if (value)
473                                 Py_DECREF(value);
474                 }
475
476                 if (selected && (!local_style || !local_style->m_selection))
477                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
478         }
479
480         painter.clippop();
481 }
482
483 int eListboxPythonConfigContent::currentCursorSelectable()
484 {
485         return eListboxPythonStringContent::currentCursorSelectable();
486 }
487
488 //////////////////////////////////////
489
490         /* todo: make a real infrastructure here! */
491 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
492
493 eListboxPythonMultiContent::eListboxPythonMultiContent()
494         :m_clip(gRegion::invalidRegion()), m_old_clip(gRegion::invalidRegion())
495 {
496 }
497
498 eListboxPythonMultiContent::~eListboxPythonMultiContent()
499 {
500         Py_XDECREF(m_buildFunc);
501         Py_XDECREF(m_selectableFunc);
502 }
503
504 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
505 {
506         m_selection_clip = rect;
507         if (m_listbox)
508                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
509         if (m_clip.valid())
510                 m_clip |= rect;
511         else
512                 m_clip = rect;
513         if (update && m_listbox)
514                 m_listbox->entryChanged(m_cursor);
515 }
516
517 static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid)
518 {
519         if (pbackColor)
520         {
521                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColor);
522                 painter.setBackgroundColor(gRGB(color));
523         }
524         else if (local_style)
525         {
526                 if (local_style && local_style->m_background_color_set)
527                         painter.setBackgroundColor(local_style->m_background_color);
528                 if (local_style->m_background && cursorValid)
529                 {
530                         if (local_style->m_transparent_background)
531                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
532                         else
533                                 painter.blit(local_style->m_background, offset, eRect(), 0);
534                         return;
535                 }
536                 else if (local_style->m_transparent_background)
537                         return;
538         }
539         painter.clear();
540 }
541
542 static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid)
543 {
544         if (pbackColorSelected)
545         {
546                 unsigned int color = PyInt_AsUnsignedLongMask(pbackColorSelected);
547                 painter.setBackgroundColor(gRGB(color));
548         }
549         else if (local_style)
550         {
551                 if (local_style && local_style->m_background_color_selected_set)
552                         painter.setBackgroundColor(local_style->m_background_color_selected);
553                 if (local_style->m_background && cursorValid)
554                 {
555                         if (local_style->m_transparent_background)
556                                 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
557                         else
558                                 painter.blit(local_style->m_background, offset, eRect(), 0);
559                         return;
560                 }
561                 else if (local_style->m_transparent_background)
562                         return;
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                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
916
917                                 eRect rect(x, y, width, height);
918                                 painter.clip(rect);
919
920                                 {
921                                         gRegion rc(rect);
922                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
923                                 }
924
925                                 // border
926                                 rect.setRect(x, y, width, bwidth);
927                                 painter.fill(rect);
928
929                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
930                                 painter.fill(rect);
931
932                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
933                                 painter.fill(rect);
934
935                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
936                                 painter.fill(rect);
937
938                                 // progress
939                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
940                                 painter.fill(rect);
941
942                                 painter.clippop();
943
944                                 break;
945                         }
946                         case TYPE_PIXMAP_ALPHATEST:
947                         case TYPE_PIXMAP: // pixmap
948                         {
949                         /*
950                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
951                         */
952
953                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
954                                                         py = PyTuple_GET_ITEM(item, 2),
955                                                         pwidth = PyTuple_GET_ITEM(item, 3),
956                                                         pheight = PyTuple_GET_ITEM(item, 4),
957                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
958                                                         pbackColor, pbackColorSelected;
959
960                                 if (!(px && py && pwidth && pheight && ppixmap))
961                                 {
962                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
963                                         goto error_out;
964                                 }
965
966                                 if (PyInt_Check(ppixmap) && data) /* if the pixemap is in fact a number, it refers to the 'data' list. */
967                                         ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap));
968
969                                                         /* don't do anything if we have 'None' as pixmap */
970                                 if (ppixmap == Py_None)
971                                         continue;
972
973                                 int x = PyInt_AsLong(px) + offset.x();
974                                 int y = PyInt_AsLong(py) + offset.y();
975                                 int width = PyInt_AsLong(pwidth);
976                                 int height = PyInt_AsLong(pheight);
977                                 ePtr<gPixmap> pixmap;
978                                 if (SwigFromPython(pixmap, ppixmap))
979                                 {
980                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
981                                         goto error_out;
982                                 }
983
984                                 if (size > 6)
985                                         pbackColor = lookupColor(PyTuple_GET_ITEM(item, 6), data);
986
987                                 if (size > 7)
988                                         pbackColorSelected = lookupColor(PyTuple_GET_ITEM(item, 7), data);
989
990                                 eRect rect(x, y, width, height);
991                                 painter.clip(rect);
992
993                                 {
994                                         gRegion rc(rect);
995                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid);
996                                 }
997
998                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
999                                 painter.clippop();
1000                                 break;
1001                         }
1002                         default:
1003                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
1004                                 goto error_out;
1005                         }
1006                 }
1007         }
1008
1009         if (selected && !sel_clip.valid() && (!local_style || !local_style->m_selection))
1010                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
1011
1012 error_out:
1013         if (buildfunc_ret)
1014                 Py_DECREF(buildfunc_ret);
1015
1016         painter.clippop();
1017 }
1018
1019 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
1020 {
1021         Py_XDECREF(m_buildFunc);
1022         m_buildFunc=cb;
1023         Py_XINCREF(m_buildFunc);
1024 }
1025
1026 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
1027 {
1028         Py_XDECREF(m_selectableFunc);
1029         m_selectableFunc=cb;
1030         Py_XINCREF(m_selectableFunc);
1031 }
1032
1033 int eListboxPythonMultiContent::currentCursorSelectable()
1034 {
1035         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
1036         if (m_list && cursorValid())
1037         {
1038                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
1039                 {
1040                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
1041                         if (PyTuple_Check(args))
1042                         {
1043                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
1044                                 if (ret)
1045                                 {
1046                                         bool retval = ret == Py_True;
1047                                         Py_DECREF(ret);
1048                                         return retval;
1049                                 }
1050                                 eDebug("call m_selectableFunc failed!!! assume not callable");
1051                         }
1052                         else
1053                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
1054                 }
1055                 else
1056                 {
1057                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
1058                         if (PyList_Check(item))
1059                         {
1060                                 item = PyList_GET_ITEM(item, 0);
1061                                 if (item != Py_None)
1062                                         return 1;
1063                         } else if (PyTuple_Check(item))
1064                         {
1065                                 item = PyTuple_GET_ITEM(item, 0);
1066                                 if (item != Py_None)
1067                                         return 1;
1068                         }
1069                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
1070                                 return 1;
1071                 }
1072         }
1073         return 0;
1074 }
1075
1076 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
1077 {
1078         if (font)
1079                 m_font[fnt] = font;
1080         else
1081                 m_font.erase(fnt);
1082 }
1083
1084 void eListboxPythonMultiContent::setItemHeight(int height)
1085 {
1086         m_itemheight = height;
1087         if (m_listbox)
1088                 m_listbox->setItemHeight(height);
1089 }
1090
1091 void eListboxPythonMultiContent::setList(ePyObject list)
1092 {
1093         m_old_clip = m_clip = gRegion::invalidRegion();
1094         eListboxPythonStringContent::setList(list);
1095 }
1096
1097 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1098 {
1099         if (m_clip.valid())
1100         {
1101                 clip &= m_clip;
1102                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1103                         m_clip -= m_old_clip;
1104                 m_old_clip = m_clip;
1105         }
1106         else
1107                 m_old_clip = m_clip = gRegion::invalidRegion();
1108 }
1109
1110 void eListboxPythonMultiContent::entryRemoved(int idx)
1111 {
1112         if (m_listbox)
1113                 m_listbox->entryRemoved(idx);
1114 }
1115
1116 void eListboxPythonMultiContent::setTemplate(ePyObject tmplate)
1117 {
1118         m_template = tmplate;
1119 }