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