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