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)
42 int iListboxContent::currentCursorSelectable()
47 //////////////////////////////////////
49 DEFINE_REF(eListboxPythonStringContent);
51 eListboxPythonStringContent::eListboxPythonStringContent()
55 eListboxPythonStringContent::~eListboxPythonStringContent()
60 void eListboxPythonStringContent::cursorHome()
65 void eListboxPythonStringContent::cursorEnd()
70 int eListboxPythonStringContent::cursorMove(int count)
76 else if (m_cursor > size())
81 int eListboxPythonStringContent::cursorValid()
83 return m_cursor < size();
86 int eListboxPythonStringContent::cursorSet(int n)
92 else if (m_cursor > size())
97 int eListboxPythonStringContent::cursorGet()
102 int eListboxPythonStringContent::currentCursorSelectable()
104 if (m_list && cursorValid())
106 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
107 if (!PyTuple_Check(item))
109 if (PyTuple_Size(item) >= 2)
115 void eListboxPythonStringContent::cursorSave()
117 m_saved_cursor = m_cursor;
120 void eListboxPythonStringContent::cursorRestore()
122 m_cursor = m_saved_cursor;
125 int eListboxPythonStringContent::size()
129 return PyList_Size(m_list);
132 void eListboxPythonStringContent::setSize(const eSize &size)
137 void eListboxPythonStringContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
139 ePtr<gFont> fnt = new gFont("Regular", 20);
140 painter.clip(eRect(offset, m_itemsize));
141 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
144 if (m_list && cursorValid())
147 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
148 painter.setFont(fnt);
150 /* the user can supply tuples, in this case the first one will be displayed. */
151 if (PyTuple_Check(item))
153 if (PyTuple_Size(item) == 1)
155 item = PyTuple_GET_ITEM(item, 0);
160 int half_height = m_itemsize.height() / 2;
162 painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
165 const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
166 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
168 painter.setForegroundColor(gRGB(0x808080));
169 painter.renderText(eRect(text_offset, m_itemsize), string);
173 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
179 void eListboxPythonStringContent::setList(ePyObject list)
182 if (!PyList_Check(list))
184 m_list = ePyObject();
192 m_listbox->entryReset(false);
195 PyObject *eListboxPythonStringContent::getCurrentSelection()
197 if (!(m_list && cursorValid()))
202 ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
207 void eListboxPythonStringContent::invalidateEntry(int index)
210 m_listbox->entryChanged(index);
213 void eListboxPythonStringContent::invalidate()
219 m_listbox->moveSelectionTo(s?s-1:0);
220 m_listbox->invalidate();
224 //////////////////////////////////////
226 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
228 ePtr<gFont> fnt = new gFont("Regular", 20);
229 ePtr<gFont> fnt2 = new gFont("Regular", 16);
230 eRect itemrect(offset, m_itemsize);
231 painter.clip(itemrect);
232 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
235 if (m_list && cursorValid())
237 /* get current list item */
238 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
239 ePyObject text, value;
240 painter.setFont(fnt);
242 /* the first tuple element is a string for the left side.
243 the second one will be called, and the result shall be an tuple.
246 the first one is the type (string).
247 the second one is the value. */
248 if (PyTuple_Check(item))
250 /* handle left part. get item from tuple, convert to string, display. */
252 text = PyTuple_GET_ITEM(item, 0);
253 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
254 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
255 eSize item_left = eSize(m_seperation, m_itemsize.height());
256 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
257 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
260 /* when we have no label, align value to the left. (FIXME:
261 don't we want to specifiy this individually?) */
262 int value_alignment_left = !*string;
264 /* now, handle the value. get 2nd part from tuple*/
265 value = PyTuple_GET_ITEM(item, 1);
268 ePyObject args = PyTuple_New(1);
269 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
271 /* CallObject will call __call__ which should return the value tuple */
272 value = PyObject_CallObject(value, args);
274 if (PyErr_Occurred())
278 /* the PyInt was stolen. */
281 /* check if this is really a tuple */
282 if (value && PyTuple_Check(value))
284 /* convert type to string */
285 ePyObject type = PyTuple_GET_ITEM(value, 0);
286 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
290 if (!strcmp(atype, "text"))
292 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
293 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
294 painter.setFont(fnt2);
295 if (value_alignment_left)
296 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
298 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
300 /* pvalue is borrowed */
301 } else if (!strcmp(atype, "slider"))
303 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
304 ePyObject psize = PyTuple_GET_ITEM(value, 2);
306 /* convert value to Long. fallback to -1 on error. */
307 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
308 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
310 /* calc. slider length */
311 int width = item_right.width() * value / size;
312 int height = item_right.height();
316 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
317 //hack - make it customizable
318 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
320 /* pvalue is borrowed */
321 } else if (!strcmp(atype, "mtext"))
323 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
324 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
325 int xoffs = value_alignment_left ? 0 : m_seperation;
326 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
328 para->renderString(text, 0);
329 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
330 int glyphs = para->size();
334 if (PyTuple_Size(value) >= 3)
335 plist = PyTuple_GET_ITEM(value, 2);
339 if (plist && PyList_Check(plist))
340 entries = PyList_Size(plist);
342 for (int i = 0; i < entries; ++i)
344 ePyObject entry = PyList_GET_ITEM(plist, i);
345 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
347 if ((num < 0) || (num >= glyphs))
348 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
351 para->setGlyphFlag(num, GS_INVERT);
353 bbox = para->getGlyphBBox(num);
354 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
357 /* entry is borrowed */
360 painter.renderPara(para, ePoint(0, 0));
361 /* pvalue is borrowed */
362 /* plist is 0 or borrowed */
365 /* type is borrowed */
367 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
368 /* value is borrowed */
372 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
378 int eListboxPythonConfigContent::currentCursorSelectable()
380 return eListboxPythonStringContent::currentCursorSelectable();
383 //////////////////////////////////////
385 /* todo: make a real infrastructure here! */
386 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
388 eListboxPythonMultiContent::eListboxPythonMultiContent()
392 eListboxPythonMultiContent::~eListboxPythonMultiContent()
395 Py_DECREF(m_buildFunc);
398 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
400 eRect itemrect(offset, m_itemsize);
401 painter.clip(itemrect);
402 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
407 if (m_list && cursorValid())
409 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
413 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
415 if (PyTuple_Check(items))
416 items = PyObject_CallObject(m_buildFunc, items);
418 eDebug("items is no tuple");
421 eDebug("buildfunc is not callable");
426 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
430 if (!PyList_Check(items))
432 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
436 int size = PyList_Size(items);
437 for (int i = 1; i < size; ++i)
439 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
443 eDebug("eListboxPythonMultiContent: ?");
447 ePyObject px, py, pwidth, pheight, pfnt, pstring, pflags, pcolor;
450 we have a list of tuples:
452 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
455 (1, x, y, width, height, filled_percent )
459 (2, x, y, width, height, pixmap )
463 if (!PyTuple_Check(item))
465 eDebug("eListboxPythonMultiContent did not receive a tuple.");
469 int size = PyTuple_Size(item);
473 eDebug("eListboxPythonMultiContent receive empty tuple.");
477 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
481 px = PyTuple_GET_ITEM(item, 1);
482 py = PyTuple_GET_ITEM(item, 2);
483 pwidth = PyTuple_GET_ITEM(item, 3);
484 pheight = PyTuple_GET_ITEM(item, 4);
485 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
488 pflags = PyTuple_GET_ITEM(item, 6);
489 pstring = PyTuple_GET_ITEM(item, 7);
492 pcolor = PyTuple_GET_ITEM(item, 8);
497 case TYPE_TEXT: // text
499 if (!(px && py && pwidth && pheight && pfnt && pstring))
501 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
505 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
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 int fnt = PyInt_AsLong(pfnt);
515 int color = PyInt_AsLong(pcolor);
516 painter.setForegroundColor(gRGB(color));
519 if (m_font.find(fnt) == m_font.end())
521 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
525 eRect r = eRect(x, y, width, height);
529 painter.setFont(m_font[fnt]);
532 painter.renderText(r, string, flags);
536 case TYPE_PROGRESS: // Progress
538 if (!(px && py && pwidth && pheight && pfnt))
540 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
543 int x = PyInt_AsLong(px);
544 int y = PyInt_AsLong(py);
545 int width = PyInt_AsLong(pwidth);
546 int height = PyInt_AsLong(pheight);
547 int filled = PyInt_AsLong(pfnt);
549 eRect r = eRect(x, y, width, height);
554 int bwidth=2; // borderwidth hardcoded yet
557 eRect rc = eRect(x, y, width, bwidth);
561 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
565 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
569 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
574 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
582 case TYPE_PIXMAP_ALPHATEST:
583 case TYPE_PIXMAP: // pixmap
585 if (!(px && py && pwidth && pheight && pfnt))
587 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
590 int x = PyInt_AsLong(px);
591 int y = PyInt_AsLong(py);
592 int width = PyInt_AsLong(pwidth);
593 int height = PyInt_AsLong(pheight);
594 ePtr<gPixmap> pixmap;
595 if (SwigFromPython(pixmap, pfnt))
597 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
601 eRect r = eRect(x, y, width, height);
606 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
612 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
617 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
622 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
625 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
631 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
634 Py_DECREF(m_buildFunc);
637 Py_INCREF(m_buildFunc);
640 int eListboxPythonMultiContent::currentCursorSelectable()
642 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
643 if (m_list && cursorValid())
645 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
646 if (PyList_Check(item))
648 item = PyList_GET_ITEM(item, 0);
652 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
653 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
659 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)