changeset 2831:1aad97a58668

Fl_Browser instead of Fl_Tree for <select> list box. Discussion: http://lists.auriga.wearlab.de/pipermail/dillo-dev/2012-December/009604.html http://lists.auriga.wearlab.de/pipermail/dillo-dev/2012-December/009609.html Fl_Tree didn't handle the 18000-item list in https://bugzilla.redhat.com/query.cgi?format=advanced so well, plus it's relatively heavy, plus we've found bugs in it on occasion because it's newer than Fl_Browser. Fl_Browser is naturally closer to what we use than Fl_Tree is in its ordinary default usage. Why didn't I use Fl_Browser initially when going to fltk-1.3? Documentation made it sound like fltk1 and fltk2's browsers were quite different despite having the same name (I think the APIs were rather dissimilar, although I don't feel it's worth my time to verify that right now), and I believe I had the impression that Fl_Browser was going to be too limited in some ways.
author corvid <corvid@lavabit.com>
date Fri, 19 Apr 2013 01:16:38 +0000
parents 4af25c14d9a6
children 9e5b55080adb
files dw/fltkui.cc dw/fltkui.hh
diffstat 2 files changed, 138 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/dw/fltkui.cc	Wed Apr 17 11:20:23 2013 -0300
+++ b/dw/fltkui.cc	Fri Apr 19 01:16:38 2013 +0000
@@ -32,7 +32,7 @@
 #include <FL/Fl_Check_Button.H>
 #include <FL/Fl_Round_Button.H>
 #include <FL/Fl_Choice.H>
-#include <FL/Fl_Tree.H>
+#include <FL/Fl_Browser.H>
 
 #include <stdio.h>
 
@@ -1219,11 +1219,37 @@
 
 // ----------------------------------------------------------------------
 
+class CustBrowser : public Fl_Browser {
+public:
+   CustBrowser(int x, int y, int w, int h) : Fl_Browser(x, y, w, h) {};
+   int full_width();
+   int avg_height() {return size() ? Fl_Browser_::incr_height() : 0;}
+};
+
+/*
+ * Fl_Browser_ has a full_width(), but it has a tendency to contain 0, so...
+ */
+int CustBrowser::full_width()
+{
+   int max = 0;
+   void *item = item_first();
+
+   while (item) {
+      int w = item_width(item);
+
+      if (w > max)
+         max = w;
+
+      item = item_next(item);
+   }
+   return max;
+}
+
 FltkListResource::FltkListResource (FltkPlatform *platform,
                                     core::ui::ListResource::SelectionMode
                                     selectionMode, int rowCount):
    FltkSelectionResource <dw::core::ui::ListResource> (platform),
-   itemsSelected(8)
+   currDepth(0)
 {
    mode = selectionMode;
    showRows = rowCount;
@@ -1237,165 +1263,160 @@
 
 Fl_Widget *FltkListResource::createNewWidget (core::Allocation *allocation)
 {
-   Fl_Tree *tree =
-      new Fl_Tree (allocation->x, allocation->y, allocation->width,
-                           allocation->ascent + allocation->descent);
+   CustBrowser *b =
+      new CustBrowser (allocation->x, allocation->y, allocation->width,
+                      allocation->ascent + allocation->descent);
 
-   tree->selectmode((mode == SELECTION_MULTIPLE) ? FL_TREE_SELECT_MULTI
-                                                 : FL_TREE_SELECT_SINGLE);
-   tree->showroot(0);
-   tree->connectorstyle(FL_TREE_CONNECTOR_NONE);
-   tree->margintop(0);
-   tree->marginleft(-14);
-   tree->callback(widgetCallback,this);
-   tree->when(FL_WHEN_CHANGED);
+   b->type((mode == SELECTION_MULTIPLE) ? FL_MULTI_BROWSER : FL_HOLD_BROWSER);
+   b->callback(widgetCallback, this);
+   b->when(FL_WHEN_CHANGED);
+   b->column_widths(colWidths);
+   b->column_char('\a');   // I just chose a nonprinting character.
 
-   currParent = tree->root();
-   return tree;
+   return b;
 }
 
 void FltkListResource::setWidgetStyle (Fl_Widget *widget,
                                        core::style::Style *style)
 {
-   Fl_Tree *t = (Fl_Tree *)widget;
+   Fl_Browser *b = (Fl_Browser *)widget;
 
    FltkResource::setWidgetStyle(widget, style);
 
-   t->item_labelfont(widget->labelfont());
-   t->item_labelsize(widget->labelsize());
-   t->item_labelfgcolor(widget->labelcolor());
-   t->item_labelbgcolor(widget->color());
+   b->textfont(widget->labelfont());
+   b->textsize(widget->labelsize());
+   b->textcolor(widget->labelcolor());
+
+   colWidths[0] = b->textsize();
+   colWidths[1] = colWidths[0];
+   colWidths[2] = colWidths[0];
+   colWidths[3] = 0;
 }
 
 void FltkListResource::widgetCallback (Fl_Widget *widget, void *data)
 {
-   Fl_Tree_Item *fltkItem = ((Fl_Tree *) widget)->callback_item ();
-   int index = -1;
+   Fl_Browser *b = (Fl_Browser *) widget;
 
-   if (fltkItem)
-      index = (long) (fltkItem->user_data ());
-   if (index > -1) {
-      bool selected = fltkItem->is_selected ();
+   if (b->selected(b->value())) {
+      /* If it shouldn't be selectable, deselect it again. It would be nice to
+       * have a less unpleasant way to do this.
+       */
+      const char *inactive_code;
+      if ((inactive_code = strstr(b->text(b->value()), "@N"))) {
+         const char *ignore_codes = strstr(b->text(b->value()), "@.");
 
-      if (selected && fltkItem->has_children()) {
-         /* Don't permit a group to be selected. */
-         fltkItem->deselect();
-      } else {
-         FltkListResource *res = (FltkListResource *) data;
-         res->itemsSelected.set (index, selected);
+         if (inactive_code < ignore_codes)
+            b->select(b->value(), 0);
       }
    }
 }
 
 void *FltkListResource::newItem (const char *str, bool enabled, bool selected)
 {
-   Fl_Tree *tree = (Fl_Tree *) widget;
-   Fl_Tree_Item *parent = (Fl_Tree_Item *)currParent;
-   Fl_Tree_Item *item = tree->add(parent, str);
-   int index = itemsSelected.size();
+   Fl_Browser *b = (Fl_Browser *) widget;
+   int index = b->size() + 1;
+   char *label = (char *)malloc(strlen(str) + 1 + currDepth + 4),
+        *s = label;
 
-   enabled &= parent->is_active();
-   item->activate(enabled);
-   item->user_data((void*)(long)index);
-   itemsSelected.increase ();
-   itemsSelected.set (itemsSelected.size() - 1, selected);
+   memset(s, '\a', currDepth);
+   s += currDepth;
+   if (!enabled) {
+      // FL_INACTIVE_COLOR
+      *s++ = '@';
+      *s++ = 'N';
+   }
+   // ignore further '@' chars
+   *s++ = '@';
+   *s++ = '.';
 
-   return item;
+   strcpy(s, str);
+
+   b->add(label, (void*)(long)(index));
+   free(label);
+
+   if (selected) {
+      b->select(index, selected);
+      if (b->type() == FL_HOLD_BROWSER) {
+         /* Left to its own devices, it sometimes has some suboptimal ideas
+          * about how to scroll, and sometimes doesn't seem to show everything
+          * where it thinks it is.
+          */
+         if (index > showRows) {
+            /* bottomline() and middleline() don't work because the widget is
+             * too tiny at this point for the bbox() call in
+             * Fl_Browser::lineposition() to do what one would want.
+             */
+            b->topline(index - showRows + 1);
+         } else {
+            b->topline(1);
+         }
+      }
+   }
+   queueResize (true);
+   return NULL;
 }
 
 void FltkListResource::addItem (const char *str, bool enabled, bool selected)
 {
-   Fl_Tree *tree = (Fl_Tree *) widget;
-   Fl_Tree_Item *item = (Fl_Tree_Item *) newItem(str, enabled, selected);
-
-   if (selected) {
-      if (mode == SELECTION_MULTIPLE) {
-         item->select(selected);
-      } else {
-         const bool do_callback = true;
-         tree->select_only(item, do_callback);
-      }
-   }
-   queueResize (true);
+   // Fl_Browser_::incr_height() for item height won't do the right thing if
+   // the first item doesn't have anything to it.
+   if (!str || !*str)
+      str = " ";
+   newItem(str, enabled, selected);
 }
 
 void FltkListResource::setItem (int index, bool selected)
 {
-   Fl_Tree *tree = (Fl_Tree *) widget;
-   Fl_Tree_Item *item = tree->root()->next();
-
-   for (int i = 0; item && i < index; i++)
-      item = item->next();
+   Fl_Browser *b = (Fl_Browser *) widget;
 
-   if (item) {
-      bool do_callback = false;
-      itemsSelected.set (index, selected);
-      if (selected) {
-         if (mode == SELECTION_MULTIPLE) {
-            tree->select(item, do_callback);
-         } else {
-            /* callback to deselect other selected item */
-            do_callback = true;
-            tree->select_only(item, do_callback);
-         }
-      } else {
-         tree->deselect(item, do_callback);
-      }
-   }
+   b->select(index + 1, selected);
 }
 
 void FltkListResource::pushGroup (const char *name, bool enabled)
 {
+   bool en = false;
    bool selected = false;
 
-   currParent = (Fl_Tree_Item *) newItem(name, enabled, selected);
-   queueResize (true);
+   // Fl_Browser_::incr_height() for item height won't do the right thing if
+   // the first item doesn't have anything to it.
+   if (!name || !*name)
+      name = " ";
+
+   // TODO: Proper disabling of item groups
+   newItem(name, en, selected);
+   
+   if (currDepth < 3)
+      currDepth++;
 }
 
 void FltkListResource::popGroup ()
 {
-   Fl_Tree_Item *p = (Fl_Tree_Item *)currParent;
-
-   if (p->parent())
-      currParent = p->parent();
+   if (currDepth)
+      currDepth--;
 }
 
 int FltkListResource::getMaxItemWidth()
 {
-   Fl_Tree *tree = (Fl_Tree *)widget;
-   int max = 0;
-
-   for (Fl_Tree_Item *i = tree->first(); i; i = tree->next(i)) {
-      int width = 0;
-
-      if (i == tree->root())
-         continue;
-
-      for (Fl_Tree_Item *p = i->parent(); p != tree->root(); p = p->parent())
-         width += tree->connectorwidth();
-
-      if (i->label())
-         width += fl_width(i->label());
-
-      if (width > max)
-         max = width;
-   }
-   return max;
+   return ((CustBrowser *) widget)->full_width();
 }
 
 void FltkListResource::sizeRequest (core::Requisition *requisition)
 {
    if (style) {
-      FltkFont *font = (FltkFont*)style->font;
-      fl_font(font->font,font->size);
-      int rows = getNumberOfItems();
+      CustBrowser *b = (CustBrowser *) widget;
+      int rows = b->size();
+      requisition->width = getMaxItemWidth() + 4;
+
       if (showRows < rows) {
          rows = showRows;
+         b->has_scrollbar(Fl_Browser_::VERTICAL_ALWAYS);
+         requisition->width += Fl::scrollbar_size();
+      } else {
+         b->has_scrollbar(0);
       }
-      requisition->width = getMaxItemWidth() + 5 + Fl::scrollbar_size();
-      requisition->descent = font->descent + 2;
-      requisition->ascent = (rows * (font->size + font->descent + 1)) + 4 -
-                            requisition->descent;
+      
+      requisition->descent = style->font->descent + 2;
+      requisition->ascent = rows * b->avg_height() - style->font->descent + 2;
    } else {
       requisition->width = 1;
       requisition->ascent = 1;
@@ -1403,9 +1424,16 @@
    }
 }
 
+int FltkListResource::getNumberOfItems()
+{
+   return ((Fl_Browser*)widget)->size();
+}
+
 bool FltkListResource::isSelected (int index)
 {
-   return itemsSelected.get (index);
+   Fl_Browser *b = (Fl_Browser *) widget;
+
+   return b->selected(index + 1) ? true : false;
 }
 
 } // namespace ui
--- a/dw/fltkui.hh	Wed Apr 17 11:20:23 2013 -0300
+++ b/dw/fltkui.hh	Fri Apr 19 01:16:38 2013 +0000
@@ -505,15 +505,13 @@
 protected:
    Fl_Widget *createNewWidget (core::Allocation *allocation);
    void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
-
-   int getNumberOfItems () {return itemsSelected.size();};
+   int getNumberOfItems();
    int getMaxItemWidth ();
-
 private:
    static void widgetCallback (Fl_Widget *widget, void *data);
    void *newItem (const char *str, bool enabled, bool selected);
-   void *currParent;
-   lout::misc::SimpleVector <bool> itemsSelected;
+   int currDepth;
+   int colWidths[4];
    int showRows;
    ListResource::SelectionMode mode;
 public: