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