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