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()))
200 ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
205 void eListboxPythonStringContent::invalidateEntry(int index)
208 m_listbox->entryChanged(index);
211 void eListboxPythonStringContent::invalidate()
217 m_listbox->moveSelectionTo(s?s-1:0);
218 m_listbox->invalidate();
222 //////////////////////////////////////
224 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
226 ePtr<gFont> fnt = new gFont("Regular", 20);
227 ePtr<gFont> fnt2 = new gFont("Regular", 16);
228 eRect itemrect(offset, m_itemsize);
229 painter.clip(itemrect);
230 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
233 if (m_list && cursorValid())
235 /* get current list item */
236 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
237 ePyObject text, value;
238 painter.setFont(fnt);
240 /* the first tuple element is a string for the left side.
241 the second one will be called, and the result shall be an tuple.
244 the first one is the type (string).
245 the second one is the value. */
246 if (PyTuple_Check(item))
248 /* handle left part. get item from tuple, convert to string, display. */
250 text = PyTuple_GET_ITEM(item, 0);
251 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
252 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
253 eSize item_left = eSize(m_seperation, m_itemsize.height());
254 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
255 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
258 /* when we have no label, align value to the left. (FIXME:
259 don't we want to specifiy this individually?) */
260 int value_alignment_left = !*string;
262 /* now, handle the value. get 2nd part from tuple*/
263 value = PyTuple_GET_ITEM(item, 1);
266 ePyObject args = PyTuple_New(1);
267 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
269 /* CallObject will call __call__ which should return the value tuple */
270 value = PyObject_CallObject(value, args);
272 if (PyErr_Occurred())
276 /* the PyInt was stolen. */
279 /* check if this is really a tuple */
280 if (value && PyTuple_Check(value))
282 /* convert type to string */
283 ePyObject type = PyTuple_GET_ITEM(value, 0);
284 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
288 if (!strcmp(atype, "text"))
290 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
291 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
292 painter.setFont(fnt2);
293 if (value_alignment_left)
294 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
296 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
298 /* pvalue is borrowed */
299 } else if (!strcmp(atype, "slider"))
301 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
302 ePyObject psize = PyTuple_GET_ITEM(value, 2);
304 /* convert value to Long. fallback to -1 on error. */
305 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
306 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
308 /* calc. slider length */
309 int width = item_right.width() * value / size;
310 int height = item_right.height();
314 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
315 //hack - make it customizable
316 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
318 /* pvalue is borrowed */
319 } else if (!strcmp(atype, "mtext"))
321 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
322 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
323 int xoffs = value_alignment_left ? 0 : m_seperation;
324 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
326 para->renderString(text, 0);
327 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
328 int glyphs = para->size();
332 if (PyTuple_Size(value) >= 3)
333 plist = PyTuple_GET_ITEM(value, 2);
337 if (plist && PyList_Check(plist))
338 entries = PyList_Size(plist);
340 for (int i = 0; i < entries; ++i)
342 ePyObject entry = PyList_GET_ITEM(plist, i);
343 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
345 if ((num < 0) || (num >= glyphs))
346 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
349 para->setGlyphFlag(num, GS_INVERT);
351 bbox = para->getGlyphBBox(num);
352 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
355 /* entry is borrowed */
358 painter.renderPara(para, ePoint(0, 0));
359 /* pvalue is borrowed */
360 /* plist is 0 or borrowed */
363 /* type is borrowed */
365 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
366 /* value is borrowed */
370 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
376 int eListboxPythonConfigContent::currentCursorSelectable()
378 return eListboxPythonStringContent::currentCursorSelectable();
381 //////////////////////////////////////
383 /* todo: make a real infrastructure here! */
384 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
386 eListboxPythonMultiContent::eListboxPythonMultiContent()
390 eListboxPythonMultiContent::~eListboxPythonMultiContent()
393 Py_DECREF(m_buildFunc);
396 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
398 eRect itemrect(offset, m_itemsize);
399 painter.clip(itemrect);
400 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
405 if (m_list && cursorValid())
407 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
411 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
413 if (PyTuple_Check(items))
414 items = PyObject_CallObject(m_buildFunc, items);
416 eDebug("items is no tuple");
419 eDebug("buildfunc is not callable");
424 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
428 if (!PyList_Check(items))
430 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
434 int size = PyList_Size(items);
435 for (int i = 1; i < size; ++i)
437 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
441 eDebug("eListboxPythonMultiContent: ?");
445 ePyObject px, py, pwidth, pheight, pfnt, pstring, pflags, pcolor;
448 we have a list of tuples:
450 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
453 (1, x, y, width, height, filled_percent )
457 (2, x, y, width, height, pixmap )
461 if (!PyTuple_Check(item))
463 eDebug("eListboxPythonMultiContent did not receive a tuple.");
467 int size = PyTuple_Size(item);
471 eDebug("eListboxPythonMultiContent receive empty tuple.");
475 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
479 px = PyTuple_GET_ITEM(item, 1);
480 py = PyTuple_GET_ITEM(item, 2);
481 pwidth = PyTuple_GET_ITEM(item, 3);
482 pheight = PyTuple_GET_ITEM(item, 4);
483 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
486 pflags = PyTuple_GET_ITEM(item, 6);
487 pstring = PyTuple_GET_ITEM(item, 7);
490 pcolor = PyTuple_GET_ITEM(item, 8);
495 case TYPE_TEXT: // text
497 if (!(px && py && pwidth && pheight && pfnt && pstring))
499 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
503 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
504 int x = PyInt_AsLong(px);
505 int y = PyInt_AsLong(py);
506 int width = PyInt_AsLong(pwidth);
507 int height = PyInt_AsLong(pheight);
508 int flags = PyInt_AsLong(pflags);
509 int fnt = PyInt_AsLong(pfnt);
513 int color = PyInt_AsLong(pcolor);
514 painter.setForegroundColor(gRGB(color));
517 if (m_font.find(fnt) == m_font.end())
519 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
523 eRect r = eRect(x, y, width, height);
527 painter.setFont(m_font[fnt]);
530 painter.renderText(r, string, flags);
534 case TYPE_PROGRESS: // Progress
536 if (!(px && py && pwidth && pheight && pfnt))
538 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
541 int x = PyInt_AsLong(px);
542 int y = PyInt_AsLong(py);
543 int width = PyInt_AsLong(pwidth);
544 int height = PyInt_AsLong(pheight);
545 int filled = PyInt_AsLong(pfnt);
547 eRect r = eRect(x, y, width, height);
552 int bwidth=2; // borderwidth hardcoded yet
555 eRect rc = eRect(x, y, width, bwidth);
559 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
563 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
567 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
572 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
580 case TYPE_PIXMAP_ALPHATEST:
581 case TYPE_PIXMAP: // pixmap
583 if (!(px && py && pwidth && pheight && pfnt))
585 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
588 int x = PyInt_AsLong(px);
589 int y = PyInt_AsLong(py);
590 int width = PyInt_AsLong(pwidth);
591 int height = PyInt_AsLong(pheight);
592 ePtr<gPixmap> pixmap;
593 if (SwigFromPython(pixmap, pfnt))
595 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
599 eRect r = eRect(x, y, width, height);
604 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
610 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
615 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
620 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
623 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
629 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
632 Py_DECREF(m_buildFunc);
635 Py_INCREF(m_buildFunc);
638 int eListboxPythonMultiContent::currentCursorSelectable()
640 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
641 if (m_list && cursorValid())
643 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
644 if (PyList_Check(item))
646 item = PyList_GET_ITEM(item, 0);
650 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
651 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
657 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)