- minor bugfix (allow empty content)
[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
33 DEFINE_REF(eListboxTestContent);
34
35 void eListboxTestContent::cursorHome()
36 {
37         m_cursor = 0;
38 }
39
40 void eListboxTestContent::cursorEnd()
41 {
42         m_cursor = size();
43 }
44
45 int eListboxTestContent::cursorMove(int count)
46 {
47         m_cursor += count;
48         
49         if (m_cursor < 0)
50                 cursorHome();
51         else if (m_cursor > size())
52                 cursorEnd();
53         return 0;
54 }
55
56 int eListboxTestContent::cursorValid()
57 {
58         return m_cursor < size();
59 }
60
61 int eListboxTestContent::cursorSet(int n)
62 {
63         m_cursor = n;
64         
65         if (m_cursor < 0)
66                 cursorHome();
67         else if (m_cursor > size())
68                 cursorEnd();
69         return 0;
70 }
71
72 int eListboxTestContent::cursorGet()
73 {
74         return m_cursor;
75 }
76
77 void eListboxTestContent::cursorSave()
78 {
79         m_saved_cursor = m_cursor;
80 }
81
82 void eListboxTestContent::cursorRestore()
83 {
84         m_cursor = m_saved_cursor;
85 }
86
87 int eListboxTestContent::size()
88 {
89         return 10;
90 }
91         
92 RESULT eListboxTestContent::connectItemChanged(const Slot0<void> &itemChanged, ePtr<eConnection> &connection)
93 {
94         return 0;
95 }
96
97 void eListboxTestContent::setSize(const eSize &size)
98 {
99         m_size = size;
100 }
101
102 void eListboxTestContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
103 {
104         ePtr<gFont> fnt = new gFont("Arial", 14);
105         painter.clip(eRect(offset, m_size));
106         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
107         painter.clear();
108
109         if (cursorValid())
110         {
111                 painter.setFont(fnt);
112                 char string[10];
113                 sprintf(string, "%d.)", m_cursor);
114                 
115                 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
116                 
117                 painter.renderText(eRect(text_offset, m_size), string);
118                 
119                 if (selected)
120                         style.drawFrame(painter, eRect(offset, m_size), eWindowStyle::frameListboxEntry);
121         }
122         
123         painter.clippop();
124 }
125
126 //////////////////////////////////////
127
128 DEFINE_REF(eListboxStringContent);
129
130 eListboxStringContent::eListboxStringContent()
131 {
132         m_size = 0;
133         cursorHome();
134 }
135
136 void eListboxStringContent::cursorHome()
137 {
138         m_cursor = m_list.begin();
139         m_cursor_number = 0;
140 }
141
142 void eListboxStringContent::cursorEnd()
143 {
144         m_cursor = m_list.end();
145         m_cursor_number = m_size;
146 }
147
148 int eListboxStringContent::cursorMove(int count)
149 {
150         if (count > 0)
151         {
152                 while (count && (m_cursor != m_list.end()))
153                 {
154                         ++m_cursor;
155                         ++m_cursor_number;
156                         --count;
157                 }
158         } else if (count < 0)
159         {
160                 while (count && (m_cursor != m_list.begin()))
161                 {
162                         --m_cursor;
163                         --m_cursor_number;
164                         ++count;
165                 }
166         }
167         
168         return 0;
169 }
170
171 int eListboxStringContent::cursorValid()
172 {
173         return m_cursor != m_list.end();
174 }
175
176 int eListboxStringContent::cursorSet(int n)
177 {
178         cursorHome();
179         cursorMove(n);
180         
181         return 0;
182 }
183
184 int eListboxStringContent::cursorGet()
185 {
186         return m_cursor_number;
187 }
188
189 void eListboxStringContent::cursorSave()
190 {
191         m_saved_cursor = m_cursor;
192         m_saved_cursor_number = m_cursor_number;
193 }
194
195 void eListboxStringContent::cursorRestore()
196 {
197         m_cursor = m_saved_cursor;
198         m_cursor_number = m_saved_cursor_number;
199 }
200
201 int eListboxStringContent::size()
202 {
203         return m_size;
204 }
205         
206 RESULT eListboxStringContent::connectItemChanged(const Slot0<void> &itemChanged, ePtr<eConnection> &connection)
207 {
208         return 0;
209 }
210
211 void eListboxStringContent::setSize(const eSize &size)
212 {
213         m_itemsize = size;
214 }
215
216 void eListboxStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
217 {
218         ePtr<gFont> fnt = new gFont("Arial", 14);
219         painter.clip(eRect(offset, m_itemsize));
220         style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
221         painter.clear();
222         
223         eDebug("item %d", m_cursor_number);
224         if (cursorValid())
225         {
226                 eDebug("is valid..");
227                 painter.setFont(fnt);
228                 
229                 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
230                 
231                 painter.renderText(eRect(text_offset, m_itemsize), *m_cursor);
232                 
233                 if (selected)
234                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
235         }
236         
237         painter.clippop();
238 }
239
240 void eListboxStringContent::setList(std::list<std::string> &list)
241 {
242         m_list = list;
243         m_size = list.size();
244         cursorHome();
245 }
246
247 //////////////////////////////////////
248
249 DEFINE_REF(eListboxPythonStringContent);
250
251 eListboxPythonStringContent::eListboxPythonStringContent()
252 {
253         m_list = 0;
254 }
255
256 eListboxPythonStringContent::~eListboxPythonStringContent()
257 {
258 }
259
260 void eListboxPythonStringContent::cursorHome()
261 {
262         m_cursor = 0;
263 }
264
265 void eListboxPythonStringContent::cursorEnd()
266 {
267         m_cursor = size();
268 }
269
270 int eListboxPythonStringContent::cursorMove(int count)
271 {
272         m_cursor += count;
273         
274         if (m_cursor < 0)
275                 cursorHome();
276         else if (m_cursor > size())
277                 cursorEnd();
278         return 0;
279 }
280
281 int eListboxPythonStringContent::cursorValid()
282 {
283         return m_cursor < size();
284 }
285
286 int eListboxPythonStringContent::cursorSet(int n)
287 {
288         m_cursor = n;
289         
290         if (m_cursor < 0)
291                 cursorHome();
292         else if (m_cursor > size())
293                 cursorEnd();
294         return 0;
295 }
296
297 int eListboxPythonStringContent::cursorGet()
298 {
299         return m_cursor;
300 }
301
302 void eListboxPythonStringContent::cursorSave()
303 {
304         m_saved_cursor = m_cursor;
305 }
306
307 void eListboxPythonStringContent::cursorRestore()
308 {
309         m_cursor = m_saved_cursor;
310 }
311
312 int eListboxPythonStringContent::size()
313 {
314         if (!m_list)
315                 return 0;
316         return PyList_Size(m_list);
317 }
318         
319 RESULT eListboxPythonStringContent::connectItemChanged(const Slot0<void> &itemChanged, ePtr<eConnection> &connection)
320 {
321         return 0;
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("Arial", 14);
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
371 PyObject *eListboxPythonStringContent::getCurrentSelection()
372 {
373         if (!m_list)
374                 return 0;
375         if (!cursorValid())
376                 return 0;
377         PyObject *r = PyList_GetItem(m_list, m_cursor);
378         Py_XINCREF(r);
379         return r;
380 }
381
382 //////////////////////////////////////