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