1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.h>
4 #include <lib/python/python.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)
40 m_listbox->setItemHeight(getItemHeight());
43 int iListboxContent::currentCursorSelectable()
48 //////////////////////////////////////
50 DEFINE_REF(eListboxPythonStringContent);
52 eListboxPythonStringContent::eListboxPythonStringContent(): m_itemheight(25)
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 ePyObject 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 ePyObject 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(ePyObject list)
183 if (!PyList_Check(list))
185 m_list = ePyObject();
193 m_listbox->entryReset(false);
196 PyObject *eListboxPythonStringContent::getCurrentSelection()
198 if (!(m_list && cursorValid()))
201 ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
206 void eListboxPythonStringContent::invalidateEntry(int index)
209 m_listbox->entryChanged(index);
212 void eListboxPythonStringContent::invalidate()
218 m_listbox->moveSelectionTo(s?s-1:0);
219 m_listbox->invalidate();
223 //////////////////////////////////////
225 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
227 ePtr<gFont> fnt = new gFont("Regular", 20);
228 ePtr<gFont> fnt2 = new gFont("Regular", 16);
229 eRect itemrect(offset, m_itemsize);
230 painter.clip(itemrect);
231 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
234 if (m_list && cursorValid())
236 /* get current list item */
237 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
238 ePyObject text, value;
239 painter.setFont(fnt);
241 /* the first tuple element is a string for the left side.
242 the second one will be called, and the result shall be an tuple.
245 the first one is the type (string).
246 the second one is the value. */
247 if (PyTuple_Check(item))
249 /* handle left part. get item from tuple, convert to string, display. */
251 text = PyTuple_GET_ITEM(item, 0);
252 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
253 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
254 eSize item_left = eSize(m_seperation, m_itemsize.height());
255 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
256 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
259 /* when we have no label, align value to the left. (FIXME:
260 don't we want to specifiy this individually?) */
261 int value_alignment_left = !*string;
263 /* now, handle the value. get 2nd part from tuple*/
264 value = PyTuple_GET_ITEM(item, 1);
267 ePyObject args = PyTuple_New(1);
268 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
270 /* CallObject will call __call__ which should return the value tuple */
271 value = PyObject_CallObject(value, args);
273 if (PyErr_Occurred())
277 /* the PyInt was stolen. */
280 /* check if this is really a tuple */
281 if (value && PyTuple_Check(value))
283 /* convert type to string */
284 ePyObject type = PyTuple_GET_ITEM(value, 0);
285 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
289 if (!strcmp(atype, "text"))
291 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
292 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
293 painter.setFont(fnt2);
294 if (value_alignment_left)
295 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
297 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
299 /* pvalue is borrowed */
300 } else if (!strcmp(atype, "slider"))
302 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
303 ePyObject psize = PyTuple_GET_ITEM(value, 2);
305 /* convert value to Long. fallback to -1 on error. */
306 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
307 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
309 /* calc. slider length */
310 int width = item_right.width() * value / size;
311 int height = item_right.height();
315 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
316 //hack - make it customizable
317 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
319 /* pvalue is borrowed */
320 } else if (!strcmp(atype, "mtext"))
322 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
323 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
324 int xoffs = value_alignment_left ? 0 : m_seperation;
325 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
327 para->renderString(text, 0);
328 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
329 int glyphs = para->size();
333 if (PyTuple_Size(value) >= 3)
334 plist = PyTuple_GET_ITEM(value, 2);
338 if (plist && PyList_Check(plist))
339 entries = PyList_Size(plist);
341 for (int i = 0; i < entries; ++i)
343 ePyObject entry = PyList_GET_ITEM(plist, i);
344 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
346 if ((num < 0) || (num >= glyphs))
347 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
350 para->setGlyphFlag(num, GS_INVERT);
352 bbox = para->getGlyphBBox(num);
353 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
356 /* entry is borrowed */
359 painter.renderPara(para, ePoint(0, 0));
360 /* pvalue is borrowed */
361 /* plist is 0 or borrowed */
364 /* type is borrowed */
366 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
367 /* value is borrowed */
371 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
377 int eListboxPythonConfigContent::currentCursorSelectable()
379 return eListboxPythonStringContent::currentCursorSelectable();
382 //////////////////////////////////////
384 /* todo: make a real infrastructure here! */
385 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
387 eListboxPythonMultiContent::eListboxPythonMultiContent()
391 eListboxPythonMultiContent::~eListboxPythonMultiContent()
394 Py_DECREF(m_buildFunc);
397 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
399 eRect itemrect(offset, m_itemsize);
400 painter.clip(itemrect);
401 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
406 if (m_list && cursorValid())
408 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
412 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
414 if (PyTuple_Check(items))
415 items = PyObject_CallObject(m_buildFunc, items);
417 eDebug("items is no tuple");
420 eDebug("buildfunc is not callable");
425 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
429 if (!PyList_Check(items))
431 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
435 int size = PyList_Size(items);
436 for (int i = 1; i < size; ++i)
438 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
442 eDebug("eListboxPythonMultiContent: ?");
446 ePyObject px, py, pwidth, pheight, pfnt, pstring, pflags, pcolor;
449 we have a list of tuples:
451 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
454 (1, x, y, width, height, filled_percent )
458 (2, x, y, width, height, pixmap )
462 if (!PyTuple_Check(item))
464 eDebug("eListboxPythonMultiContent did not receive a tuple.");
468 int size = PyTuple_Size(item);
472 eDebug("eListboxPythonMultiContent receive empty tuple.");
476 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
480 px = PyTuple_GET_ITEM(item, 1);
481 py = PyTuple_GET_ITEM(item, 2);
482 pwidth = PyTuple_GET_ITEM(item, 3);
483 pheight = PyTuple_GET_ITEM(item, 4);
484 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
487 pflags = PyTuple_GET_ITEM(item, 6);
488 pstring = PyTuple_GET_ITEM(item, 7);
491 pcolor = PyTuple_GET_ITEM(item, 8);
496 case TYPE_TEXT: // text
498 if (!(px && py && pwidth && pheight && pfnt && pstring))
500 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
504 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
505 int x = PyInt_AsLong(px);
506 int y = PyInt_AsLong(py);
507 int width = PyInt_AsLong(pwidth);
508 int height = PyInt_AsLong(pheight);
509 int flags = PyInt_AsLong(pflags);
510 int fnt = PyInt_AsLong(pfnt);
514 int color = PyInt_AsLong(pcolor);
515 painter.setForegroundColor(gRGB(color));
518 if (m_font.find(fnt) == m_font.end())
520 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
524 eRect r = eRect(x, y, width, height);
528 painter.setFont(m_font[fnt]);
531 painter.renderText(r, string, flags);
535 case TYPE_PROGRESS: // Progress
537 if (!(px && py && pwidth && pheight && pfnt))
539 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
542 int x = PyInt_AsLong(px);
543 int y = PyInt_AsLong(py);
544 int width = PyInt_AsLong(pwidth);
545 int height = PyInt_AsLong(pheight);
546 int filled = PyInt_AsLong(pfnt);
548 eRect r = eRect(x, y, width, height);
553 int bwidth=2; // borderwidth hardcoded yet
556 eRect rc = eRect(x, y, width, bwidth);
560 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
564 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
568 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
573 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
581 case TYPE_PIXMAP_ALPHATEST:
582 case TYPE_PIXMAP: // pixmap
584 if (!(px && py && pwidth && pheight && pfnt))
586 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
589 int x = PyInt_AsLong(px);
590 int y = PyInt_AsLong(py);
591 int width = PyInt_AsLong(pwidth);
592 int height = PyInt_AsLong(pheight);
593 ePtr<gPixmap> pixmap;
594 if (SwigFromPython(pixmap, pfnt))
596 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
600 eRect r = eRect(x, y, width, height);
605 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
611 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
616 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
621 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
624 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
630 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
633 Py_DECREF(m_buildFunc);
636 Py_INCREF(m_buildFunc);
639 int eListboxPythonMultiContent::currentCursorSelectable()
641 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
642 if (m_list && cursorValid())
644 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
645 if (PyList_Check(item))
647 item = PyList_GET_ITEM(item, 0);
651 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
652 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
658 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
666 void eListboxPythonMultiContent::setItemHeight(int height)
668 m_itemheight = height;
670 m_listbox->setItemHeight(height);