removed thedoc's colors because of broken network setup
[enigma2.git] / 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                                                                                                 
456                                                         /* draw slider */
457                                                 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
458                                                 //hack - make it customizable
459                                                 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
460                                                 
461                                                         /* pvalue is borrowed */
462                                         }
463                                 }
464                                 Py_XDECREF(type);
465                         }
466                                 /* value is borrowed */
467                 }
468
469                 if (selected)
470                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
471         }
472         
473         painter.clippop();
474 }
475
476 //////////////////////////////////////
477
478 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
479 {
480         painter.clip(eRect(offset, m_itemsize));
481         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
482         painter.clear();
483
484         if (m_list && cursorValid())
485         {
486                 PyObject *items = PyList_GetItem(m_list, m_cursor); // borrowed reference!
487                 
488                 if (!items)
489                 {
490                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
491                         painter.clippop();
492                         return;
493                 }
494                 
495                 if (!PyList_Check(items))
496                 {
497                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
498                         painter.clippop();
499                         return;
500                 }
501                 
502                 int size = PyList_Size(items);
503                 for (int i = 1; i < size; ++i)
504                 {
505                         PyObject *item = PyList_GetItem(items, i); // borrowed reference!
506                         
507                         if (!item)
508                         {
509                                 eDebug("eListboxPythonMultiContent: ?");
510                                 painter.clippop();
511                                 return;
512                         }
513                         
514                         
515                         PyObject *px, *py, *pwidth, *pheight, *pfnt, *pstring, *pflags;
516                 
517                         /*
518                                 we have a list of tuples:
519                                 
520                                 (x, y, width, height, fnt, flags, "bla" ),
521                                 
522                          */
523                         
524                         if (!PyTuple_Check(item))
525                         {
526                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
527                                 painter.clippop();
528                                 return;
529                         }
530                 
531                         px = PyTuple_GetItem(item, 0);
532                         py = PyTuple_GetItem(item, 1);
533                         pwidth = PyTuple_GetItem(item, 2);
534                         pheight = PyTuple_GetItem(item, 3);
535                         pfnt = PyTuple_GetItem(item, 4);
536                         pflags = PyTuple_GetItem(item, 5);
537                         pstring = PyTuple_GetItem(item, 6);
538                         
539                         if (!(px && py && pwidth && pheight && pfnt && pstring))
540                         {
541                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (x, y, width, height, fnt, flags, string[, ...])");
542                                 painter.clippop();
543                                 return;
544                         }
545         
546                         pstring = PyObject_Str(pstring);
547                         
548                         const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
549                         
550                         int x = PyInt_AsLong(px);
551                         int y = PyInt_AsLong(py);
552                         int width = PyInt_AsLong(pwidth);
553                         int height = PyInt_AsLong(pheight);
554                         int flags = PyInt_AsLong(pflags);
555                         
556                         int fnt = PyInt_AsLong(pfnt);
557                         
558                         if (m_font.find(fnt) == m_font.end())
559                         {
560                                 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
561                                 Py_XDECREF(pstring);
562                                 painter.clippop();
563                                 return;
564                         }
565                         
566                         eRect r = eRect(x, y, width, height);
567                         r.moveBy(offset);
568                         
569                         painter.setFont(m_font[fnt]);
570                         
571                         painter.renderText(r, string, flags);
572         
573                         Py_XDECREF(pstring);
574                         
575                         if (selected)
576                                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
577                 }
578         }
579         
580         painter.clippop();
581 }
582
583 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
584 {
585         if (font)
586                 m_font[fnt] = font;
587         else
588                 m_font.erase(fnt);
589 }