update scrollbar in timerlist when a entry is removed
[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                                                 for (int i = 0; i < entries; ++i)
435                                                 {
436                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
437                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
438                                                         
439                                                         if ((num < 0) || (num >= glyphs))
440                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
441                                                         else
442                                                         {
443                                                                 para->setGlyphFlag(num, GS_INVERT);
444                                                                 eRect bbox;
445                                                                 bbox = para->getGlyphBBox(num);
446                                                                 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
447                                                                 painter.fill(bbox);
448                                                         }
449                                                                 /* entry is borrowed */
450                                                 }
451                                                 
452                                                 painter.renderPara(para, ePoint(0, 0));
453                                                         /* pvalue is borrowed */
454                                                         /* plist is 0 or borrowed */
455                                         }
456                                 }
457                                         /* type is borrowed */
458                         } else
459                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
460                                 /* value is borrowed */
461                 }
462
463                 if (selected && (!local_style || !local_style->m_selection))
464                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
465         }
466         
467         painter.clippop();
468 }
469
470 int eListboxPythonConfigContent::currentCursorSelectable()
471 {
472         return eListboxPythonStringContent::currentCursorSelectable();
473 }
474
475 //////////////////////////////////////
476
477         /* todo: make a real infrastructure here! */
478 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
479
480 eListboxPythonMultiContent::eListboxPythonMultiContent()
481         :m_clip(gRegion::invalidRegion()), m_old_clip(gRegion::invalidRegion())
482 {
483 }
484
485 eListboxPythonMultiContent::~eListboxPythonMultiContent()
486 {
487         Py_XDECREF(m_buildFunc);
488         Py_XDECREF(m_selectableFunc);
489 }
490
491 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
492 {
493         m_selection_clip = rect;
494         if (m_listbox)
495                 rect.moveBy(ePoint(0, m_listbox->getEntryTop()));
496         if (m_clip.valid())
497                 m_clip |= rect;
498         else
499                 m_clip = rect;
500         if (update && m_listbox)
501                 m_listbox->entryChanged(m_cursor);
502 }
503
504 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)
505 {
506         if (selected && sel_clip.valid())
507         {
508                 gRegion part = rc - sel_clip;
509                 if (!part.empty())
510                 {
511                         painter.clip(part);
512                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
513                         if (pbackColor)
514                         {
515                                 int color = PyInt_AsLong(pbackColor);
516                                 painter.setBackgroundColor(gRGB(color));
517                         } // transparent background?
518                         // if we have a local background color set, use that. 
519                         else if (local_style && local_style->m_background_color_set)
520                                 painter.setBackgroundColor(local_style->m_background_color);
521                         if (!pbackColor && local_style && local_style->m_transparent_background)
522                                 ;
523                         else
524                                 painter.clear();
525                         painter.clippop();
526                         selected = 0;
527                 }
528                 part = rc & sel_clip;
529                 if (!part.empty())
530                 {
531                         painter.clip(part);
532                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
533                         if (pbackColorSelected)
534                         {
535                                 int color = PyInt_AsLong(pbackColorSelected);
536                                 painter.setBackgroundColor(gRGB(color));
537                         }
538                         else if (local_style && local_style->m_background_color_selected_set)
539                                 painter.setBackgroundColor(local_style->m_background_color_selected);
540                         painter.clear();
541                         painter.clippop();
542                         selected = 1;
543                 }
544         }
545         else
546         {
547                 if (selected)
548                 {
549                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
550                         if (pbackColorSelected)
551                         {
552                                 int color = PyInt_AsLong(pbackColorSelected);
553                                 painter.setBackgroundColor(gRGB(color));
554                         }
555                         else if (local_style && local_style->m_background_color_selected_set)
556                                 painter.setBackgroundColor(local_style->m_background_color_selected);
557                         painter.clear();
558                 }
559                 else
560                 {
561                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
562                         if (pbackColor)
563                         {
564                                 int color = PyInt_AsLong(pbackColor);
565                                 painter.setBackgroundColor(gRGB(color));
566                         }/* if we have a local background color set, use that. */
567                         else if (local_style && local_style->m_background_color_set)
568                                 painter.setBackgroundColor(local_style->m_background_color);
569                         /* if we have no transparent background */
570                         if (!pbackColor && local_style && local_style->m_transparent_background)
571                                 ;
572                         else
573                                 painter.clear();
574                 }
575         }
576         if (selected)
577         {
578                 if (pforeColorSelected)
579                 {
580                         int color = PyInt_AsLong(pforeColorSelected);
581                         painter.setForegroundColor(gRGB(color));
582                 }
583                 /* if we have a local foreground color set, use that. */
584                 else if (local_style && local_style->m_foreground_color_selected_set)
585                         painter.setForegroundColor(local_style->m_foreground_color_selected);
586         }
587         else
588         {
589                 if (pforeColor)
590                 {
591                         int color = PyInt_AsLong(pforeColor);
592                         painter.setForegroundColor(gRGB(color));
593                 }
594                 /* if we have a local foreground color set, use that. */
595                 else if (local_style && local_style->m_foreground_color_set)
596                         painter.setForegroundColor(local_style->m_foreground_color);
597         }
598 }
599
600 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
601 {
602         gRegion itemregion(eRect(offset, m_itemsize));
603         eListboxStyle *local_style = 0;
604         eRect sel_clip(m_selection_clip);
605         if (sel_clip.valid())
606                 sel_clip.moveBy(offset);
607
608                 /* get local listbox style, if present */
609         if (m_listbox)
610                 local_style = m_listbox->getLocalStyle();
611
612         painter.clip(itemregion);
613         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip);
614
615         ePyObject items;
616
617         if (m_list && cursorValid())
618         {
619                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
620
621                 if (m_buildFunc)
622                 {
623                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
624                         {
625                                 if (PyTuple_Check(items))
626                                         items = PyObject_CallObject(m_buildFunc, items);
627                                 else
628                                         eDebug("items is no tuple");
629                         }
630                         else
631                                 eDebug("buildfunc is not callable");
632                 }
633
634                 if (!items)
635                 {
636                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
637                         goto error_out;
638                 }
639
640                 if (!PyList_Check(items))
641                 {
642                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
643                         goto error_out;
644                 }
645
646                 int size = PyList_Size(items);
647                 for (int i = 1; i < size; ++i)
648                 {
649                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
650
651                         if (!item)
652                         {
653                                 eDebug("eListboxPythonMultiContent: ?");
654                                 goto error_out;
655                         }
656
657                         if (!PyTuple_Check(item))
658                         {
659                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
660                                 goto error_out;
661                         }
662
663                         int size = PyTuple_Size(item);
664
665                         if (!size)
666                         {
667                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
668                                 goto error_out;
669                         }
670
671                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
672
673                         switch (type)
674                         {
675                         case TYPE_TEXT: // text
676                         {
677                         /*
678                                 (0, x, y, width, height, fnt, flags, "bla" [, color, colorSelected, backColor, backColorSelected, borderWidth, borderColor] )
679                         */
680                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
681                                                         py = PyTuple_GET_ITEM(item, 2),
682                                                         pwidth = PyTuple_GET_ITEM(item, 3),
683                                                         pheight = PyTuple_GET_ITEM(item, 4),
684                                                         pfnt = PyTuple_GET_ITEM(item, 5),
685                                                         pflags = PyTuple_GET_ITEM(item, 6),
686                                                         pstring = PyTuple_GET_ITEM(item, 7),
687                                                         pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
688
689                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
690                                 {
691                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
692                                         goto error_out;
693                                 }
694
695                                 if (size > 8)
696                                 {
697                                         pforeColor = PyTuple_GET_ITEM(item, 8);
698                                         if (pforeColor == Py_None)
699                                                 pforeColor=ePyObject();
700                                 }
701                                 if (size > 9)
702                                 {
703                                         pforeColorSelected = PyTuple_GET_ITEM(item, 9);
704                                         if (pforeColorSelected == Py_None)
705                                                 pforeColorSelected=ePyObject();
706                                 }
707                                 if (size > 10)
708                                 {
709                                         pbackColor = PyTuple_GET_ITEM(item, 10);
710                                         if (pbackColor == Py_None)
711                                                 pbackColor=ePyObject();
712                                 }
713                                 if (size > 11)
714                                 {
715                                         pbackColorSelected = PyTuple_GET_ITEM(item, 11);
716                                         if (pbackColorSelected == Py_None)
717                                                 pbackColorSelected=ePyObject();
718                                 }
719                                 if (size > 12)
720                                 {
721                                         pborderWidth = PyTuple_GET_ITEM(item, 12);
722                                         if (pborderWidth == Py_None)
723                                                 pborderWidth=ePyObject();
724                                 }
725                                 if (size > 13)
726                                 {
727                                         pborderColor = PyTuple_GET_ITEM(item, 13);
728                                         if (pborderColor == Py_None)
729                                                 pborderColor=ePyObject();
730                                 }
731
732                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
733                                 int x = PyInt_AsLong(px) + offset.x();
734                                 int y = PyInt_AsLong(py) + offset.y();
735                                 int width = PyInt_AsLong(pwidth);
736                                 int height = PyInt_AsLong(pheight);
737                                 int flags = PyInt_AsLong(pflags);
738                                 int fnt = PyInt_AsLong(pfnt);
739                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
740
741                                 if (m_font.find(fnt) == m_font.end())
742                                 {
743                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
744                                         goto error_out;
745                                 }
746
747                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
748                                 painter.clip(rect);
749
750                                 {
751                                         gRegion rc(rect);
752                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
753                                 }
754
755                                 painter.setFont(m_font[fnt]);
756                                 painter.renderText(rect, string, flags);
757                                 painter.clippop();
758
759                                 // draw border
760                                 if (bwidth)
761                                 {
762                                         eRect rect(eRect(x, y, width, height));
763                                         painter.clip(rect);
764                                         if (pborderColor)
765                                         {
766                                                 int color = PyInt_AsLong(pborderColor);
767                                                 painter.setForegroundColor(gRGB(color));
768                                         }
769
770                                         rect.setRect(x, y, width, bwidth);
771                                         painter.fill(rect);
772
773                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
774                                         painter.fill(rect);
775
776                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
777                                         painter.fill(rect);
778
779                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
780                                         painter.fill(rect);
781
782                                         painter.clippop();
783                                 }
784                                 break;
785                         }
786                         case TYPE_PROGRESS: // Progress
787                         {
788                         /*
789                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
790                         */
791                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
792                                                         py = PyTuple_GET_ITEM(item, 2),
793                                                         pwidth = PyTuple_GET_ITEM(item, 3),
794                                                         pheight = PyTuple_GET_ITEM(item, 4),
795                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
796                                                         pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected;
797
798                                 if (!(px && py && pwidth && pheight && pfilled_perc))
799                                 {
800                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
801                                         goto error_out;
802                                 }
803
804                                 if (size > 6)
805                                 {
806                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
807                                         if (pborderWidth == Py_None)
808                                                 pborderWidth = ePyObject();
809                                 }
810                                 if (size > 7)
811                                 {
812                                         pforeColor = PyTuple_GET_ITEM(item, 7);
813                                         if (pforeColor == Py_None)
814                                                 pforeColor = ePyObject();
815                                 }
816                                 if (size > 8)
817                                 {
818                                         pforeColorSelected = PyTuple_GET_ITEM(item, 8);
819                                         if (pforeColorSelected == Py_None)
820                                                 pforeColorSelected=ePyObject();
821                                 }
822                                 if (size > 9)
823                                 {
824                                         pbackColor = PyTuple_GET_ITEM(item, 9);
825                                         if (pbackColor == Py_None)
826                                                 pbackColor=ePyObject();
827                                 }
828                                 if (size > 10)
829                                 {
830                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
831                                         if (pbackColorSelected == Py_None)
832                                                 pbackColorSelected=ePyObject();
833                                 }
834
835                                 int x = PyInt_AsLong(px) + offset.x();
836                                 int y = PyInt_AsLong(py) + offset.y();
837                                 int width = PyInt_AsLong(pwidth);
838                                 int height = PyInt_AsLong(pheight);
839                                 int filled = PyInt_AsLong(pfilled_perc);
840                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
841
842                                 eRect rect(x, y, width, height);
843                                 painter.clip(rect);
844
845                                 {
846                                         gRegion rc(rect);
847                                         clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip);
848                                 }
849
850                                 // border
851                                 rect.setRect(x, y, width, bwidth);
852                                 painter.fill(rect);
853
854                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
855                                 painter.fill(rect);
856
857                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
858                                 painter.fill(rect);
859
860                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
861                                 painter.fill(rect);
862
863                                 // progress
864                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
865                                 painter.fill(rect);
866
867                                 painter.clippop();
868
869                                 break;
870                         }
871                         case TYPE_PIXMAP_ALPHATEST:
872                         case TYPE_PIXMAP: // pixmap
873                         {
874                         /*
875                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
876                         */
877
878                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
879                                                         py = PyTuple_GET_ITEM(item, 2),
880                                                         pwidth = PyTuple_GET_ITEM(item, 3),
881                                                         pheight = PyTuple_GET_ITEM(item, 4),
882                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
883                                                         pbackColor, pbackColorSelected;
884
885                                 if (!(px && py && pwidth && pheight && ppixmap))
886                                 {
887                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
888                                         goto error_out;
889                                 }
890
891                                 int x = PyInt_AsLong(px) + offset.x();
892                                 int y = PyInt_AsLong(py) + offset.y();
893                                 int width = PyInt_AsLong(pwidth);
894                                 int height = PyInt_AsLong(pheight);
895                                 ePtr<gPixmap> pixmap;
896                                 if (SwigFromPython(pixmap, ppixmap))
897                                 {
898                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
899                                         goto error_out;
900                                 }
901
902                                 if (size > 6)
903                                 {
904                                         pbackColor = PyTuple_GET_ITEM(item, 6);
905                                         if (pbackColor == Py_None)
906                                                 pbackColor=ePyObject();
907                                 }
908                                 if (size > 7)
909                                 {
910                                         pbackColorSelected = PyTuple_GET_ITEM(item, 7);
911                                         if (pbackColorSelected == Py_None)
912                                                 pbackColorSelected=ePyObject();
913                                 }
914
915                                 eRect rect(x, y, width, height);
916                                 painter.clip(rect);
917
918                                 {
919                                         gRegion rc(rect);
920                                         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
921                                 }
922
923                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
924                                 painter.clippop();
925                                 break;
926                         }
927                         default:
928                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
929                                 goto error_out;
930                         }
931                 }
932         }
933
934         if (selected)
935                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
936
937 error_out:
938         if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
939                 Py_DECREF(items);
940
941         painter.clippop();
942 }
943
944 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
945 {
946         Py_XDECREF(m_buildFunc);
947         m_buildFunc=cb;
948         Py_XINCREF(m_buildFunc);
949 }
950
951 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
952 {
953         Py_XDECREF(m_selectableFunc);
954         m_selectableFunc=cb;
955         Py_XINCREF(m_selectableFunc);
956 }
957
958 int eListboxPythonMultiContent::currentCursorSelectable()
959 {
960         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
961         if (m_list && cursorValid())
962         {
963                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
964                 {
965                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
966                         if (PyTuple_Check(args))
967                         {
968                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
969                                 if (ret)
970                                         return ret == Py_True;
971                                 eDebug("call m_selectableFunc failed!!! assume not callable");
972                         }
973                         else
974                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
975                 }
976                 else
977                 {
978                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
979                         if (PyList_Check(item))
980                         {
981                                 item = PyList_GET_ITEM(item, 0);
982                                 if (item != Py_None)
983                                         return 1;
984                         }
985                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
986                                 return 1;
987                 }
988         }
989         return 0;
990 }
991
992 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
993 {
994         if (font)
995                 m_font[fnt] = font;
996         else
997                 m_font.erase(fnt);
998 }
999
1000 void eListboxPythonMultiContent::setItemHeight(int height)
1001 {
1002         m_itemheight = height;
1003         if (m_listbox)
1004                 m_listbox->setItemHeight(height);
1005 }
1006
1007 void eListboxPythonMultiContent::setList(ePyObject list)
1008 {
1009         m_old_clip = m_clip = gRegion::invalidRegion();
1010         eListboxPythonStringContent::setList(list);
1011 }
1012
1013 void eListboxPythonMultiContent::updateClip(gRegion &clip)
1014 {
1015         if (m_clip.valid())
1016         {
1017                 clip &= m_clip;
1018                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
1019                         m_clip -= m_old_clip;
1020                 m_old_clip = m_clip;
1021         }
1022         else
1023                 m_old_clip = m_clip = gRegion::invalidRegion();
1024 }
1025
1026 void eListboxPythonMultiContent::entryRemoved(int idx)
1027 {
1028         if (m_listbox)
1029                 m_listbox->entryRemoved(idx);
1030 }