1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
7 The basic idea is to have an interface which gives all relevant list
8 processing functions, and can be used by the listbox to browse trough
11 The listbox directly uses the implemented cursor. It tries hard to avoid
12 iterating trough the (possibly very large) list, so it should be O(1),
13 i.e. the performance should not be influenced by the size of the list.
15 The list interface knows how to draw the current entry to a specified
16 offset. Different interfaces can be used to adapt different lists,
17 pre-filter lists on the fly etc.
19 cursorSave/Restore is used to avoid re-iterating the list on redraw.
20 The current selection is always selected as cursor position, the
21 cursor is then positioned to the start, and then iterated. This gives
22 at most 2x m_items_per_page cursor movements per redraw, indepenent
23 of the size of the list.
25 Although cursorSet is provided, it should be only used when there is no
26 other way, as it involves iterating trough the list.
29 iListboxContent::~iListboxContent()
33 iListboxContent::iListboxContent(): m_listbox(0)
37 void iListboxContent::setListbox(eListbox *lb)
42 int iListboxContent::currentCursorSelectable()
47 //////////////////////////////////////
49 DEFINE_REF(eListboxPythonStringContent);
51 eListboxPythonStringContent::eListboxPythonStringContent()
56 eListboxPythonStringContent::~eListboxPythonStringContent()
61 void eListboxPythonStringContent::cursorHome()
66 void eListboxPythonStringContent::cursorEnd()
71 int eListboxPythonStringContent::cursorMove(int count)
77 else if (m_cursor > size())
82 int eListboxPythonStringContent::cursorValid()
84 return m_cursor < size();
87 int eListboxPythonStringContent::cursorSet(int n)
93 else if (m_cursor > size())
98 int eListboxPythonStringContent::cursorGet()
103 int eListboxPythonStringContent::currentCursorSelectable()
105 if (m_list && cursorValid())
107 PyObject *item = PyList_GET_ITEM(m_list, m_cursor);
108 if (!PyTuple_Check(item))
110 if (PyTuple_Size(item) >= 2)
116 void eListboxPythonStringContent::cursorSave()
118 m_saved_cursor = m_cursor;
121 void eListboxPythonStringContent::cursorRestore()
123 m_cursor = m_saved_cursor;
126 int eListboxPythonStringContent::size()
130 return PyList_Size(m_list);
133 void eListboxPythonStringContent::setSize(const eSize &size)
138 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
140 ePtr<gFont> fnt = new gFont("Regular", 20);
141 painter.clip(eRect(offset, m_itemsize));
142 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
145 if (m_list && cursorValid())
148 PyObject *item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
149 painter.setFont(fnt);
151 /* the user can supply tuples, in this case the first one will be displayed. */
152 if (PyTuple_Check(item))
154 if (PyTuple_Size(item) == 1)
156 item = PyTuple_GET_ITEM(item, 0);
161 int half_height = m_itemsize.height() / 2;
163 painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
166 const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
167 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
169 painter.setForegroundColor(gRGB(0x808080));
170 painter.renderText(eRect(text_offset, m_itemsize), string);
174 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
180 void eListboxPythonStringContent::setList(PyObject *list)
183 if (!PyList_Check(list))
193 m_listbox->entryReset(false);
196 PyObject *eListboxPythonStringContent::getCurrentSelection()
198 if (!(m_list && cursorValid()))
203 PyObject *r = PyList_GET_ITEM(m_list, m_cursor);
208 void eListboxPythonStringContent::invalidateEntry(int index)
211 m_listbox->entryChanged(index);
214 void eListboxPythonStringContent::invalidate()
217 m_listbox->invalidate();
220 //////////////////////////////////////
222 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
224 ePtr<gFont> fnt = new gFont("Regular", 20);
225 ePtr<gFont> fnt2 = new gFont("Regular", 16);
226 eRect itemrect(offset, m_itemsize);
227 painter.clip(itemrect);
228 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
231 if (m_list && cursorValid())
233 /* get current list item */
234 PyObject *item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
235 PyObject *text = 0, *value = 0;
236 painter.setFont(fnt);
238 /* the first tuple element is a string for the left side.
239 the second one will be called, and the result shall be an tuple.
242 the first one is the type (string).
243 the second one is the value. */
244 if (PyTuple_Check(item))
246 /* handle left part. get item from tuple, convert to string, display. */
248 text = PyTuple_GET_ITEM(item, 0);
249 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
250 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
251 eSize item_left = eSize(m_seperation, m_itemsize.height());
252 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
253 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
256 /* when we have no label, align value to the left. (FIXME:
257 don't we want to specifiy this individually?) */
258 int value_alignment_left = !*string;
260 /* now, handle the value. get 2nd part from tuple*/
261 value = PyTuple_GET_ITEM(item, 1);
264 PyObject *args = PyTuple_New(1);
265 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
267 /* CallObject will call __call__ which should return the value tuple */
268 value = PyObject_CallObject(value, args);
270 if (PyErr_Occurred())
274 /* the PyInt was stolen. */
277 /* check if this is really a tuple */
278 if (value && PyTuple_Check(value))
280 /* convert type to string */
281 PyObject *type = PyTuple_GET_ITEM(value, 0);
282 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
286 if (!strcmp(atype, "text"))
288 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
289 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
290 painter.setFont(fnt2);
291 if (value_alignment_left)
292 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
294 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
296 /* pvalue is borrowed */
297 } else if (!strcmp(atype, "slider"))
299 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
300 PyObject *psize = PyTuple_GET_ITEM(value, 2);
302 /* convert value to Long. fallback to -1 on error. */
303 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
304 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
306 /* calc. slider length */
307 int width = item_right.width() * value / size;
308 int height = item_right.height();
312 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
313 //hack - make it customizable
314 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
316 /* pvalue is borrowed */
317 } else if (!strcmp(atype, "mtext"))
319 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
320 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
321 int xoffs = value_alignment_left ? 0 : m_seperation;
322 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
324 para->renderString(text, 0);
325 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
326 int glyphs = para->size();
330 if (PyTuple_Size(value) >= 3)
331 plist = PyTuple_GET_ITEM(value, 2);
335 if (plist && PyList_Check(plist))
336 entries = PyList_Size(plist);
338 for (int i = 0; i < entries; ++i)
340 PyObject *entry = PyList_GET_ITEM(plist, i);
341 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
343 if ((num < 0) || (num >= glyphs))
344 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
347 para->setGlyphFlag(num, GS_INVERT);
349 bbox = para->getGlyphBBox(num);
350 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
353 /* entry is borrowed */
356 painter.renderPara(para, ePoint(0, 0));
357 /* pvalue is borrowed */
358 /* plist is 0 or borrowed */
361 /* type is borrowed */
363 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
364 /* value is borrowed */
368 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
374 int eListboxPythonConfigContent::currentCursorSelectable()
376 return eListboxPythonStringContent::currentCursorSelectable();
379 //////////////////////////////////////
381 /* todo: make a real infrastructure here! */
382 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
384 eListboxPythonMultiContent::eListboxPythonMultiContent()
389 eListboxPythonMultiContent::~eListboxPythonMultiContent()
392 Py_DECREF(m_buildFunc);
395 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
397 eRect itemrect(offset, m_itemsize);
398 painter.clip(itemrect);
399 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
404 if (m_list && cursorValid())
406 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
410 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
412 if (PyTuple_Check(items))
413 items = PyObject_CallObject(m_buildFunc, items);
415 eDebug("items is no tuple");
418 eDebug("buildfunc is not callable");
423 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
427 if (!PyList_Check(items))
429 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
433 int size = PyList_Size(items);
434 for (int i = 1; i < size; ++i)
436 PyObject *item = PyList_GET_ITEM(items, i); // borrowed reference!
440 eDebug("eListboxPythonMultiContent: ?");
444 PyObject *px = 0, *py = 0, *pwidth = 0, *pheight = 0, *pfnt = 0, *pstring = 0, *pflags = 0, *pcolor = 0;
447 we have a list of tuples:
449 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
452 (1, x, y, width, height, filled_percent )
456 (2, x, y, width, height, pixmap )
460 if (!PyTuple_Check(item))
462 eDebug("eListboxPythonMultiContent did not receive a tuple.");
466 int size = PyTuple_Size(item);
470 eDebug("eListboxPythonMultiContent receive empty tuple.");
474 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
478 px = PyTuple_GET_ITEM(item, 1);
479 py = PyTuple_GET_ITEM(item, 2);
480 pwidth = PyTuple_GET_ITEM(item, 3);
481 pheight = PyTuple_GET_ITEM(item, 4);
482 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
485 pflags = PyTuple_GET_ITEM(item, 6);
486 pstring = PyTuple_GET_ITEM(item, 7);
489 pcolor = PyTuple_GET_ITEM(item, 8);
494 case TYPE_TEXT: // text
496 if (!(px && py && pwidth && pheight && pfnt && pstring))
498 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
502 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
503 int x = PyInt_AsLong(px);
504 int y = PyInt_AsLong(py);
505 int width = PyInt_AsLong(pwidth);
506 int height = PyInt_AsLong(pheight);
507 int flags = PyInt_AsLong(pflags);
508 int fnt = PyInt_AsLong(pfnt);
512 int color = PyInt_AsLong(pcolor);
513 painter.setForegroundColor(gRGB(color));
516 if (m_font.find(fnt) == m_font.end())
518 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
522 eRect r = eRect(x, y, width, height);
526 painter.setFont(m_font[fnt]);
529 painter.renderText(r, string, flags);
533 case TYPE_PROGRESS: // Progress
535 if (!(px && py && pwidth && pheight && pfnt))
537 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
540 int x = PyInt_AsLong(px);
541 int y = PyInt_AsLong(py);
542 int width = PyInt_AsLong(pwidth);
543 int height = PyInt_AsLong(pheight);
544 int filled = PyInt_AsLong(pfnt);
546 eRect r = eRect(x, y, width, height);
551 int bwidth=2; // borderwidth hardcoded yet
554 eRect rc = eRect(x, y, width, bwidth);
558 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
562 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
566 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
571 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
579 case TYPE_PIXMAP_ALPHATEST:
580 case TYPE_PIXMAP: // pixmap
582 if (!(px && py && pwidth && pheight && pfnt))
584 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
587 int x = PyInt_AsLong(px);
588 int y = PyInt_AsLong(py);
589 int width = PyInt_AsLong(pwidth);
590 int height = PyInt_AsLong(pheight);
591 ePtr<gPixmap> pixmap;
592 if (SwigFromPython(pixmap, pfnt))
594 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
598 eRect r = eRect(x, y, width, height);
603 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
609 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
614 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
619 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
622 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
628 void eListboxPythonMultiContent::setBuildFunc(PyObject *cb)
631 Py_DECREF(m_buildFunc);
634 Py_INCREF(m_buildFunc);
637 int eListboxPythonMultiContent::currentCursorSelectable()
639 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
640 if (m_list && cursorValid())
642 PyObject *item = PyList_GET_ITEM(m_list, m_cursor);
643 if (PyList_Check(item))
645 item = PyList_GET_ITEM(item, 0);
649 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
650 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
656 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)