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()
391 eListboxPythonMultiContent::~eListboxPythonMultiContent()
394 Py_DECREF(m_buildFunc);
397 void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update)
399 if (update && m_selection_clip.valid())
401 // redraw old selection
402 m_temp_clip = m_selection_clip;
403 m_selection_clip = eRect();
405 m_listbox->entryChanged(m_cursor);
406 // redraw new selection
407 m_selection_clip = rect;
410 m_listbox->entryChanged(m_cursor);
411 m_temp_clip = eRect();
414 m_selection_clip = rect;
417 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
419 eRect itemrect(offset, m_itemsize);
420 if (m_temp_clip.valid())
422 eRect tmp = m_temp_clip;
427 painter.clip(itemrect);
428 style.setStyle(painter, selected && !m_selection_clip.valid() ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
430 if (selected && m_selection_clip.valid())
432 eRect tmp = m_selection_clip;
435 style.setStyle(painter, eWindowStyle::styleListboxSelected );
442 if (m_list && cursorValid())
444 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
448 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
450 if (PyTuple_Check(items))
451 items = PyObject_CallObject(m_buildFunc, items);
453 eDebug("items is no tuple");
456 eDebug("buildfunc is not callable");
461 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
465 if (!PyList_Check(items))
467 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
471 int size = PyList_Size(items);
472 for (int i = 1; i < size; ++i)
474 ePyObject item = PyList_GET_ITEM(items, i); // borrowed reference!
478 eDebug("eListboxPythonMultiContent: ?");
482 ePyObject px, py, pwidth, pheight, pfnt, pstring, pflags, pcolor, pbackColor, pbackColorSelected, pborderWidth, pborderColor;
485 we have a list of tuples:
487 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
490 (1, x, y, width, height, filled_percent )
494 (2, x, y, width, height, pixmap )
498 if (!PyTuple_Check(item))
500 eDebug("eListboxPythonMultiContent did not receive a tuple.");
504 int size = PyTuple_Size(item);
508 eDebug("eListboxPythonMultiContent receive empty tuple.");
512 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
516 px = PyTuple_GET_ITEM(item, 1);
517 py = PyTuple_GET_ITEM(item, 2);
518 pwidth = PyTuple_GET_ITEM(item, 3);
519 pheight = PyTuple_GET_ITEM(item, 4);
520 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
523 pflags = PyTuple_GET_ITEM(item, 6);
524 pstring = PyTuple_GET_ITEM(item, 7);
528 pcolor = PyTuple_GET_ITEM(item, 8);
529 if (pcolor == Py_None)
534 pbackColor = PyTuple_GET_ITEM(item, 9);
535 if (pbackColor == Py_None)
536 pbackColor=ePyObject();
540 pbackColorSelected = PyTuple_GET_ITEM(item, 10);
541 if (pbackColorSelected == Py_None)
542 pbackColorSelected=ePyObject();
545 pborderWidth = PyTuple_GET_ITEM(item, 11);
547 pborderColor = PyTuple_GET_ITEM(item, 12);
552 case TYPE_TEXT: // text
554 if (!(px && py && pwidth && pheight && pfnt && pstring))
556 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
560 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
561 int x = PyInt_AsLong(px);
562 int y = PyInt_AsLong(py);
563 int width = PyInt_AsLong(pwidth);
564 int height = PyInt_AsLong(pheight);
565 int flags = PyInt_AsLong(pflags);
566 int fnt = PyInt_AsLong(pfnt);
567 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
571 int color = PyInt_AsLong(pcolor);
572 painter.setForegroundColor(gRGB(color));
575 if (m_font.find(fnt) == m_font.end())
577 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
581 eRect rc = eRect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
586 if (pbackColor && !selected)
588 int color = PyInt_AsLong(pbackColor);
589 painter.setBackgroundColor(gRGB(color));
592 else if (pbackColorSelected && selected)
594 int color = PyInt_AsLong(pbackColorSelected);
595 painter.setBackgroundColor(gRGB(color));
599 painter.setFont(m_font[fnt]);
600 painter.renderText(rc, string, flags);
606 rc.setRect(x, y, width, height);
612 int color = PyInt_AsLong(pborderColor);
613 painter.setForegroundColor(gRGB(color));
615 else if (pcolor) // reset to normal color
616 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
618 rc.setRect(x, y, width, bwidth);
622 rc.setRect(x, y+bwidth, bwidth, height-bwidth);
626 rc.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
630 rc.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
639 case TYPE_PROGRESS: // Progress
641 if (!(px && py && pwidth && pheight && pfnt))
643 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
646 int x = PyInt_AsLong(px);
647 int y = PyInt_AsLong(py);
648 int width = PyInt_AsLong(pwidth);
649 int height = PyInt_AsLong(pheight);
650 int filled = PyInt_AsLong(pfnt);
652 eRect rc = eRect(x, y, width, height);
657 int bwidth=2; // borderwidth hardcoded yet
660 rc.setRect(x, y, width, bwidth);
664 rc.setRect(x, y+bwidth, bwidth, height-bwidth);
668 rc.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
672 rc.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
677 rc.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
685 case TYPE_PIXMAP_ALPHATEST:
686 case TYPE_PIXMAP: // pixmap
688 if (!(px && py && pwidth && pheight && pfnt))
690 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
693 int x = PyInt_AsLong(px);
694 int y = PyInt_AsLong(py);
695 int width = PyInt_AsLong(pwidth);
696 int height = PyInt_AsLong(pheight);
697 ePtr<gPixmap> pixmap;
698 if (SwigFromPython(pixmap, pfnt))
700 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
704 eRect r = eRect(x, y, width, height);
709 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
715 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
719 if (pcolor || pborderColor || pbackColor || pbackColorSelected)
720 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
725 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
728 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
734 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
737 Py_DECREF(m_buildFunc);
740 Py_INCREF(m_buildFunc);
743 int eListboxPythonMultiContent::currentCursorSelectable()
745 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
746 if (m_list && cursorValid())
748 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
749 if (PyList_Check(item))
751 item = PyList_GET_ITEM(item, 0);
755 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
756 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
762 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
770 void eListboxPythonMultiContent::setItemHeight(int height)
772 m_itemheight = height;
774 m_listbox->setItemHeight(height);