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