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, pfillColor, 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 pfillColor = PyTuple_GET_ITEM(item, 9);
535 if (pfillColor == Py_None)
536 pfillColor=ePyObject();
539 pborderWidth = PyTuple_GET_ITEM(item, 10);
541 pborderColor = PyTuple_GET_ITEM(item, 11);
546 case TYPE_TEXT: // text
548 if (!(px && py && pwidth && pheight && pfnt && pstring))
550 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
554 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
555 int x = PyInt_AsLong(px);
556 int y = PyInt_AsLong(py);
557 int width = PyInt_AsLong(pwidth);
558 int height = PyInt_AsLong(pheight);
559 int flags = PyInt_AsLong(pflags);
560 int fnt = PyInt_AsLong(pfnt);
561 int bwidth = pborderWidth ? PyInt_AsLong(pborderWidth) : 0;
565 int color = PyInt_AsLong(pcolor);
566 painter.setForegroundColor(gRGB(color));
569 if (m_font.find(fnt) == m_font.end())
571 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
575 eRect rc = eRect(x+bwidth, y+bwidth, width-bwidth*2, height-bwidth*2);
580 if (pfillColor && !selected)
582 int color = PyInt_AsLong(pfillColor);
583 painter.setBackgroundColor(gRGB(color));
587 painter.setFont(m_font[fnt]);
588 painter.renderText(rc, string, flags);
594 rc.setRect(x, y, width, height);
600 int color = PyInt_AsLong(pborderColor);
601 painter.setForegroundColor(gRGB(color));
604 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
606 rc.setRect(x, y, width, bwidth);
610 rc.setRect(x, y+bwidth, bwidth, height-bwidth);
614 rc.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
618 rc.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
627 case TYPE_PROGRESS: // Progress
629 if (!(px && py && pwidth && pheight && pfnt))
631 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
634 int x = PyInt_AsLong(px);
635 int y = PyInt_AsLong(py);
636 int width = PyInt_AsLong(pwidth);
637 int height = PyInt_AsLong(pheight);
638 int filled = PyInt_AsLong(pfnt);
640 eRect rc = eRect(x, y, width, height);
645 int bwidth=2; // borderwidth hardcoded yet
648 rc.setRect(x, y, width, bwidth);
652 rc.setRect(x, y+bwidth, bwidth, height-bwidth);
656 rc.setRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
660 rc.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
665 rc.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
673 case TYPE_PIXMAP_ALPHATEST:
674 case TYPE_PIXMAP: // pixmap
676 if (!(px && py && pwidth && pheight && pfnt))
678 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
681 int x = PyInt_AsLong(px);
682 int y = PyInt_AsLong(py);
683 int width = PyInt_AsLong(pwidth);
684 int height = PyInt_AsLong(pheight);
685 ePtr<gPixmap> pixmap;
686 if (SwigFromPython(pixmap, pfnt))
688 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
692 eRect r = eRect(x, y, width, height);
697 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
703 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
707 if (pcolor || pborderColor || pfillColor)
708 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
713 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
716 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
722 void eListboxPythonMultiContent::setBuildFunc(ePyObject cb)
725 Py_DECREF(m_buildFunc);
728 Py_INCREF(m_buildFunc);
731 int eListboxPythonMultiContent::currentCursorSelectable()
733 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
734 if (m_list && cursorValid())
736 ePyObject item = PyList_GET_ITEM(m_list, m_cursor);
737 if (PyList_Check(item))
739 item = PyList_GET_ITEM(item, 0);
743 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
744 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
750 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)
758 void eListboxPythonMultiContent::setItemHeight(int height)
760 m_itemheight = height;
762 m_listbox->setItemHeight(height);