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);
144 eListboxStyle *local_style = 0;
146 /* get local listbox style, if present */
148 local_style = m_listbox->getLocalStyle();
150 /* if we have a local background color set, use that. */
151 if (local_style && local_style->m_background_color_set)
152 painter.setBackgroundColor(local_style->m_background_color);
154 /* same for foreground */
155 if (local_style && local_style->m_foreground_color_set)
156 painter.setBackgroundColor(local_style->m_foreground_color);
158 /* if we have no transparent background */
159 if (!local_style || !local_style->m_transparent_background)
161 /* blit background picture, if available (otherwise, clear only) */
162 if (local_style && local_style->m_background)
163 painter.blit(local_style->m_background, offset, eRect(), 0);
168 if (local_style && local_style->m_background)
169 painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
172 if (m_list && cursorValid())
175 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
176 painter.setFont(fnt);
178 /* the user can supply tuples, in this case the first one will be displayed. */
179 if (PyTuple_Check(item))
181 if (PyTuple_Size(item) == 1)
183 item = PyTuple_GET_ITEM(item, 0);
186 if (selected && local_style && local_style->m_selection)
187 painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
192 int half_height = m_itemsize.height() / 2;
193 painter.fill(eRect(offset.x() + half_height, offset.y() + half_height - 2, m_itemsize.width() - m_itemsize.height(), 4));
196 const char *string = PyString_Check(item) ? PyString_AsString(item) : "<not-a-string>";
197 ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1));
199 painter.setForegroundColor(gRGB(0x808080));
200 painter.renderText(eRect(text_offset, m_itemsize), string);
203 if (selected && (!local_style || !local_style->m_selection))
204 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
210 void eListboxPythonStringContent::setList(ePyObject list)
213 if (!PyList_Check(list))
215 m_list = ePyObject();
223 m_listbox->entryReset(false);
226 PyObject *eListboxPythonStringContent::getCurrentSelection()
228 if (!(m_list && cursorValid()))
231 ePyObject r = PyList_GET_ITEM(m_list, m_cursor);
236 void eListboxPythonStringContent::invalidateEntry(int index)
239 m_listbox->entryChanged(index);
242 void eListboxPythonStringContent::invalidate()
248 m_listbox->moveSelectionTo(s?s-1:0);
249 m_listbox->invalidate();
253 //////////////////////////////////////
255 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
257 ePtr<gFont> fnt = new gFont("Regular", 20);
258 ePtr<gFont> fnt2 = new gFont("Regular", 16);
259 eRect itemrect(offset, m_itemsize);
260 painter.clip(itemrect);
261 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
264 if (m_list && cursorValid())
266 /* get current list item */
267 ePyObject item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
268 ePyObject text, value;
269 painter.setFont(fnt);
271 /* the first tuple element is a string for the left side.
272 the second one will be called, and the result shall be an tuple.
275 the first one is the type (string).
276 the second one is the value. */
277 if (PyTuple_Check(item))
279 /* handle left part. get item from tuple, convert to string, display. */
281 text = PyTuple_GET_ITEM(item, 0);
282 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
283 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
284 eSize item_left = eSize(m_seperation, m_itemsize.height());
285 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
286 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
289 /* when we have no label, align value to the left. (FIXME:
290 don't we want to specifiy this individually?) */
291 int value_alignment_left = !*string;
293 /* now, handle the value. get 2nd part from tuple*/
294 value = PyTuple_GET_ITEM(item, 1);
297 ePyObject args = PyTuple_New(1);
298 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
300 /* CallObject will call __call__ which should return the value tuple */
301 value = PyObject_CallObject(value, args);
303 if (PyErr_Occurred())
307 /* the PyInt was stolen. */
310 /* check if this is really a tuple */
311 if (value && PyTuple_Check(value))
313 /* convert type to string */
314 ePyObject type = PyTuple_GET_ITEM(value, 0);
315 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
319 if (!strcmp(atype, "text"))
321 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
322 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
323 painter.setFont(fnt2);
324 if (value_alignment_left)
325 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
327 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
329 /* pvalue is borrowed */
330 } else if (!strcmp(atype, "slider"))
332 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
333 ePyObject psize = PyTuple_GET_ITEM(value, 2);
335 /* convert value to Long. fallback to -1 on error. */
336 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
337 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
339 /* calc. slider length */
340 int width = item_right.width() * value / size;
341 int height = item_right.height();
345 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
346 //hack - make it customizable
347 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
349 /* pvalue is borrowed */
350 } else if (!strcmp(atype, "mtext"))
352 ePyObject pvalue = PyTuple_GET_ITEM(value, 1);
353 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
354 int xoffs = value_alignment_left ? 0 : m_seperation;
355 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
357 para->renderString(text, 0);
358 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
359 int glyphs = para->size();
363 if (PyTuple_Size(value) >= 3)
364 plist = PyTuple_GET_ITEM(value, 2);
368 if (plist && PyList_Check(plist))
369 entries = PyList_Size(plist);
371 for (int i = 0; i < entries; ++i)
373 ePyObject entry = PyList_GET_ITEM(plist, i);
374 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
376 if ((num < 0) || (num >= glyphs))
377 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
380 para->setGlyphFlag(num, GS_INVERT);
382 bbox = para->getGlyphBBox(num);
383 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
386 /* entry is borrowed */
389 painter.renderPara(para, ePoint(0, 0));
390 /* pvalue is borrowed */
391 /* plist is 0 or borrowed */
394 /* type is borrowed */
396 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
397 /* value is borrowed */
401 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
407 int eListboxPythonConfigContent::currentCursorSelectable()
409 return eListboxPythonStringContent::currentCursorSelectable();
412 //////////////////////////////////////
414 /* todo: make a real infrastructure here! */
415 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
417 eListboxPythonMultiContent::eListboxPythonMultiContent()
418 :m_temp_clip(gRegion::invalidRegion())
422 eListboxPythonMultiContent::~eListboxPythonMultiContent()
424 Py_XDECREF(m_buildFunc);
425 Py_XDECREF(m_selectableFunc);
428 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
430 if (update && m_selection_clip.valid())
432 m_temp_clip = m_selection_clip;
434 m_selection_clip = rect;
436 m_listbox->entryChanged(m_cursor);
439 m_selection_clip = rect;
442 static void clearRegion(gPainter &painter, eWindowStyle &style, ePyObject pforeColor, ePyObject pbackColor, ePyObject pbackColorSelected, int selected, gRegion &rc, eRect &sel_clip)
444 if (selected && sel_clip.valid())
446 painter.clip(rc-sel_clip);
449 int color = PyInt_AsLong(pbackColor);
450 painter.setBackgroundColor(gRGB(color));
453 style.setStyle(painter, eWindowStyle::styleListboxNormal);
456 painter.clip(rc&sel_clip);
457 style.setStyle(painter, eWindowStyle::styleListboxSelected);
458 if (pbackColorSelected)
460 int color = PyInt_AsLong(pbackColorSelected);
461 painter.setBackgroundColor(gRGB(color));
470 style.setStyle(painter, eWindowStyle::styleListboxSelected);
471 if (pbackColorSelected)
473 int color = PyInt_AsLong(pbackColorSelected);
474 painter.setBackgroundColor(gRGB(color));
479 style.setStyle(painter, eWindowStyle::styleListboxNormal);
482 int color = PyInt_AsLong(pbackColor);
483 painter.setBackgroundColor(gRGB(color));
490 int color = PyInt_AsLong(pforeColor);
491 painter.setForegroundColor(gRGB(color));
495 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
497 gRegion itemregion(eRect(offset, m_itemsize));
499 eRect sel_clip(m_selection_clip);
500 if (sel_clip.valid())
501 sel_clip.moveBy(offset);
503 if (m_temp_clip.valid())
505 m_temp_clip.moveBy(offset);
506 itemregion &= m_temp_clip;
507 m_temp_clip = eRect();
510 painter.clip(itemregion);
512 clearRegion(painter, style, ePyObject(), ePyObject(), ePyObject(), selected, itemregion, sel_clip);
516 if (m_list && cursorValid())
518 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
522 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
524 if (PyTuple_Check(items))
525 items = PyObject_CallObject(m_buildFunc, items);
527 eDebug("items is no tuple");
530 eDebug("buildfunc is not callable");
535 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
539 if (!PyList_Check(items))
541 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
545 int size = PyList_Size(items);
546 for (int i = 1; i < size; ++i)
548 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
549 bool reset_colors=false;
553 eDebug("eListboxPythonMultiContent: ?");
557 if (!PyTuple_Check(item))
559 eDebug("eListboxPythonMultiContent did not receive a tuple.");
563 int size = PyTuple_Size(item);
567 eDebug("eListboxPythonMultiContent receive empty tuple.");
571 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
575 case TYPE_TEXT: // text
578 (0, x, y, width, height, fnt, flags, "bla" [, color, backColor, backColorSelected, borderWidth, borderColor] )
580 ePyObject px = PyTuple_GET_ITEM(item, 1),
581 py = PyTuple_GET_ITEM(item, 2),
582 pwidth = PyTuple_GET_ITEM(item, 3),
583 pheight = PyTuple_GET_ITEM(item, 4),
584 pfnt = PyTuple_GET_ITEM(item, 5),
585 pflags = PyTuple_GET_ITEM(item, 6),
586 pstring = PyTuple_GET_ITEM(item, 7),
587 pforeColor, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
589 if (!(px && py && pwidth && pheight && pfnt && pflags && pstring))
591 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string [, color, backColor, backColorSelected, borderWidth, borderColor])");
597 pforeColor = PyTuple_GET_ITEM(item, 8);
598 if (pforeColor == Py_None)
599 pforeColor=ePyObject();
603 pbackColor = PyTuple_GET_ITEM(item, 9);
604 if (pbackColor == Py_None)
605 pbackColor=ePyObject();
609 pbackColorSelected = PyTuple_GET_ITEM(item, 10);
610 if (pbackColorSelected == Py_None)
611 pbackColorSelected=ePyObject();
614 pborderWidth = PyTuple_GET_ITEM(item, 11);
616 pborderColor = PyTuple_GET_ITEM(item, 12);
618 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
619 int x = PyInt_AsLong(px) + offset.x();
620 int y = PyInt_AsLong(py) + offset.y();
621 int width = PyInt_AsLong(pwidth);
622 int height = PyInt_AsLong(pheight);
623 int flags = PyInt_AsLong(pflags);
624 int fnt = PyInt_AsLong(pfnt);
625 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
627 if (m_font.find(fnt) == m_font.end())
629 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
633 eRect rect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
635 if (pbackColor || pbackColorSelected || pforeColor)
638 clearRegion(painter, style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
642 painter.setFont(m_font[fnt]);
643 painter.renderText(rect, string, flags);
649 eRect rect(eRect(x, y, width, height));
653 int color = PyInt_AsLong(pborderColor);
654 painter.setForegroundColor(gRGB(color));
656 else if (pforeColor) // reset to normal color
657 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
659 rect.setRect(x, y, width, bwidth);
662 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
665 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
668 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
675 case TYPE_PROGRESS: // Progress
678 (1, x, y, width, height, filled_percent [, borderWidth, foreColor, backColor, backColorSelected] )
680 ePyObject px = PyTuple_GET_ITEM(item, 1),
681 py = PyTuple_GET_ITEM(item, 2),
682 pwidth = PyTuple_GET_ITEM(item, 3),
683 pheight = PyTuple_GET_ITEM(item, 4),
684 pfilled_perc = PyTuple_GET_ITEM(item, 5),
685 pborderWidth, pforeColor, pbackColor, pbackColorSelected;
687 if (!(px && py && pwidth && pheight && pfilled_perc))
689 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))");
694 pborderWidth = PyTuple_GET_ITEM(item, 6);
696 pforeColor = PyTuple_GET_ITEM(item, 7);
699 pbackColor = PyTuple_GET_ITEM(item, 8);
700 if (pbackColor == Py_None)
701 pbackColor=ePyObject();
705 pbackColorSelected = PyTuple_GET_ITEM(item, 9);
706 if (pbackColorSelected == Py_None)
707 pbackColorSelected=ePyObject();
710 int x = PyInt_AsLong(px) + offset.x();
711 int y = PyInt_AsLong(py) + offset.y();
712 int width = PyInt_AsLong(pwidth);
713 int height = PyInt_AsLong(pheight);
714 int filled = PyInt_AsLong(pfilled_perc);
715 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 2;
717 eRect rect(x, y, width, height);
719 if (pbackColor || pbackColorSelected || pforeColor)
722 clearRegion(painter, style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
727 rect.setRect(x, y, width, bwidth);
730 rect.setRect(x, y+bwidth, bwidth, height-bwidth);
733 rect.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
736 rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
740 rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
747 case TYPE_PIXMAP_ALPHATEST:
748 case TYPE_PIXMAP: // pixmap
751 (2, x, y, width, height, pixmap [, foreColor, backColor, backColorSelected] )
754 ePyObject px = PyTuple_GET_ITEM(item, 1),
755 py = PyTuple_GET_ITEM(item, 2),
756 pwidth = PyTuple_GET_ITEM(item, 3),
757 pheight = PyTuple_GET_ITEM(item, 4),
758 ppixmap = PyTuple_GET_ITEM(item, 5),
759 pbackColor, pbackColorSelected;
761 if (!(px && py && pwidth && pheight && ppixmap))
763 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
767 int x = PyInt_AsLong(px) + offset.x();
768 int y = PyInt_AsLong(py) + offset.y();
769 int width = PyInt_AsLong(pwidth);
770 int height = PyInt_AsLong(pheight);
771 ePtr<gPixmap> pixmap;
772 if (SwigFromPython(pixmap, ppixmap))
774 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
780 pbackColor = PyTuple_GET_ITEM(item, 6);
781 if (pbackColor == Py_None)
782 pbackColor=ePyObject();
786 pbackColorSelected = PyTuple_GET_ITEM(item, 7);
787 if (pbackColorSelected == Py_None)
788 pbackColorSelected=ePyObject();
791 eRect rect(x, y, width, height);
793 if (pbackColor || pbackColorSelected)
796 clearRegion(painter, style, ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
800 painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
805 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
809 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
814 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
817 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
823 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
825 Py_XDECREF(m_buildFunc);
827 Py_XINCREF(m_buildFunc);
830 void eListboxPythonMultiContent::setSelectableFunc(ePyObject cb)
832 Py_XDECREF(m_selectableFunc);
834 Py_XINCREF(m_selectableFunc);
837 int eListboxPythonMultiContent::currentCursorSelectable()
839 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
840 if (m_list && cursorValid())
842 if (m_selectableFunc && PyCallable_Check(m_selectableFunc))
844 ePyObject args = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
845 if (PyTuple_Check(args))
847 ePyObject ret = PyObject_CallObject(m_selectableFunc, args);
849 return ret == Py_True;
850 eDebug("call m_selectableFunc failed!!! assume not callable");
853 eDebug("m_list[m_cursor] is not a tuple!!! assume not callable");
857 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
858 if (PyList_Check(item))
860 item = PyList_GET_ITEM(item, 0);
864 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
871 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
879 void eListboxPythonMultiContent::setItemHeight(int height)
881 m_itemheight = height;
883 m_listbox->setItemHeight(height);