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()
388 :m_temp_clip(gRegion::invalidRegion())
392 eListboxPythonMultiContent::~eListboxPythonMultiContent()
394 Py_XDECREF(m_buildFunc);
395 Py_XDECREF(m_callableFunc);
398 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
400 if (update && m_selection_clip.valid())
402 m_temp_clip = m_selection_clip;
404 m_selection_clip = rect;
406 m_listbox->entryChanged(m_cursor);
409 m_selection_clip = rect;
412 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
414 gRegion itemregion(eRect(offset, m_itemsize));
416 eRect sel_clip(m_selection_clip);
417 if (sel_clip.valid())
418 sel_clip.moveBy(offset);
420 if (m_temp_clip.valid())
422 m_temp_clip.moveBy(offset);
423 itemregion &= m_temp_clip;
424 m_temp_clip = eRect();
427 painter.clip(itemregion);
431 if (m_list && cursorValid())
433 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
437 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
439 if (PyTuple_Check(items))
440 items = PyObject_CallObject(m_buildFunc, items);
442 eDebug("items is no tuple");
445 eDebug("buildfunc is not callable");
450 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
454 if (!PyList_Check(items))
456 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
460 int size = PyList_Size(items);
461 for (int i = 1; i < size; ++i)
463 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
467 eDebug("eListboxPythonMultiContent: ?");
471 if (!PyTuple_Check(item))
473 eDebug("eListboxPythonMultiContent did not receive a tuple.");
477 int size = PyTuple_Size(item);
481 eDebug("eListboxPythonMultiContent receive empty tuple.");
485 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
489 case TYPE_TEXT: // text
492 (0, x, y, width, height, fnt, flags, "bla" [, color] )
494 ePyObject px = PyTuple_GET_ITEM(item, 1),
495 py = PyTuple_GET_ITEM(item, 2),
496 pwidth = PyTuple_GET_ITEM(item, 3),
497 pheight = PyTuple_GET_ITEM(item, 4),
498 pfnt = PyTuple_GET_ITEM(item, 5),
499 pflags = PyTuple_GET_ITEM(item, 6),
500 pstring = PyTuple_GET_ITEM(item, 7),
501 pforeColor, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
503 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
505 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
511 pforeColor = PyTuple_GET_ITEM(item, 8);
512 if (pforeColor == Py_None)
513 pforeColor=ePyObject();
517 pbackColor = PyTuple_GET_ITEM(item, 9);
518 if (pbackColor == Py_None)
519 pbackColor=ePyObject();
523 pbackColorSelected = PyTuple_GET_ITEM(item, 10);
524 if (pbackColorSelected == Py_None)
525 pbackColorSelected=ePyObject();
528 pborderWidth = PyTuple_GET_ITEM(item, 11);
530 pborderColor = PyTuple_GET_ITEM(item, 12);
532 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
533 int x = PyInt_AsLong(px) + offset.x();
534 int y = PyInt_AsLong(py) + offset.y();
535 int width = PyInt_AsLong(pwidth);
536 int height = PyInt_AsLong(pheight);
537 int flags = PyInt_AsLong(pflags);
538 int fnt = PyInt_AsLong(pfnt);
539 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
541 if (m_font.find(fnt) == m_font.end())
543 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
547 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
552 if (selected && sel_clip.valid())
554 painter.clip(rc-sel_clip);
557 int color = PyInt_AsLong(pbackColor);
558 painter.setBackgroundColor(gRGB(color));
561 style.setStyle(painter, eWindowStyle::styleListboxNormal);
564 painter.clip(rc&sel_clip);
565 style.setStyle(painter, eWindowStyle::styleListboxSelected);
566 if (pbackColorSelected)
568 int color = PyInt_AsLong(pbackColorSelected);
569 painter.setBackgroundColor(gRGB(color));
578 style.setStyle(painter, eWindowStyle::styleListboxSelected);
579 if (pbackColorSelected)
581 int color = PyInt_AsLong(pbackColorSelected);
582 painter.setBackgroundColor(gRGB(color));
587 style.setStyle(painter, eWindowStyle::styleListboxNormal);
590 int color = PyInt_AsLong(pbackColor);
591 painter.setBackgroundColor(gRGB(color));
599 int color = PyInt_AsLong(pforeColor);
600 painter.setForegroundColor(gRGB(color));
603 painter.setFont(m_font[fnt]);
604 painter.renderText(rect, string, flags);
610 eRect rect(eRect(x, y, width, height));
617 int color = PyInt_AsLong(pborderColor);
618 painter.setForegroundColor(gRGB(color));
620 else if (pforeColor) // reset to normal color
621 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
623 rect.setRect(x, y, width, bwidth);
626 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
629 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
632 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
639 case TYPE_PROGRESS: // Progress
642 (1, x, y, width, height, filled_percent )
644 ePyObject px = PyTuple_GET_ITEM(item, 1),
645 py = PyTuple_GET_ITEM(item, 2),
646 pwidth = PyTuple_GET_ITEM(item, 3),
647 pheight = PyTuple_GET_ITEM(item, 4),
648 pfilled_perc = PyTuple_GET_ITEM(item, 5);
650 if (!(px && py && pwidth && pheight && pfilled_perc))
652 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
656 int x = PyInt_AsLong(px) + offset.x();
657 int y = PyInt_AsLong(py) + offset.y();
658 int width = PyInt_AsLong(pwidth);
659 int height = PyInt_AsLong(pheight);
660 int filled = PyInt_AsLong(pfilled_perc);
662 eRect rect(x, y, width, height);
667 if (selected && sel_clip.valid())
669 painter.clip(rc-sel_clip);
670 style.setStyle(painter, eWindowStyle::styleListboxNormal);
673 painter.clip(rc&sel_clip);
674 style.setStyle(painter, eWindowStyle::styleListboxSelected);
680 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
684 int bwidth=2; // borderwidth hardcoded yet
687 rect.setRect(x, y, width, bwidth);
690 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
693 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
696 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
700 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
707 case TYPE_PIXMAP_ALPHATEST:
708 case TYPE_PIXMAP: // pixmap
711 (2, x, y, width, height, pixmap )
714 ePyObject px = PyTuple_GET_ITEM(item, 1),
715 py = PyTuple_GET_ITEM(item, 2),
716 pwidth = PyTuple_GET_ITEM(item, 3),
717 pheight = PyTuple_GET_ITEM(item, 4),
718 ppixmap = PyTuple_GET_ITEM(item, 5);
720 if (!(px && py && pwidth && pheight && ppixmap))
722 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
726 int x = PyInt_AsLong(px) + offset.x();
727 int y = PyInt_AsLong(py) + offset.y();
728 int width = PyInt_AsLong(pwidth);
729 int height = PyInt_AsLong(pheight);
730 ePtr<gPixmap> pixmap;
731 if (SwigFromPython(pixmap, ppixmap))
733 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
737 eRect rect(x, y, width, height);
743 if (selected && sel_clip.valid())
745 painter.clip(rc-sel_clip);
746 style.setStyle(painter, eWindowStyle::styleListboxNormal);
749 painter.clip(rc&sel_clip);
750 style.setStyle(painter, eWindowStyle::styleListboxSelected);
756 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
760 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
766 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
772 if (selected && sel_clip.valid())
774 painter.clip(itemregion-sel_clip);
775 style.setStyle(painter, eWindowStyle::styleListboxNormal);
778 itemregion &= sel_clip;
779 painter.clip(itemregion);
780 style.setStyle(painter, eWindowStyle::styleListboxSelected);
786 painter.clip(itemregion);
787 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
793 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
796 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
802 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
804 Py_XDECREF(m_buildFunc);
806 Py_XINCREF(m_buildFunc);
809 void eListboxPythonMultiContent::setCallableFunc(ePyObject cb)
811 Py_XDECREF(m_callableFunc);
813 Py_XINCREF(m_callableFunc);
816 int eListboxPythonMultiContent::currentCursorSelectable()
818 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
819 if (m_list && cursorValid())
821 if (m_callableFunc && PyCallable_Check(m_callableFunc))
823 ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
824 if (PyTuple_Check(args))
826 ePyObject ret = PyObject_CallObject(m_callableFunc, args);
828 return ret == Py_True;
829 eDebug("call m_callableFunc failed!!! assume not callable");
832 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
836 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
837 if (PyList_Check(item))
839 item = PyList_GET_ITEM(item, 0);
843 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
850 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
858 void eListboxPythonMultiContent::setItemHeight(int height)
860 m_itemheight = height;
862 m_listbox->setItemHeight(height);