1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gdi/font.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)
42 int iListboxContent::currentCursorSelectable()
47 //////////////////////////////////////
49 DEFINE_REF(eListboxPythonStringContent);
51 eListboxPythonStringContent::eListboxPythonStringContent()
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 PyObject *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 PyObject *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(PyObject *list)
183 if (!PyList_Check(list))
193 m_listbox->entryReset(false);
196 PyObject *eListboxPythonStringContent::getCurrentSelection()
198 if (!(m_list && cursorValid()))
203 PyObject *r = PyList_GET_ITEM(m_list, m_cursor);
208 void eListboxPythonStringContent::invalidateEntry(int index)
211 m_listbox->entryChanged(index);
214 void eListboxPythonStringContent::invalidate()
220 m_listbox->moveSelectionTo(s?s-1:0);
221 m_listbox->invalidate();
225 //////////////////////////////////////
227 void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
229 ePtr<gFont> fnt = new gFont("Regular", 20);
230 ePtr<gFont> fnt2 = new gFont("Regular", 16);
231 eRect itemrect(offset, m_itemsize);
232 painter.clip(itemrect);
233 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
236 if (m_list && cursorValid())
238 /* get current list item */
239 PyObject *item = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
240 PyObject *text = 0, *value = 0;
241 painter.setFont(fnt);
243 /* the first tuple element is a string for the left side.
244 the second one will be called, and the result shall be an tuple.
247 the first one is the type (string).
248 the second one is the value. */
249 if (PyTuple_Check(item))
251 /* handle left part. get item from tuple, convert to string, display. */
253 text = PyTuple_GET_ITEM(item, 0);
254 text = PyObject_Str(text); /* creates a new object - old object was borrowed! */
255 const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>";
256 eSize item_left = eSize(m_seperation, m_itemsize.height());
257 eSize item_right = eSize(m_itemsize.width() - m_seperation, m_itemsize.height());
258 painter.renderText(eRect(offset, item_left), string, gPainter::RT_HALIGN_LEFT);
261 /* when we have no label, align value to the left. (FIXME:
262 don't we want to specifiy this individually?) */
263 int value_alignment_left = !*string;
265 /* now, handle the value. get 2nd part from tuple*/
266 value = PyTuple_GET_ITEM(item, 1);
269 PyObject *args = PyTuple_New(1);
270 PyTuple_SET_ITEM(args, 0, PyInt_FromLong(selected));
272 /* CallObject will call __call__ which should return the value tuple */
273 value = PyObject_CallObject(value, args);
275 if (PyErr_Occurred())
279 /* the PyInt was stolen. */
282 /* check if this is really a tuple */
283 if (value && PyTuple_Check(value))
285 /* convert type to string */
286 PyObject *type = PyTuple_GET_ITEM(value, 0);
287 const char *atype = (type && PyString_Check(type)) ? PyString_AsString(type) : 0;
291 if (!strcmp(atype, "text"))
293 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
294 const char *value = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
295 painter.setFont(fnt2);
296 if (value_alignment_left)
297 painter.renderText(eRect(offset, item_right), value, gPainter::RT_HALIGN_LEFT);
299 painter.renderText(eRect(offset + eSize(m_seperation, 0), item_right), value, gPainter::RT_HALIGN_RIGHT);
301 /* pvalue is borrowed */
302 } else if (!strcmp(atype, "slider"))
304 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
305 PyObject *psize = PyTuple_GET_ITEM(value, 2);
307 /* convert value to Long. fallback to -1 on error. */
308 int value = (pvalue && PyInt_Check(pvalue)) ? PyInt_AsLong(pvalue) : -1;
309 int size = (pvalue && PyInt_Check(psize)) ? PyInt_AsLong(psize) : 100;
311 /* calc. slider length */
312 int width = item_right.width() * value / size;
313 int height = item_right.height();
317 //painter.fill(eRect(offset.x() + m_seperation, offset.y(), width, height));
318 //hack - make it customizable
319 painter.fill(eRect(offset.x() + m_seperation, offset.y() + 5, width, height-10));
321 /* pvalue is borrowed */
322 } else if (!strcmp(atype, "mtext"))
324 PyObject *pvalue = PyTuple_GET_ITEM(value, 1);
325 const char *text = (pvalue && PyString_Check(pvalue)) ? PyString_AsString(pvalue) : "<not-a-string>";
326 int xoffs = value_alignment_left ? 0 : m_seperation;
327 ePtr<eTextPara> para = new eTextPara(eRect(offset + eSize(xoffs, 0), item_right));
329 para->renderString(text, 0);
330 para->realign(value_alignment_left ? eTextPara::dirLeft : eTextPara::dirRight);
331 int glyphs = para->size();
335 if (PyTuple_Size(value) >= 3)
336 plist = PyTuple_GET_ITEM(value, 2);
340 if (plist && PyList_Check(plist))
341 entries = PyList_Size(plist);
343 for (int i = 0; i < entries; ++i)
345 PyObject *entry = PyList_GET_ITEM(plist, i);
346 int num = PyInt_Check(entry) ? PyInt_AsLong(entry) : -1;
348 if ((num < 0) || (num >= glyphs))
349 eWarning("glyph index %d in PythonConfigList out of bounds!", num);
352 para->setGlyphFlag(num, GS_INVERT);
354 bbox = para->getGlyphBBox(num);
355 bbox = eRect(bbox.left(), offset.y(), bbox.width(), m_itemsize.height());
358 /* entry is borrowed */
361 painter.renderPara(para, ePoint(0, 0));
362 /* pvalue is borrowed */
363 /* plist is 0 or borrowed */
366 /* type is borrowed */
368 eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple.");
369 /* value is borrowed */
373 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
379 int eListboxPythonConfigContent::currentCursorSelectable()
381 return eListboxPythonStringContent::currentCursorSelectable();
384 //////////////////////////////////////
386 /* todo: make a real infrastructure here! */
387 RESULT SwigFromPython(ePtr<gPixmap> &res, PyObject *obj);
389 eListboxPythonMultiContent::eListboxPythonMultiContent()
394 eListboxPythonMultiContent::~eListboxPythonMultiContent()
397 Py_DECREF(m_buildFunc);
400 void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
402 eRect itemrect(offset, m_itemsize);
403 painter.clip(itemrect);
404 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
409 if (m_list && cursorValid())
411 items = PyList_GET_ITEM(m_list, m_cursor); // borrowed reference!
415 if (PyCallable_Check(m_buildFunc)) // when we have a buildFunc then call it
417 if (PyTuple_Check(items))
418 items = PyObject_CallObject(m_buildFunc, items);
420 eDebug("items is no tuple");
423 eDebug("buildfunc is not callable");
428 eDebug("eListboxPythonMultiContent: error getting item %d", m_cursor);
432 if (!PyList_Check(items))
434 eDebug("eListboxPythonMultiContent: list entry %d is not a list", m_cursor);
438 int size = PyList_Size(items);
439 for (int i = 1; i < size; ++i)
441 PyObject *item = PyList_GET_ITEM(items, i); // borrowed reference!
445 eDebug("eListboxPythonMultiContent: ?");
449 PyObject *px = 0, *py = 0, *pwidth = 0, *pheight = 0, *pfnt = 0, *pstring = 0, *pflags = 0, *pcolor = 0;
452 we have a list of tuples:
454 (0, x, y, width, height, fnt, flags, "bla"[, color] ),
457 (1, x, y, width, height, filled_percent )
461 (2, x, y, width, height, pixmap )
465 if (!PyTuple_Check(item))
467 eDebug("eListboxPythonMultiContent did not receive a tuple.");
471 int size = PyTuple_Size(item);
475 eDebug("eListboxPythonMultiContent receive empty tuple.");
479 int type = PyInt_AsLong(PyTuple_GET_ITEM(item, 0));
483 px = PyTuple_GET_ITEM(item, 1);
484 py = PyTuple_GET_ITEM(item, 2);
485 pwidth = PyTuple_GET_ITEM(item, 3);
486 pheight = PyTuple_GET_ITEM(item, 4);
487 pfnt = PyTuple_GET_ITEM(item, 5); /* could also be an pixmap or an int (progress filled percent) */
490 pflags = PyTuple_GET_ITEM(item, 6);
491 pstring = PyTuple_GET_ITEM(item, 7);
494 pcolor = PyTuple_GET_ITEM(item, 8);
499 case TYPE_TEXT: // text
501 if (!(px && py && pwidth && pheight && pfnt && pstring))
503 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_TEXT, x, y, width, height, fnt, flags, string, [color, ]...])");
507 const char *string = (PyString_Check(pstring)) ? PyString_AsString(pstring) : "<not-a-string>";
508 int x = PyInt_AsLong(px);
509 int y = PyInt_AsLong(py);
510 int width = PyInt_AsLong(pwidth);
511 int height = PyInt_AsLong(pheight);
512 int flags = PyInt_AsLong(pflags);
513 int fnt = PyInt_AsLong(pfnt);
517 int color = PyInt_AsLong(pcolor);
518 painter.setForegroundColor(gRGB(color));
521 if (m_font.find(fnt) == m_font.end())
523 eDebug("eListboxPythonMultiContent: specified font %d was not found!", fnt);
527 eRect r = eRect(x, y, width, height);
531 painter.setFont(m_font[fnt]);
534 painter.renderText(r, string, flags);
538 case TYPE_PROGRESS: // Progress
540 if (!(px && py && pwidth && pheight && pfnt))
542 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent))");
545 int x = PyInt_AsLong(px);
546 int y = PyInt_AsLong(py);
547 int width = PyInt_AsLong(pwidth);
548 int height = PyInt_AsLong(pheight);
549 int filled = PyInt_AsLong(pfnt);
551 eRect r = eRect(x, y, width, height);
556 int bwidth=2; // borderwidth hardcoded yet
559 eRect rc = eRect(x, y, width, bwidth);
563 rc = eRect(x, y+bwidth, bwidth, height-bwidth);
567 rc = eRect(x+bwidth, y+height-bwidth, width-bwidth, bwidth);
571 rc = eRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth);
576 rc = eRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2);
584 case TYPE_PIXMAP_ALPHATEST:
585 case TYPE_PIXMAP: // pixmap
587 if (!(px && py && pwidth && pheight && pfnt))
589 eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap))");
592 int x = PyInt_AsLong(px);
593 int y = PyInt_AsLong(py);
594 int width = PyInt_AsLong(pwidth);
595 int height = PyInt_AsLong(pheight);
596 ePtr<gPixmap> pixmap;
597 if (SwigFromPython(pixmap, pfnt))
599 eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed");
603 eRect r = eRect(x, y, width, height);
608 painter.blit(pixmap, r.topLeft(), r, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : 0);
614 eWarning("eListboxPythonMultiContent received unknown type (%d)", type);
619 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
624 style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
627 if (m_buildFunc && PyCallable_Check(m_buildFunc) && items)
633 void eListboxPythonMultiContent::setBuildFunc(PyObject *cb)
636 Py_DECREF(m_buildFunc);
639 Py_INCREF(m_buildFunc);
642 int eListboxPythonMultiContent::currentCursorSelectable()
644 /* each list-entry is a list of tuples. if the first of these is none, it's not selectable */
645 if (m_list && cursorValid())
647 PyObject *item = PyList_GET_ITEM(m_list, m_cursor);
648 if (PyList_Check(item))
650 item = PyList_GET_ITEM(item, 0);
654 else if (m_buildFunc && PyCallable_Check(m_buildFunc))
655 // FIXME .. how we can detect non selectable entrys when we have a buildFunc callback
661 void eListboxPythonMultiContent::setFont(int fnt, gFont *font)