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