small fix
[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 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                 } // transparent background?
482                 else if (local_style && local_style->m_transparent_background) 
483                         clear=false;
484                 // if we have a local background color set, use that. 
485                 else if (local_style && local_style->m_background_color_set)
486                         painter.setBackgroundColor(local_style->m_background_color);
487                 else
488                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
489                 if (clear)
490                         painter.clear();
491                 painter.clippop();
492                 painter.clip(rc&sel_clip);
493                 style.setStyle(painter, eWindowStyle::styleListboxSelected);
494                 if (pbackColorSelected)
495                 {
496                         int color = PyInt_AsLong(pbackColorSelected);
497                         painter.setBackgroundColor(gRGB(color));
498                 }
499                 painter.clear();
500                 painter.clippop();
501         }
502         else
503         {
504                 if (selected)
505                 {
506                         style.setStyle(painter, eWindowStyle::styleListboxSelected);
507                         if (pbackColorSelected)
508                         {
509                                 int color = PyInt_AsLong(pbackColorSelected);
510                                 painter.setBackgroundColor(gRGB(color));
511                         }
512                         painter.clear();
513                 }
514                 else
515                 {
516                         bool clear=true;
517                         style.setStyle(painter, eWindowStyle::styleListboxNormal);
518                         if (pbackColor)
519                         {
520                                 int color = PyInt_AsLong(pbackColor);
521                                 painter.setBackgroundColor(gRGB(color));
522                         }/* if we have a local background color set, use that. */
523                         else if (local_style)
524                         {
525                                 if (local_style->m_transparent_background)
526                                         clear=false;
527                                 else if (local_style->m_background_color_set)
528                                         painter.setBackgroundColor(local_style->m_background_color);
529                         }
530                         /* if we have no transparent background */
531                         if (clear)
532                                 painter.clear();
533                 }
534         }
535         if (pforeColor)
536         {
537                 int color = PyInt_AsLong(pforeColor);
538                 painter.setForegroundColor(gRGB(color));
539         }/* if we have a local foreground color set, use that. */
540         else if (local_style && local_style->m_foreground_color_set)
541                 painter.setBackgroundColor(local_style->m_foreground_color);
542 }
543
544 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
545 {
546         gRegion itemregion(eRect(offset, m_itemsize));
547         eListboxStyle *local_style = 0;
548         eRect sel_clip(m_selection_clip);
549         if (sel_clip.valid())
550                 sel_clip.moveBy(offset);
551
552                 /* get local listbox style, if present */
553         if (m_listbox)
554                 local_style = m_listbox->getLocalStyle();
555
556         painter.clip(itemregion);
557
558         clearRegion(painter, style, local_style, ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip);
559
560         ePyObject items;
561
562         if (m_list && cursorValid())
563         {
564                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
565
566                 if (m_buildFunc)
567                 {
568                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
569                         {
570                                 if (PyTuple_Check(items))
571                                         items = PyObject_CallObject(m_buildFunc, items);
572                                 else
573                                         eDebug("items is no tuple");
574                         }
575                         else
576                                 eDebug("buildfunc is not callable");
577                 }
578
579                 if (!items)
580                 {
581                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
582                         goto error_out;
583                 }
584
585                 if (!PyList_Check(items))
586                 {
587                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
588                         goto error_out;
589                 }
590
591                 int size = PyList_Size(items);
592                 for (int i = 1; i < size; ++i)
593                 {
594                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
595                         bool reset_colors=false;
596
597                         if (!item)
598                         {
599                                 eDebug("eListboxPythonMultiContent: ?");
600                                 goto error_out;
601                         }
602
603                         if (!PyTuple_Check(item))
604                         {
605                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
606                                 goto error_out;
607                         }
608
609                         int size = PyTuple_Size(item);
610
611                         if (!size)
612                         {
613                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
614                                 goto error_out;
615                         }
616
617                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
618
619                         switch (type)
620                         {
621                         case TYPE_TEXT: // text
622                         {
623                         /*
624                                 (0, x, y, width, height, fnt, flags, "bla" [, color, backColor, backColorSelected, borderWidth, borderColor] )
625                         */
626                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
627                                                         py = PyTuple_GET_ITEM(item, 2),
628                                                         pwidth = PyTuple_GET_ITEM(item, 3),
629                                                         pheight = PyTuple_GET_ITEM(item, 4),
630                                                         pfnt = PyTuple_GET_ITEM(item, 5),
631                                                         pflags = PyTuple_GET_ITEM(item, 6),
632                                                         pstring = PyTuple_GET_ITEM(item, 7),
633                                                         pforeColor, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
634
635                                 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
636                                 {
637                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
638                                         goto error_out;
639                                 }
640
641                                 if (size > 8)
642                                 {
643                                         pforeColor = PyTuple_GET_ITEM(item, 8);
644                                         if (pforeColor == Py_None)
645                                                 pforeColor=ePyObject();
646                                 }
647                                 if (size > 9)
648                                 {
649                                         pbackColor = PyTuple_GET_ITEM(item, 9);
650                                         if (pbackColor == Py_None)
651                                                 pbackColor=ePyObject();
652                                 }
653                                 if (size > 10)
654                                 {
655                                         pbackColorSelected = PyTuple_GET_ITEM(item, 10);
656                                         if (pbackColorSelected == Py_None)
657                                                 pbackColorSelected=ePyObject();
658                                 }
659                                 if (size > 11)
660                                         pborderWidth = PyTuple_GET_ITEM(item, 11);
661                                 if (size > 12)
662                                         pborderColor = PyTuple_GET_ITEM(item, 12);
663
664                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
665                                 int x = PyInt_AsLong(px) + offset.x();
666                                 int y = PyInt_AsLong(py) + offset.y();
667                                 int width = PyInt_AsLong(pwidth);
668                                 int height = PyInt_AsLong(pheight);
669                                 int flags = PyInt_AsLong(pflags);
670                                 int fnt = PyInt_AsLong(pfnt);
671                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
672
673                                 if (m_font.find(fnt) == m_font.end())
674                                 {
675                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
676                                         goto error_out;
677                                 }
678
679                                 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
680                                 painter.clip(rect);
681                                 if (pbackColor || pbackColorSelected || pforeColor)
682                                 {
683                                         gRegion rc(rect);
684                                         clearRegion(painter, style, local_style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
685                                         reset_colors=true;
686                                 }
687
688                                 painter.setFont(m_font[fnt]);
689                                 painter.renderText(rect, string, flags);
690                                 painter.clippop();
691
692                                 // draw border
693                                 if (bwidth)
694                                 {
695                                         eRect rect(eRect(x, y, width, height));
696                                         painter.clip(rect);
697                                         if (pborderColor)
698                                         {
699                                                 int color = PyInt_AsLong(pborderColor);
700                                                 painter.setForegroundColor(gRGB(color));
701                                         }
702                                         else if (pforeColor) // reset to normal color
703                                                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
704
705                                         rect.setRect(x, y, width, bwidth);
706                                         painter.fill(rect);
707
708                                         rect.setRect(x, y+bwidth, bwidth, height-bwidth);
709                                         painter.fill(rect);
710
711                                         rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
712                                         painter.fill(rect);
713
714                                         rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
715                                         painter.fill(rect);
716
717                                         painter.clippop();
718                                 }
719                                 break;
720                         }
721                         case TYPE_PROGRESS: // Progress
722                         {
723                         /*
724                                 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
725                         */
726                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
727                                                         py = PyTuple_GET_ITEM(item, 2),
728                                                         pwidth = PyTuple_GET_ITEM(item, 3),
729                                                         pheight = PyTuple_GET_ITEM(item, 4),
730                                                         pfilled_perc = PyTuple_GET_ITEM(item, 5),
731                                                         pborderWidth, pforeColor, pbackColor, pbackColorSelected;
732
733                                 if (!(px && py && pwidth && pheight && pfilled_perc))
734                                 {
735                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
736                                         goto error_out;
737                                 }
738
739                                 if (size > 6)
740                                         pborderWidth = PyTuple_GET_ITEM(item, 6);
741                                 if (size > 7)
742                                         pforeColor = PyTuple_GET_ITEM(item, 7);
743                                 if (size > 8)
744                                 {
745                                         pbackColor = PyTuple_GET_ITEM(item, 8);
746                                         if (pbackColor == Py_None)
747                                                 pbackColor=ePyObject();
748                                 }
749                                 if (size > 9)
750                                 {
751                                         pbackColorSelected = PyTuple_GET_ITEM(item, 9);
752                                         if (pbackColorSelected == Py_None)
753                                                 pbackColorSelected=ePyObject();
754                                 }
755
756                                 int x = PyInt_AsLong(px) + offset.x();
757                                 int y = PyInt_AsLong(py) + offset.y();
758                                 int width = PyInt_AsLong(pwidth);
759                                 int height = PyInt_AsLong(pheight);
760                                 int filled = PyInt_AsLong(pfilled_perc);
761                                 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
762
763                                 eRect rect(x, y, width, height);
764                                 painter.clip(rect);
765                                 if (pbackColor || pbackColorSelected || pforeColor)
766                                 {
767                                         gRegion rc(rect);
768                                         clearRegion(painter, style, local_style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
769                                         reset_colors=true;
770                                 }
771
772                                 // border
773                                 rect.setRect(x, y, width, bwidth);
774                                 painter.fill(rect);
775
776                                 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
777                                 painter.fill(rect);
778
779                                 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
780                                 painter.fill(rect);
781
782                                 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
783                                 painter.fill(rect);
784
785                                 // progress
786                                 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
787                                 painter.fill(rect);
788
789                                 painter.clippop();
790
791                                 break;
792                         }
793                         case TYPE_PIXMAP_ALPHATEST:
794                         case TYPE_PIXMAP: // pixmap
795                         {
796                         /*
797                                 (2, x, y, width, height, pixmap [, backColor, backColorSelected] )
798                         */
799
800                                 ePyObject px = PyTuple_GET_ITEM(item, 1),
801                                                         py = PyTuple_GET_ITEM(item, 2),
802                                                         pwidth = PyTuple_GET_ITEM(item, 3),
803                                                         pheight = PyTuple_GET_ITEM(item, 4),
804                                                         ppixmap = PyTuple_GET_ITEM(item, 5),
805                                                         pbackColor, pbackColorSelected;
806
807                                 if (!(px && py && pwidth && pheight && ppixmap))
808                                 {
809                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
810                                         goto error_out;
811                                 }
812
813                                 int x = PyInt_AsLong(px) + offset.x();
814                                 int y = PyInt_AsLong(py) + offset.y();
815                                 int width = PyInt_AsLong(pwidth);
816                                 int height = PyInt_AsLong(pheight);
817                                 ePtr<gPixmap> pixmap;
818                                 if (SwigFromPython(pixmap, ppixmap))
819                                 {
820                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
821                                         goto error_out;
822                                 }
823
824                                 if (size > 6)
825                                 {
826                                         pbackColor = PyTuple_GET_ITEM(item, 6);
827                                         if (pbackColor == Py_None)
828                                                 pbackColor=ePyObject();
829                                 }
830                                 if (size > 7)
831                                 {
832                                         pbackColorSelected = PyTuple_GET_ITEM(item, 7);
833                                         if (pbackColorSelected == Py_None)
834                                                 pbackColorSelected=ePyObject();
835                                 }
836
837                                 eRect rect(x, y, width, height);
838                                 painter.clip(rect);
839                                 if (pbackColor || pbackColorSelected)
840                                 {
841                                         gRegion rc(rect);
842                                         clearRegion(painter, style, local_style, ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
843                                         reset_colors=true;
844                                 }
845                                 
846                                 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
847                                 painter.clippop();
848                                 break;
849                         }
850                         default:
851                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
852                                 goto error_out;
853                         }
854                         if (reset_colors)
855                                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
856                 }
857         }
858
859         if (selected)
860                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
861
862 error_out:
863         if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
864                 Py_DECREF(items);
865
866         painter.clippop();
867 }
868
869 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
870 {
871         Py_XDECREF(m_buildFunc);
872         m_buildFunc=cb;
873         Py_XINCREF(m_buildFunc);
874 }
875
876 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
877 {
878         Py_XDECREF(m_selectableFunc);
879         m_selectableFunc=cb;
880         Py_XINCREF(m_selectableFunc);
881 }
882
883 int eListboxPythonMultiContent::currentCursorSelectable()
884 {
885         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
886         if (m_list && cursorValid())
887         {
888                 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
889                 {
890                         ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
891                         if (PyTuple_Check(args))
892                         {
893                                 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
894                                 if (ret)
895                                         return ret == Py_True;
896                                 eDebug("call m_selectableFunc failed!!! assume not callable");
897                         }
898                         else
899                                 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
900                 }
901                 else
902                 {
903                         ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
904                         if (PyList_Check(item))
905                         {
906                                 item = PyList_GET_ITEM(item, 0);
907                                 if (item != Py_None)
908                                         return 1;
909                         }
910                         else if (m_buildFunc && PyCallable_Check(m_buildFunc))
911                                 return 1;
912                 }
913         }
914         return 0;
915 }
916
917 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
918 {
919         if (font)
920                 m_font[fnt] = font;
921         else
922                 m_font.erase(fnt);
923 }
924
925 void eListboxPythonMultiContent::setItemHeight(int height)
926 {
927         m_itemheight = height;
928         if (m_listbox)
929                 m_listbox->setItemHeight(height);
930 }
931
932 void eListboxPythonMultiContent::setList(ePyObject list)
933 {
934         m_old_clip = m_clip = gRegion::invalidRegion();
935         eListboxPythonStringContent::setList(list);
936 }
937
938 void eListboxPythonMultiContent::updateClip(gRegion &clip)
939 {
940         if (m_clip.valid())
941         {
942                 clip &= m_clip;
943                 if (m_old_clip.valid() && !(m_clip-m_old_clip).empty())
944                         m_clip -= m_old_clip;
945                 m_old_clip = m_clip;
946         }
947         else
948                 m_old_clip = m_clip = gRegion::invalidRegion();
949 }