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