- add config slider
[vuplus_dvbapp] / lib / gui / elistboxcontent.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <Python.h>
4
5 /*
6     The basic idea is to have an interface which gives all relevant list
7     processing functions, and can be used by the listbox to browse trough
8     the list.
9     
10     The listbox directly uses the implemented cursor. It tries hard to avoid
11     iterating trough the (possibly very large) list, so it should be O(1),
12     i.e. the performance should not be influenced by the size of the list.
13     
14     The list interface knows how to draw the current entry to a specified 
15     offset. Different interfaces can be used to adapt different lists,
16     pre-filter lists on the fly etc.
17     
18                 cursorSave/Restore is used to avoid re-iterating the list on redraw.
19                 The current selection is always selected as cursor position, the
20     cursor is then positioned to the start, and then iterated. This gives
21     at most 2x m_items_per_page cursor movements per redraw, indepenent
22     of the size of the list.
23     
24     Although cursorSet is provided, it should be only used when there is no
25     other way, as it involves iterating trough the list.
26  */
27
28 iListboxContent::~iListboxContent()
29 {
30 }
31
32 iListboxContent::iListboxContent(): m_listbox(0)
33 {
34 }
35
36 void iListboxContent::setListbox(eListbox *lb)
37 {
38         m_listbox = lb;
39 }
40
41 DEFINE_REF(eListboxTestContent);
42
43 void eListboxTestContent::cursorHome()
44 {
45         m_cursor = 0;
46 }
47
48 void eListboxTestContent::cursorEnd()
49 {
50         m_cursor = size();
51 }
52
53 int eListboxTestContent::cursorMove(int count)
54 {
55         m_cursor += count;
56         
57         if (m_cursor < 0)
58                 cursorHome();
59         else if (m_cursor > size())
60                 cursorEnd();
61         return 0;
62 }
63
64 int eListboxTestContent::cursorValid()
65 {
66         return m_cursor < size();
67 }
68
69 int eListboxTestContent::cursorSet(int n)
70 {
71         m_cursor = n;
72         
73         if (m_cursor < 0)
74                 cursorHome();
75         else if (m_cursor > size())
76                 cursorEnd();
77         return 0;
78 }
79
80 int eListboxTestContent::cursorGet()
81 {
82         return m_cursor;
83 }
84
85 void eListboxTestContent::cursorSave()
86 {
87         m_saved_cursor = m_cursor;
88 }
89
90 void eListboxTestContent::cursorRestore()
91 {
92         m_cursor = m_saved_cursor;
93 }
94
95 int eListboxTestContent::size()
96 {
97         return 10;
98 }
99         
100 RESULT eListboxTestContent::connectItemChanged(const Slot0<void> &itemChanged, ePtr<eConnection> &connection)
101 {
102         return 0;
103 }
104
105 void eListboxTestContent::setSize(const eSize &size)
106 {
107         m_size = size;
108 }
109
110 void eListboxTestContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
111 {
112         ePtr<gFont> fnt = new gFont("Arial", 14);
113         painter.clip(eRect(offset, m_size));
114         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
115         painter.clear();
116
117         if (cursorValid())
118         {
119                 painter.setFont(fnt);
120                 char string[10];
121                 sprintf(string, "%d.)", m_cursor);
122                 
123                 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
124                 
125                 painter.renderText(eRect(text_offset, m_size), string);
126                 
127                 if (selected)
128                         style.drawFrame(painter, eRect(offset, m_size), eWindowStyle::frameListboxEntry);
129         }
130         
131         painter.clippop();
132 }
133
134 //////////////////////////////////////
135
136 DEFINE_REF(eListboxStringContent);
137
138 eListboxStringContent::eListboxStringContent()
139 {
140         m_size = 0;
141         cursorHome();
142 }
143
144 void eListboxStringContent::cursorHome()
145 {
146         m_cursor = m_list.begin();
147         m_cursor_number = 0;
148 }
149
150 void eListboxStringContent::cursorEnd()
151 {
152         m_cursor = m_list.end();
153         m_cursor_number = m_size;
154 }
155
156 int eListboxStringContent::cursorMove(int count)
157 {
158         if (count > 0)
159         {
160                 while (count && (m_cursor != m_list.end()))
161                 {
162                         ++m_cursor;
163                         ++m_cursor_number;
164                         --count;
165                 }
166         } else if (count < 0)
167         {
168                 while (count && (m_cursor != m_list.begin()))
169                 {
170                         --m_cursor;
171                         --m_cursor_number;
172                         ++count;
173                 }
174         }
175         
176         return 0;
177 }
178
179 int eListboxStringContent::cursorValid()
180 {
181         return m_cursor != m_list.end();
182 }
183
184 int eListboxStringContent::cursorSet(int n)
185 {
186         cursorHome();
187         cursorMove(n);
188         
189         return 0;
190 }
191
192 int eListboxStringContent::cursorGet()
193 {
194         return m_cursor_number;
195 }
196
197 void eListboxStringContent::cursorSave()
198 {
199         m_saved_cursor = m_cursor;
200         m_saved_cursor_number = m_cursor_number;
201 }
202
203 void eListboxStringContent::cursorRestore()
204 {
205         m_cursor = m_saved_cursor;
206         m_cursor_number = m_saved_cursor_number;
207 }
208
209 int eListboxStringContent::size()
210 {
211         return m_size;
212 }
213         
214 void eListboxStringContent::setSize(const eSize &size)
215 {
216         m_itemsize = size;
217 }
218
219 void eListboxStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
220 {
221         ePtr<gFont> fnt = new gFont("Arial", 14);
222         painter.clip(eRect(offset, m_itemsize));
223         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
224         painter.clear();
225         
226         eDebug("item %d", m_cursor_number);
227         if (cursorValid())
228         {
229                 eDebug("is valid..");
230                 painter.setFont(fnt);
231                 
232                 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
233                 
234                 painter.renderText(eRect(text_offset, m_itemsize), *m_cursor);
235                 
236                 if (selected)
237                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
238         }
239         
240         painter.clippop();
241 }
242
243 void eListboxStringContent::setList(std::list<std::string> &list)
244 {
245         m_list = list;
246         m_size = list.size();
247         cursorHome();
248 }
249
250 //////////////////////////////////////
251
252 DEFINE_REF(eListboxPythonStringContent);
253
254 eListboxPythonStringContent::eListboxPythonStringContent()
255 {
256         m_list = 0;
257 }
258
259 eListboxPythonStringContent::~eListboxPythonStringContent()
260 {
261 }
262
263 void eListboxPythonStringContent::cursorHome()
264 {
265         m_cursor = 0;
266 }
267
268 void eListboxPythonStringContent::cursorEnd()
269 {
270         m_cursor = size();
271 }
272
273 int eListboxPythonStringContent::cursorMove(int count)
274 {
275         m_cursor += count;
276         
277         if (m_cursor < 0)
278                 cursorHome();
279         else if (m_cursor > size())
280                 cursorEnd();
281         return 0;
282 }
283
284 int eListboxPythonStringContent::cursorValid()
285 {
286         return m_cursor < size();
287 }
288
289 int eListboxPythonStringContent::cursorSet(int n)
290 {
291         m_cursor = n;
292         
293         if (m_cursor < 0)
294                 cursorHome();
295         else if (m_cursor > size())
296                 cursorEnd();
297         return 0;
298 }
299
300 int eListboxPythonStringContent::cursorGet()
301 {
302         return m_cursor;
303 }
304
305 void eListboxPythonStringContent::cursorSave()
306 {
307         m_saved_cursor = m_cursor;
308 }
309
310 void eListboxPythonStringContent::cursorRestore()
311 {
312         m_cursor = m_saved_cursor;
313 }
314
315 int eListboxPythonStringContent::size()
316 {
317         if (!m_list)
318                 return 0;
319         return PyList_Size(m_list);
320 }
321         
322 void eListboxPythonStringContent::setSize(const eSize &size)
323 {
324         m_itemsize = size;
325 }
326
327 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
328 {
329         ePtr<gFont> fnt = new gFont("Arial", 14);
330         painter.clip(eRect(offset, m_itemsize));
331         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
332         painter.clear();
333
334         if (m_list && cursorValid())
335         {
336                 PyObject *item = PyList_GetItem(m_list, m_cursor); // borrowed reference!
337                 painter.setFont(fnt);
338
339                         /* the user can supply tuples, in this case the first one will be displayed. */         
340                 if (PyTuple_Check(item))
341                         item = PyTuple_GetItem(item, 0);
342                 
343                 const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
344                 
345                 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
346                 
347                 painter.renderText(eRect(text_offset, m_itemsize), string);
348                 
349                 if (selected)
350                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
351         }
352         
353         painter.clippop();
354 }
355
356 void eListboxPythonStringContent::setList(PyObject *list)
357 {
358         Py_XDECREF(m_list);
359         if (!PyList_Check(list))
360         {
361                 m_list = 0;
362         } else
363         {
364                 m_list = list;
365                 Py_INCREF(m_list);
366         }
367 }
368
369 PyObject *eListboxPythonStringContent::getCurrentSelection()
370 {
371         if (!m_list)
372                 return 0;
373         if (!cursorValid())
374                 return 0;
375         PyObject *r = PyList_GetItem(m_list, m_cursor);
376         Py_XINCREF(r);
377         return r;
378 }
379
380 void eListboxPythonStringContent::invalidateEntry(int index)
381 {
382         if (m_listbox)
383                 m_listbox->entryChanged(index);
384 }
385
386 //////////////////////////////////////
387
388 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
389 {
390         ePtr<gFont> fnt = new gFont("Arial", 14);
391         ePtr<gFont> fnt2 = new gFont("Arial", 16);
392         painter.clip(eRect(offset, m_itemsize));
393         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
394         painter.clear();
395
396         if (m_list && cursorValid())
397         {
398                         /* get current list item */
399                 PyObject *item = PyList_GetItem(m_list, m_cursor); // borrowed reference!
400                 PyObject *text = 0, *value = 0;
401                 painter.setFont(fnt);
402
403                         /* the first tuple element is a string for the left side.
404                            the second one will be called, and the result shall be an tuple.
405                            
406                            of this tuple,
407                            the first one is the type (string).
408                            the second one is the value. */
409                 if (PyTuple_Check(item))
410                 {
411                                 /* handle left part. get item from tuple, convert to string, display. */
412                                 
413                         text = PyTuple_GetItem(item, 0);
414                         text = PyObject_Str(text);
415                         const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
416                         eSize item_left = eSize(m_seperation, m_itemsize.height());
417                         eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
418                         painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
419                         Py_XDECREF(text);
420                         
421                                 /* now, handle the value. get 2nd part from tuple*/
422                         value = PyTuple_GetItem(item, 1);
423                         if (value)
424                                         /* CallObject will call __call__ which should return the value tuple */
425                                 value = PyObject_CallObject(value, 0);
426                         
427                                 /*  check if this is really a tuple */
428                         if (PyTuple_Check(value))
429                         {
430                                         /* convert type to string */
431                                 PyObject *type = PyTuple_GetItem(value, 0);
432                                 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
433                                 
434                                 if (atype)
435                                 {
436                                         if (!strcmp(atype, "text"))
437                                         {
438                                                 PyObject *pvalue = PyTuple_GetItem(value, 1);
439                                                 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
440                                                 painter.setFont(fnt2);
441                                                 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
442
443                                                         /* pvalue is borrowed */
444                                         } else if (!strcmp(atype, "slider"))
445                                         {
446                                                 PyObject *pvalue = PyTuple_GetItem(value, 1);
447                                                 
448                                                         /* convert value to Long. fallback to -1 on error. */
449                                                 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
450                                                 
451                                                         /* calc. slider length */
452                                                 int width = item_right.width() * value / 100;
453                                                 int height = item_right.height();
454                                                 
455                                                         /* draw slider */
456                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
457                                                 
458                                                         /* pvalue is borrowed */
459                                         }
460                                 }
461                                 Py_XDECREF(type);
462                         }
463                                 /* value is borrowed */
464                 }
465
466                 if (selected)
467                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
468         }
469         
470         painter.clippop();
471 }
472
473 //////////////////////////////////////
474
475 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
476 {
477         painter.clip(eRect(offset, m_itemsize));
478         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
479         painter.clear();
480
481         if (m_list && cursorValid())
482         {
483                 PyObject *items = PyList_GetItem(m_list, m_cursor); // borrowed reference!
484                 
485                 if (!items)
486                 {
487                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
488                         painter.clippop();
489                         return;
490                 }
491                 
492                 if (!PyList_Check(items))
493                 {
494                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
495                         painter.clippop();
496                         return;
497                 }
498                 
499                 int size = PyList_Size(items);
500                 for (int i = 1; i < size; ++i)
501                 {
502                         PyObject *item = PyList_GetItem(items, i); // borrowed reference!
503                         
504                         if (!item)
505                         {
506                                 eDebug("eListboxPythonMultiContent: ?");
507                                 painter.clippop();
508                                 return;
509                         }
510                         
511                         
512                         PyObject *px, *py, *pwidth, *pheight, *pfnt, *pstring, *pflags;
513                 
514                         /*
515                                 we have a list of tuples:
516                                 
517                                 (x, y, width, height, fnt, flags, "bla" ),
518                                 
519                          */
520                         
521                         if (!PyTuple_Check(item))
522                         {
523                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
524                                 painter.clippop();
525                                 return;
526                         }
527                 
528                         px = PyTuple_GetItem(item, 0);
529                         py = PyTuple_GetItem(item, 1);
530                         pwidth = PyTuple_GetItem(item, 2);
531                         pheight = PyTuple_GetItem(item, 3);
532                         pfnt = PyTuple_GetItem(item, 4);
533                         pflags = PyTuple_GetItem(item, 5);
534                         pstring = PyTuple_GetItem(item, 6);
535                         
536                         if (!(px && py && pwidth && pheight && pfnt && pstring))
537                         {
538                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (x, y, width, height, fnt, flags, string[, ...])");
539                                 painter.clippop();
540                                 return;
541                         }
542         
543                         pstring = PyObject_Str(pstring);
544                         
545                         const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
546                         
547                         int x = PyInt_AsLong(px);
548                         int y = PyInt_AsLong(py);
549                         int width = PyInt_AsLong(pwidth);
550                         int height = PyInt_AsLong(pheight);
551                         int flags = PyInt_AsLong(pflags);
552                         
553                         int fnt = PyInt_AsLong(pfnt);
554                         
555                         if (m_font.find(fnt) == m_font.end())
556                         {
557                                 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
558                                 Py_XDECREF(pstring);
559                                 painter.clippop();
560                                 return;
561                         }
562                         
563                         eRect r = eRect(x, y, width, height);
564                         r.moveBy(offset);
565                         
566                         painter.setFont(m_font[fnt]);
567                         
568                         painter.renderText(r, string, flags);
569         
570                         Py_XDECREF(pstring);
571                         
572                         if (selected)
573                                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
574                 }
575         }
576         
577         painter.clippop();
578 }
579
580 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
581 {
582         if (font)
583                 m_font[fnt] = font;
584         else
585                 m_font.erase(fnt);
586 }