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