dont show the infobar when close the last open screen
[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 }
41
42 int iListboxContent::currentCursorSelectable()
43 {
44         return 1;
45 }
46
47 //////////////////////////////////////
48
49 DEFINE_REF(eListboxPythonStringContent);
50
51 eListboxPythonStringContent::eListboxPythonStringContent()
52 {
53 }
54
55 eListboxPythonStringContent::~eListboxPythonStringContent()
56 {
57         Py_XDECREF(m_list);
58 }
59
60 void eListboxPythonStringContent::cursorHome()
61 {
62         m_cursor = 0;
63 }
64
65 void eListboxPythonStringContent::cursorEnd()
66 {
67         m_cursor = size();
68 }
69
70 int eListboxPythonStringContent::cursorMove(int count)
71 {
72         m_cursor += count;
73         
74         if (m_cursor < 0)
75                 cursorHome();
76         else if (m_cursor > size())
77                 cursorEnd();
78         return 0;
79 }
80
81 int eListboxPythonStringContent::cursorValid()
82 {
83         return m_cursor < size();
84 }
85
86 int eListboxPythonStringContent::cursorSet(int n)
87 {
88         m_cursor = n;
89         
90         if (m_cursor < 0)
91                 cursorHome();
92         else if (m_cursor > size())
93                 cursorEnd();
94         return 0;
95 }
96
97 int eListboxPythonStringContent::cursorGet()
98 {
99         return m_cursor;
100 }
101
102 int eListboxPythonStringContent::currentCursorSelectable()
103 {
104         if (m_list && cursorValid())
105         {
106                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
107                 if (!PyTuple_Check(item))
108                         return 1;
109                 if (PyTuple_Size(item) >= 2)
110                         return 1;
111         }
112         return 0;
113 }
114
115 void eListboxPythonStringContent::cursorSave()
116 {
117         m_saved_cursor = m_cursor;
118 }
119
120 void eListboxPythonStringContent::cursorRestore()
121 {
122         m_cursor = m_saved_cursor;
123 }
124
125 int eListboxPythonStringContent::size()
126 {
127         if (!m_list)
128                 return 0;
129         return PyList_Size(m_list);
130 }
131         
132 void eListboxPythonStringContent::setSize(const eSize &size)
133 {
134         m_itemsize = size;
135 }
136
137 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
138 {
139         ePtr<gFont> fnt = new gFont("Regular", 20);
140         painter.clip(eRect(offset, m_itemsize));
141         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
142         painter.clear();
143
144         if (m_list && cursorValid())
145         {
146                 int gray = 0;
147                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
148                 painter.setFont(fnt);
149
150                         /* the user can supply tuples, in this case the first one will be displayed. */         
151                 if (PyTuple_Check(item))
152                 {
153                         if (PyTuple_Size(item) == 1)
154                                 gray = 1;
155                         item = PyTuple_GET_ITEM(item, 0);
156                 }
157                 
158                 if (item == Py_None)
159                 {
160                         int half_height = m_itemsize.height() / 2;
161                         
162                         painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
163                 } else
164                 {
165                         const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
166                         ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
167                         if (gray)
168                                 painter.setForegroundColor(gRGB(0x808080));
169                         painter.renderText(eRect(text_offset, m_itemsize), string);
170                 }
171                 
172                 if (selected)
173                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
174         }
175         
176         painter.clippop();
177 }
178
179 void eListboxPythonStringContent::setList(ePyObject list)
180 {
181         Py_XDECREF(m_list);
182         if (!PyList_Check(list))
183         {
184                 m_list = ePyObject();
185         } else
186         {
187                 m_list = list;
188                 Py_INCREF(m_list);
189         }
190
191         if (m_listbox)
192                 m_listbox->entryReset(false);
193 }
194
195 PyObject *eListboxPythonStringContent::getCurrentSelection()
196 {
197         if (!(m_list && cursorValid()))
198                 Py_RETURN_NONE;
199
200         ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
201         Py_XINCREF(r);
202         return r;
203 }
204
205 void eListboxPythonStringContent::invalidateEntry(int index)
206 {
207         if (m_listbox)
208                 m_listbox->entryChanged(index);
209 }
210
211 void eListboxPythonStringContent::invalidate()
212 {
213         if (m_listbox)
214         {
215                 int s = size();
216                 if ( m_cursor >= s )
217                         m_listbox->moveSelectionTo(s?s-1:0);
218                 m_listbox->invalidate();
219         }
220 }
221
222 //////////////////////////////////////
223
224 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
225 {
226         ePtr<gFont> fnt = new gFont("Regular", 20);
227         ePtr<gFont> fnt2 = new gFont("Regular", 16);
228         eRect itemrect(offset, m_itemsize);
229         painter.clip(itemrect);
230         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
231         painter.clear();
232
233         if (m_list && cursorValid())
234         {
235                         /* get current list item */
236                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
237                 ePyObject text, value;
238                 painter.setFont(fnt);
239
240                         /* the first tuple element is a string for the left side.
241                            the second one will be called, and the result shall be an tuple.
242                            
243                            of this tuple,
244                            the first one is the type (string).
245                            the second one is the value. */
246                 if (PyTuple_Check(item))
247                 {
248                                 /* handle left part. get item from tuple, convert to string, display. */
249                                 
250                         text = PyTuple_GET_ITEM(item, 0);
251                         text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
252                         const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
253                         eSize item_left = eSize(m_seperation, m_itemsize.height());
254                         eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
255                         painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
256                         Py_XDECREF(text);
257                         
258                                 /* when we have no label, align value to the left. (FIXME: 
259                                    don't we want to specifiy this individually?) */
260                         int value_alignment_left = !*string;
261                         
262                                 /* now, handle the value. get 2nd part from tuple*/
263                         value = PyTuple_GET_ITEM(item, 1);
264                         if (value)
265                         {
266                                 ePyObject args = PyTuple_New(1);
267                                 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
268                                 
269                                         /* CallObject will call __call__ which should return the value tuple */
270                                 value = PyObject_CallObject(value, args);
271                                 
272                                 if (PyErr_Occurred())
273                                         PyErr_Print();
274
275                                 Py_DECREF(args);
276                                         /* the PyInt was stolen. */
277                         }
278                         
279                                 /*  check if this is really a tuple */
280                         if (value && PyTuple_Check(value))
281                         {
282                                         /* convert type to string */
283                                 ePyObject type = PyTuple_GET_ITEM(value, 0);
284                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
285                                 
286                                 if (atype)
287                                 {
288                                         if (!strcmp(atype, "text"))
289                                         {
290                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
291                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
292                                                 painter.setFont(fnt2);
293                                                 if (value_alignment_left)
294                                                         painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
295                                                 else
296                                                         painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
297
298                                                         /* pvalue is borrowed */
299                                         } else if (!strcmp(atype, "slider"))
300                                         {
301                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
302                                                 ePyObject psize = PyTuple_GET_ITEM(value, 2);
303                                                 
304                                                         /* convert value to Long. fallback to -1 on error. */
305                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
306                                                 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
307                                                 
308                                                         /* calc. slider length */
309                                                 int width = item_right.width() * value / size;
310                                                 int height = item_right.height();
311                                                 
312                                                                                                 
313                                                         /* draw slider */
314                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
315                                                 //hack - make it customizable
316                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
317                                                 
318                                                         /* pvalue is borrowed */
319                                         } else if (!strcmp(atype, "mtext"))
320                                         {
321                                                 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
322                                                 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
323                                                 int xoffs = value_alignment_left ? 0 : m_seperation;
324                                                 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
325                                                 para->setFont(fnt2);
326                                                 para->renderString(text, 0);
327                                                 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
328                                                 int glyphs = para->size();
329                                                 
330                                                 ePyObject plist;
331                                                 
332                                                 if (PyTuple_Size(value) >= 3)
333                                                         plist = PyTuple_GET_ITEM(value, 2);
334                                                 
335                                                 int entries = 0;
336
337                                                 if (plist && PyList_Check(plist))
338                                                         entries = PyList_Size(plist);
339                                                 
340                                                 for (int i = 0; i < entries; ++i)
341                                                 {
342                                                         ePyObject entry = PyList_GET_ITEM(plist, i);
343                                                         int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
344                                                         
345                                                         if ((num < 0) || (num >= glyphs))
346                                                                 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
347                                                         else
348                                                         {
349                                                                 para->setGlyphFlag(num, GS_INVERT);
350                                                                 eRect bbox;
351                                                                 bbox = para->getGlyphBBox(num);
352                                                                 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
353                                                                 painter.fill(bbox);
354                                                         }
355                                                                 /* entry is borrowed */
356                                                 }
357                                                 
358                                                 painter.renderPara(para, ePoint(0, 0));
359                                                         /* pvalue is borrowed */
360                                                         /* plist is 0 or borrowed */
361                                         }
362                                 }
363                                         /* type is borrowed */
364                         } else
365                                 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
366                                 /* value is borrowed */
367                 }
368
369                 if (selected)
370                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
371         }
372         
373         painter.clippop();
374 }
375
376 int eListboxPythonConfigContent::currentCursorSelectable()
377 {
378         return eListboxPythonStringContent::currentCursorSelectable();
379 }
380
381 //////////////////////////////////////
382
383         /* todo: make a real infrastructure here! */
384 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
385
386 eListboxPythonMultiContent::eListboxPythonMultiContent()
387 {
388 }
389
390 eListboxPythonMultiContent::~eListboxPythonMultiContent()
391 {
392         if (m_buildFunc)
393                 Py_DECREF(m_buildFunc);
394 }
395
396 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
397 {
398         eRect itemrect(offset, m_itemsize);
399         painter.clip(itemrect);
400         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
401         painter.clear();
402
403         ePyObject items;
404
405         if (m_list && cursorValid())
406         {
407                 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
408
409                 if (m_buildFunc)
410                 {
411                         if (PyCallable_Check(m_buildFunc))  // when we have a buildFunc then call it
412                         {
413                                 if (PyTuple_Check(items))
414                                         items = PyObject_CallObject(m_buildFunc, items);
415                                 else
416                                         eDebug("items is no tuple");
417                         }
418                         else
419                                 eDebug("buildfunc is not callable");
420                 }
421
422                 if (!items)
423                 {
424                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
425                         goto error_out;
426                 }
427                 
428                 if (!PyList_Check(items))
429                 {
430                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
431                         goto error_out;
432                 }
433                 
434                 int size = PyList_Size(items);
435                 for (int i = 1; i < size; ++i)
436                 {
437                         ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
438                         
439                         if (!item)
440                         {
441                                 eDebug("eListboxPythonMultiContent: ?");
442                                 goto error_out;
443                         }
444                         
445                         ePyObject px, py, pwidth, pheight, pfnt, pstring, pflags, pcolor;
446                 
447                         /*
448                                 we have a list of tuples:
449                                 
450                                 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
451
452                                 or, for a progress:
453                                 (1, x, y, width, height, filled_percent )
454
455                                 or, for a pixmap:
456                                 
457                                 (2, x, y, width, height, pixmap )
458                                 
459                          */
460                         
461                         if (!PyTuple_Check(item))
462                         {
463                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
464                                 goto error_out;
465                         }
466
467                         int size = PyTuple_Size(item);
468
469                         if (!size)
470                         {
471                                 eDebug("eListboxPythonMultiContent receive empty tuple.");
472                                 goto error_out;
473                         }
474
475                         int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
476
477                         if (size > 5)
478                         {
479                                 px = PyTuple_GET_ITEM(item, 1);
480                                 py = PyTuple_GET_ITEM(item, 2);
481                                 pwidth = PyTuple_GET_ITEM(item, 3);
482                                 pheight = PyTuple_GET_ITEM(item, 4);
483                                 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
484                                 if (size > 7)
485                                 {
486                                         pflags = PyTuple_GET_ITEM(item, 6);
487                                         pstring = PyTuple_GET_ITEM(item, 7);
488                                 }
489                                 if (size > 8)
490                                         pcolor = PyTuple_GET_ITEM(item, 8);
491                         }
492                         
493                         switch (type)
494                         {
495                         case TYPE_TEXT: // text
496                         {
497                                 if (!(px && py && pwidth && pheight && pfnt && pstring))
498                                 {
499                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
500                                         goto error_out;
501                                 }
502                                 
503                                 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
504                                 int x = PyInt_AsLong(px);
505                                 int y = PyInt_AsLong(py);
506                                 int width = PyInt_AsLong(pwidth);
507                                 int height = PyInt_AsLong(pheight);
508                                 int flags = PyInt_AsLong(pflags);
509                                 int fnt = PyInt_AsLong(pfnt);
510                                 
511                                 if (pcolor)
512                                 {
513                                         int color = PyInt_AsLong(pcolor);
514                                         painter.setForegroundColor(gRGB(color));
515                                 }
516                                 
517                                 if (m_font.find(fnt) == m_font.end())
518                                 {
519                                         eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
520                                         goto error_out;
521                                 }
522                                 
523                                 eRect r = eRect(x, y, width, height);
524                                 r.moveBy(offset);
525                                 r &= itemrect;
526                                 
527                                 painter.setFont(m_font[fnt]);
528                                 
529                                 painter.clip(r);
530                                 painter.renderText(r, string, flags);
531                                 painter.clippop();
532                                 break;
533                         }
534                         case TYPE_PROGRESS: // Progress
535                         {
536                                 if (!(px && py && pwidth && pheight && pfnt))
537                                 {
538                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
539                                         goto error_out;
540                                 }
541                                 int x = PyInt_AsLong(px);
542                                 int y = PyInt_AsLong(py);
543                                 int width = PyInt_AsLong(pwidth);
544                                 int height = PyInt_AsLong(pheight);
545                                 int filled = PyInt_AsLong(pfnt);
546
547                                 eRect r = eRect(x, y, width, height);
548                                 r.moveBy(offset);
549                                 r &= itemrect;
550
551                                 painter.clip(r);
552                                 int bwidth=2;  // borderwidth hardcoded yet
553
554                                 // border
555                                 eRect rc = eRect(x, y, width, bwidth);
556                                 rc.moveBy(offset);
557                                 painter.fill(rc);
558
559                                 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
560                                 rc.moveBy(offset);
561                                 painter.fill(rc);
562
563                                 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
564                                 rc.moveBy(offset);
565                                 painter.fill(rc);
566
567                                 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
568                                 rc.moveBy(offset);
569                                 painter.fill(rc);
570
571                                 // progress
572                                 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
573                                 rc.moveBy(offset);
574                                 painter.fill(rc);
575
576                                 painter.clippop();
577
578                                 break;
579                         }
580                         case TYPE_PIXMAP_ALPHATEST:
581                         case TYPE_PIXMAP: // pixmap
582                         {
583                                 if (!(px && py && pwidth && pheight && pfnt))
584                                 {
585                                         eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
586                                         goto error_out;
587                                 }
588                                 int x = PyInt_AsLong(px);
589                                 int y = PyInt_AsLong(py);
590                                 int width = PyInt_AsLong(pwidth);
591                                 int height = PyInt_AsLong(pheight);
592                                 ePtr<gPixmap> pixmap;
593                                 if (SwigFromPython(pixmap, pfnt))
594                                 {
595                                         eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
596                                         goto error_out;
597                                 }
598
599                                 eRect r = eRect(x, y, width, height);
600                                 r.moveBy(offset);
601                                 r &= itemrect;
602                                 
603                                 painter.clip(r);
604                                 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
605                                 painter.clippop();
606
607                                 break;
608                         }
609                         default:
610                                 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
611                                 goto error_out;
612                         }
613                         
614                         if (pcolor)
615                                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
616                 }
617         }
618         
619         if (selected)
620                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
621
622 error_out:
623         if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
624                 Py_DECREF(items);
625
626         painter.clippop();
627 }
628
629 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
630 {
631         if (m_buildFunc)
632                 Py_DECREF(m_buildFunc);
633         m_buildFunc=cb;
634         if (cb)
635                 Py_INCREF(m_buildFunc);
636 }
637
638 int eListboxPythonMultiContent::currentCursorSelectable()
639 {
640         /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
641         if (m_list && cursorValid())
642         {
643                 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
644                 if (PyList_Check(item))
645                 {
646                         item = PyList_GET_ITEM(item, 0);
647                         if (item != Py_None)
648                                 return 1;
649                 }
650                 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
651                 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
652                         return 1;
653         }
654         return 0;
655 }
656
657 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
658 {
659         if (font)
660                 m_font[fnt] = font;
661         else
662                 m_font.erase(fnt);
663 }