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_selectableFunc);
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 static void clearRegion(gPainter &painter, eWindowStyle &style, ePyObject pforeColor, ePyObject pbackColor, ePyObject pbackColorSelected, int selected, gRegion &rc, eRect &sel_clip)
414 if (selected && sel_clip.valid())
416 painter.clip(rc-sel_clip);
419 int color = PyInt_AsLong(pbackColor);
420 painter.setBackgroundColor(gRGB(color));
423 style.setStyle(painter, eWindowStyle::styleListboxNormal);
426 painter.clip(rc&sel_clip);
427 style.setStyle(painter, eWindowStyle::styleListboxSelected);
428 if (pbackColorSelected)
430 int color = PyInt_AsLong(pbackColorSelected);
431 painter.setBackgroundColor(gRGB(color));
440 style.setStyle(painter, eWindowStyle::styleListboxSelected);
441 if (pbackColorSelected)
443 int color = PyInt_AsLong(pbackColorSelected);
444 painter.setBackgroundColor(gRGB(color));
449 style.setStyle(painter, eWindowStyle::styleListboxNormal);
452 int color = PyInt_AsLong(pbackColor);
453 painter.setBackgroundColor(gRGB(color));
460 int color = PyInt_AsLong(pforeColor);
461 painter.setForegroundColor(gRGB(color));
465 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
467 gRegion itemregion(eRect(offset, m_itemsize));
469 eRect sel_clip(m_selection_clip);
470 if (sel_clip.valid())
471 sel_clip.moveBy(offset);
473 if (m_temp_clip.valid())
475 m_temp_clip.moveBy(offset);
476 itemregion &= m_temp_clip;
477 m_temp_clip = eRect();
480 painter.clip(itemregion);
482 clearRegion(painter, style, ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip);
486 if (m_list && cursorValid())
488 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
492 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
494 if (PyTuple_Check(items))
495 items = PyObject_CallObject(m_buildFunc, items);
497 eDebug("items is no tuple");
500 eDebug("buildfunc is not callable");
505 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
509 if (!PyList_Check(items))
511 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
515 int size = PyList_Size(items);
516 for (int i = 1; i < size; ++i)
518 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
519 bool reset_colors=false;
523 eDebug("eListboxPythonMultiContent: ?");
527 if (!PyTuple_Check(item))
529 eDebug("eListboxPythonMultiContent did not receive a tuple.");
533 int size = PyTuple_Size(item);
537 eDebug("eListboxPythonMultiContent receive empty tuple.");
541 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
545 case TYPE_TEXT: // text
548 (0, x, y, width, height, fnt, flags, "bla" [, color, backColor, backColorSelected, borderWidth, borderColor] )
550 ePyObject px = PyTuple_GET_ITEM(item, 1),
551 py = PyTuple_GET_ITEM(item, 2),
552 pwidth = PyTuple_GET_ITEM(item, 3),
553 pheight = PyTuple_GET_ITEM(item, 4),
554 pfnt = PyTuple_GET_ITEM(item, 5),
555 pflags = PyTuple_GET_ITEM(item, 6),
556 pstring = PyTuple_GET_ITEM(item, 7),
557 pforeColor, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
559 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
561 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
567 pforeColor = PyTuple_GET_ITEM(item, 8);
568 if (pforeColor == Py_None)
569 pforeColor=ePyObject();
573 pbackColor = PyTuple_GET_ITEM(item, 9);
574 if (pbackColor == Py_None)
575 pbackColor=ePyObject();
579 pbackColorSelected = PyTuple_GET_ITEM(item, 10);
580 if (pbackColorSelected == Py_None)
581 pbackColorSelected=ePyObject();
584 pborderWidth = PyTuple_GET_ITEM(item, 11);
586 pborderColor = PyTuple_GET_ITEM(item, 12);
588 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
589 int x = PyInt_AsLong(px) + offset.x();
590 int y = PyInt_AsLong(py) + offset.y();
591 int width = PyInt_AsLong(pwidth);
592 int height = PyInt_AsLong(pheight);
593 int flags = PyInt_AsLong(pflags);
594 int fnt = PyInt_AsLong(pfnt);
595 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
597 if (m_font.find(fnt) == m_font.end())
599 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
603 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
605 if (pbackColor || pbackColorSelected || pforeColor)
608 clearRegion(painter, style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
612 painter.setFont(m_font[fnt]);
613 painter.renderText(rect, string, flags);
619 eRect rect(eRect(x, y, width, height));
623 int color = PyInt_AsLong(pborderColor);
624 painter.setForegroundColor(gRGB(color));
626 else if (pforeColor) // reset to normal color
627 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
629 rect.setRect(x, y, width, bwidth);
632 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
635 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
638 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
645 case TYPE_PROGRESS: // Progress
648 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
650 ePyObject px = PyTuple_GET_ITEM(item, 1),
651 py = PyTuple_GET_ITEM(item, 2),
652 pwidth = PyTuple_GET_ITEM(item, 3),
653 pheight = PyTuple_GET_ITEM(item, 4),
654 pfilled_perc = PyTuple_GET_ITEM(item, 5),
655 pborderWidth, pforeColor, pbackColor, pbackColorSelected;
657 if (!(px && py && pwidth && pheight && pfilled_perc))
659 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
664 pborderWidth = PyTuple_GET_ITEM(item, 6);
666 pforeColor = PyTuple_GET_ITEM(item, 7);
669 pbackColor = PyTuple_GET_ITEM(item, 8);
670 if (pbackColor == Py_None)
671 pbackColor=ePyObject();
675 pbackColorSelected = PyTuple_GET_ITEM(item, 9);
676 if (pbackColorSelected == Py_None)
677 pbackColorSelected=ePyObject();
680 int x = PyInt_AsLong(px) + offset.x();
681 int y = PyInt_AsLong(py) + offset.y();
682 int width = PyInt_AsLong(pwidth);
683 int height = PyInt_AsLong(pheight);
684 int filled = PyInt_AsLong(pfilled_perc);
685 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
687 eRect rect(x, y, width, height);
689 if (pbackColor || pbackColorSelected || pforeColor)
692 clearRegion(painter, style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
697 rect.setRect(x, y, width, bwidth);
700 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
703 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
706 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
710 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
717 case TYPE_PIXMAP_ALPHATEST:
718 case TYPE_PIXMAP: // pixmap
721 (2, x, y, width, height, pixmap [, foreColor, backColor, backColorSelected] )
724 ePyObject px = PyTuple_GET_ITEM(item, 1),
725 py = PyTuple_GET_ITEM(item, 2),
726 pwidth = PyTuple_GET_ITEM(item, 3),
727 pheight = PyTuple_GET_ITEM(item, 4),
728 ppixmap = PyTuple_GET_ITEM(item, 5),
729 pbackColor, pbackColorSelected;
731 if (!(px && py && pwidth && pheight && ppixmap))
733 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
737 int x = PyInt_AsLong(px) + offset.x();
738 int y = PyInt_AsLong(py) + offset.y();
739 int width = PyInt_AsLong(pwidth);
740 int height = PyInt_AsLong(pheight);
741 ePtr<gPixmap> pixmap;
742 if (SwigFromPython(pixmap, ppixmap))
744 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
750 pbackColor = PyTuple_GET_ITEM(item, 6);
751 if (pbackColor == Py_None)
752 pbackColor=ePyObject();
756 pbackColorSelected = PyTuple_GET_ITEM(item, 7);
757 if (pbackColorSelected == Py_None)
758 pbackColorSelected=ePyObject();
761 eRect rect(x, y, width, height);
763 if (pbackColor || pbackColorSelected)
766 clearRegion(painter, style, ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
770 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
775 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
779 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
784 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
787 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
793 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
795 Py_XDECREF(m_buildFunc);
797 Py_XINCREF(m_buildFunc);
800 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
802 Py_XDECREF(m_selectableFunc);
804 Py_XINCREF(m_selectableFunc);
807 int eListboxPythonMultiContent::currentCursorSelectable()
809 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
810 if (m_list && cursorValid())
812 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
814 ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
815 if (PyTuple_Check(args))
817 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
819 return ret == Py_True;
820 eDebug("call m_selectableFunc failed!!! assume not callable");
823 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
827 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
828 if (PyList_Check(item))
830 item = PyList_GET_ITEM(item, 0);
834 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
841 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
849 void eListboxPythonMultiContent::setItemHeight(int height)
851 m_itemheight = height;
853 m_listbox->setItemHeight(height);