- start working on compositing support
[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                 PyObject *item = PyList_GetItem(m_list, m_cursor); // borrowed reference!
399                 PyObject *text = 0, *value = 0;
400                 painter.setFont(fnt);
401
402                         /* the user can supply tuples, in this case the first one will be displayed. */         
403                 if (PyTuple_Check(item))
404                 {
405                         text = PyTuple_GetItem(item, 0);
406                         value = PyTuple_GetItem(item, 1);
407                 }
408                 
409                 text = PyObject_Str(text);
410                 value = PyObject_Str(value);
411                 
412                 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
413                 const char *string_val = (value && PyString_Check(value)) ? PyString_AsString(value) : "<not-a-string>";
414                 
415                 eSize item_left = eSize(m_seperation, m_itemsize.height());
416                 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
417                 
418                 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
419                 
420                 painter.setFont(fnt2);
421                 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), string_val, gPainter::RT_HALIGN_RIGHT);
422                 
423                 Py_XDECREF(text);
424                 Py_XDECREF(value);
425                 
426                 if (selected)
427                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
428         }
429         
430         painter.clippop();
431 }
432
433 //////////////////////////////////////
434
435 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
436 {
437         painter.clip(eRect(offset, m_itemsize));
438         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
439         painter.clear();
440
441         if (m_list && cursorValid())
442         {
443                 PyObject *items = PyList_GetItem(m_list, m_cursor); // borrowed reference!
444                 
445                 if (!items)
446                 {
447                         eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
448                         painter.clippop();
449                         return;
450                 }
451                 
452                 if (!PyList_Check(items))
453                 {
454                         eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
455                         painter.clippop();
456                         return;
457                 }
458                 
459                 int size = PyList_Size(items);
460                 for (int i = 1; i < size; ++i)
461                 {
462                         PyObject *item = PyList_GetItem(items, i); // borrowed reference!
463                         
464                         if (!item)
465                         {
466                                 eDebug("eListboxPythonMultiContent: ?");
467                                 painter.clippop();
468                                 return;
469                         }
470                         
471                         
472                         PyObject *px, *py, *pwidth, *pheight, *pfnt, *pstring, *pflags;
473                 
474                         /*
475                                 we have a list of tuples:
476                                 
477                                 (x, y, width, height, fnt, flags, "bla" ),
478                                 
479                          */
480                         
481                         if (!PyTuple_Check(item))
482                         {
483                                 eDebug("eListboxPythonMultiContent did not receive a tuple.");
484                                 painter.clippop();
485                                 return;
486                         }
487                 
488                         px = PyTuple_GetItem(item, 0);
489                         py = PyTuple_GetItem(item, 1);
490                         pwidth = PyTuple_GetItem(item, 2);
491                         pheight = PyTuple_GetItem(item, 3);
492                         pfnt = PyTuple_GetItem(item, 4);
493                         pflags = PyTuple_GetItem(item, 5);
494                         pstring = PyTuple_GetItem(item, 6);
495                         
496                         if (!(px && py && pwidth && pheight && pfnt && pstring))
497                         {
498                                 eDebug("eListboxPythonMultiContent received too small tuple (must be (x, y, width, height, fnt, flags, string[, ...])");
499                                 painter.clippop();
500                                 return;
501                         }
502         
503                         pstring = PyObject_Str(pstring);
504                         
505                         const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
506                         
507                         int x = PyInt_AsLong(px);
508                         int y = PyInt_AsLong(py);
509                         int width = PyInt_AsLong(pwidth);
510                         int height = PyInt_AsLong(pheight);
511                         int flags = PyInt_AsLong(pflags);
512                         
513                         int fnt = PyInt_AsLong(pfnt);
514                         
515                         if (m_font.find(fnt) == m_font.end())
516                         {
517                                 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
518                                 Py_XDECREF(pstring);
519                                 painter.clippop();
520                                 return;
521                         }
522                         
523                         eRect r = eRect(x, y, width, height);
524                         r.moveBy(offset);
525                         
526                         painter.setFont(m_font[fnt]);
527                         
528                         painter.renderText(r, string, flags);
529         
530                         Py_XDECREF(pstring);
531                         
532                         if (selected)
533                                 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
534                 }
535         }
536         
537         painter.clippop();
538 }
539
540 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
541 {
542         if (font)
543                 m_font[fnt] = font;
544         else
545                 m_font.erase(fnt);
546 }