diff options
| -rw-r--r-- | FL/Fl_Tree.H | 56 | ||||
| -rw-r--r-- | examples/tree-simple.cxx | 34 | ||||
| -rw-r--r-- | src/Fl_Tree.cxx | 59 |
3 files changed, 124 insertions, 25 deletions
diff --git a/FL/Fl_Tree.H b/FL/Fl_Tree.H index 7bbc38a3c..29344f12e 100644 --- a/FL/Fl_Tree.H +++ b/FL/Fl_Tree.H @@ -115,7 +115,26 @@ /// /// The tree's callback() will be invoked when items change state or are open/closed. /// when() controls when mouse/keyboard events invoke the callback. -/// callback_item() and callback_reason() can be used to determine the cause of the callback. +/// callback_item() and callback_reason() can be used to determine the cause of the callback. eg: +/// +/// \code +/// void MyTreeCallback(Fl_Widget *w, void *data) { +/// Fl_Tree *tree = (Fl_Tree*)w; +/// Fl_Tree_Item *item = (Fl_Tree_Item*)tree->callback_item(); // get selected item +/// switch ( tree->callback_reason() ) { +/// case FL_TREE_REASON_SELECTED: [..] +/// case FL_TREE_REASON_DESELECTED: [..] +/// case FL_TREE_REASON_OPENED: [..] +/// case FL_TREE_REASON_CLOSED: [..] +/// } +/// \endcode +/// +/// To get the item's full menu pathname, you can use Fl_Tree_Item::item_pathname(), eg: +/// +/// \code +/// char pathname[256] = "???"; +/// tree->item_pathname(pathname, sizeof(pathname), item); // eg. "Parent/Child/Item" +/// \endcode /// /// To walk all the items of the tree from top to bottom: /// \code @@ -328,10 +347,13 @@ public: /// Handles redrawing if anything was actually changed. /// Invokes the callback depending on the value of optional parameter \p docallback. /// + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. open("Holidays/12\\/25\//2010"). + /// /// The callback can use callback_item() and callback_reason() respectively to determine /// the item changed and the reason the callback was called. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \param[in] docallback -- A flag that determines if the callback() is invoked or not: /// - 0 - callback() is not invoked /// - 1 - callback() is invoked if item changed, @@ -401,10 +423,13 @@ public: /// Handles redrawing if anything was actually changed. /// Invokes the callback depending on the value of optional parameter \p docallback. /// + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. close("Holidays/12\\/25\//2010"). + /// /// The callback can use callback_item() and callback_reason() respectively to determine /// the item changed and the reason the callback was called. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \param[in] docallback -- A flag that determines if the callback() is invoked or not: /// - 0 - callback() is not invoked /// - 1 - callback() is invoked if item changed, @@ -436,10 +461,13 @@ public: } /// See if item specified by \p path (eg: "Parent/child/item") is open. /// + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. is_open("Holidays/12\\/25\//2010"). + /// /// Items that are 'open' are themselves not necessarily visible; /// one of the item's parents might be closed. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \returns /// - 1 - OK: item is open /// - 0 - OK: item is closed @@ -462,7 +490,10 @@ public: } /// See if item specified by \p path (eg: "Parent/child/item") is closed. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. is_close("Holidays/12\\/25\//2010"). + /// + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \returns /// - 1 - OK: item is closed /// - 0 - OK: item is open @@ -506,10 +537,13 @@ public: /// Handles redrawing if anything was actually changed. /// Invokes the callback depending on the value of optional parameter \p docallback. /// + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. select("Holidays/12\\/25\//2010"). + /// /// The callback can use callback_item() and callback_reason() respectively to determine /// the item changed and the reason the callback was called. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \param[in] docallback -- A flag that determines if the callback() is invoked or not: /// - 0 - the callback() is not invoked /// - 1 - the callback() is invoked if item changed state, @@ -578,10 +612,13 @@ public: /// Handles redrawing if anything was actually changed. /// Invokes the callback depending on the value of optional parameter \p docallback. /// + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. deselect("Holidays/12\\/25\//2010"). + /// /// The callback can use callback_item() and callback_reason() respectively to determine /// the item changed and the reason the callback was called. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \param[in] docallback -- A flag that determines if the callback() is invoked or not: /// - 0 - the callback() is not invoked /// - 1 - the callback() is invoked if item changed state, @@ -615,7 +652,10 @@ public: } /// See if item specified by \p path (eg: "Parent/child/item") is selected. /// - /// \param[in] path -- the tree item's pathname (eg. "Flintstones/Fred") + /// Items or submenus that themselves contain slashes ('/' or '\') + /// should be escaped, e.g. is_selected("Holidays/12\\/25\//2010"). + /// + /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred") /// \returns /// - 1 : item selected /// - 0 : item deselected diff --git a/examples/tree-simple.cxx b/examples/tree-simple.cxx index d7c13ab23..df6722b2d 100644 --- a/examples/tree-simple.cxx +++ b/examples/tree-simple.cxx @@ -31,12 +31,30 @@ #include <FL/Fl_Tree.H> // Tree's callback -// Invoked whenever someone clicks an item. +// Invoked whenever an item's state changes. // void TreeCallback(Fl_Widget *w, void *data) { - Fl_Tree *tree = (Fl_Tree*)w; - Fl_Tree_Item *item = (Fl_Tree_Item*)tree->item_clicked(); - fprintf(stderr, "TreeCallback: item clicked='%s'\n", (item)?item->label():"???"); + Fl_Tree *tree = (Fl_Tree*)w; + Fl_Tree_Item *item = (Fl_Tree_Item*)tree->callback_item(); + if ( ! item ) return; + switch ( tree->callback_reason() ) { + case FL_TREE_REASON_SELECTED: { + char pathname[256]; + tree->item_pathname(pathname, sizeof(pathname), item); + fprintf(stderr, "TreeCallback: Item selected='%s', Full pathname='%s'\n", item->label(), pathname); + break; + } + case FL_TREE_REASON_DESELECTED: + // fprintf(stderr, "TreeCallback: Item '%s' deselected\n", item->label()); + break; + case FL_TREE_REASON_OPENED: + // fprintf(stderr, "TreeCallback: Item '%s' opened\n", item->label()); + break; + case FL_TREE_REASON_CLOSED: + // fprintf(stderr, "TreeCallback: Item '%s' closed\n", item->label()); + default: + break; + } } int main(int argc, char *argv[]) { @@ -57,9 +75,15 @@ int main(int argc, char *argv[]) { tree->add("Simpsons/Marge"); tree->add("Simpsons/Bart"); tree->add("Simpsons/Lisa"); + tree->add("Holidays/01\\/01 New Years"); + tree->add("Holidays/02\\/15 Valentine's Day"); + tree->add("Holidays/05\\/05 Cinco de Mayo"); + tree->add("Holidays/07\\/04 Independence Day"); + tree->add("Holidays/12\\/25 Christmas"); - // Start with one of the items closed + // Start with some items closed tree->close("Simpsons"); + tree->close("Holidays"); } win->end(); win->resizable(win); diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx index a3c7cd3ff..de07c599c 100644 --- a/src/Fl_Tree.cxx +++ b/src/Fl_Tree.cxx @@ -38,6 +38,7 @@ static void scroll_cb(Fl_Widget*,void *data) { } // INTERNAL: Parse elements from path into an array of null terminated strings +// Handles escape characters. // Path="/aa/bb" // Return: arr[0]="aa", arr[1]="bb", arr[2]=0 // Caller must call free_path(arr). @@ -48,19 +49,29 @@ static char **parse_path(const char *path) { int seps = 1; // separator count (1: first item) int arrsize = 1; // array size (1: first item) char *save = strdup(path); // make copy we can modify - char *s = save; - while ( ( s = strchr(s, '/') ) ) { - while ( *s == '/' ) { *s++ = 0; seps++; } - if ( *s ) { arrsize++; } + char *sin = save, *sout = save; + while ( *sin ) { + if ( *sin == '\\' ) { // handle escape character + *sout++ = *++sin; + if ( *sin ) ++sin; + } else if ( *sin == '/' ) { // handle submenu + *sout++ = 0; + sin++; + seps++; + arrsize++; + } else { // all other chars + *sout++ = *sin++; + } } + *sout = 0; arrsize++; // (room for terminating NULL) // Second pass: create array, save nonblank elements char **arr = (char**)malloc(sizeof(char*) * arrsize); int t = 0; - s = save; + sin = save; while ( seps-- > 0 ) { - if ( *s ) { arr[t++] = s; } // skips empty fields, eg. '//' - s += (strlen(s) + 1); + if ( *sin ) { arr[t++] = sin; } // skips empty fields, e.g. '//' + sin += (strlen(sin) + 1); } arr[t] = 0; return(arr); @@ -111,6 +122,15 @@ Fl_Tree::~Fl_Tree() { /// Adds a new item, given a 'menu style' path, eg: "/Parent/Child/item". /// Any parent nodes that don't already exist are created automatically. /// Adds the item based on the value of sortorder(). +/// +/// To specify items or submenus that contain slashes ('/' or '\') +/// use an escape character to protect them, e.g. +/// +/// \code +/// tree->add("/Holidays/Photos/12\\/25\\2010"); // Adds item "12/25/2010" +/// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp" +/// \endcode +/// /// \returns the child item created, or 0 on error. /// Fl_Tree_Item* Fl_Tree::add(const char *path) { @@ -156,12 +176,19 @@ Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) { } /// Find the item, given a menu style path, eg: "/Parent/Child/item". -/// /// There is both a const and non-const version of this method. /// Const version allows pure const methods to use this method /// to do lookups without causing compiler errors. /// -/// \param[in] path -- the tree item's pathname to be found (eg. "Flintstones/Fred") +/// To specify items or submenus that contain slashes ('/' or '\') +/// use an escape character to protect them, e.g. +/// +/// \code +/// tree->add("/Holidays/Photos/12\\/25\\2010"); // Adds item "12/25/2010" +/// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp" +/// \endcode +/// +/// \param[in] path -- the tree item's pathname to be found (e.g. "Flintstones/Fred") /// \returns the item, or NULL if not found. /// /// \see item_pathname() @@ -195,6 +222,9 @@ const Fl_Tree_Item *Fl_Tree::find_item(const char *path) const { /// Find the pathname for the specified \p item. /// If \p item is NULL, root() is used. /// The tree's root will be included in the pathname of showroot() is on. +/// Menu items or submenus that contain slashes ('/' or '\') in their names +/// will be escaped with a backslash. This is symmetrical with the add() +/// function which uses the same escape pattern to set names. /// \param[in] pathname The string to use to return the pathname /// \param[in] pathnamelen The maximum length of the string (including NULL). Must not be zero. /// \param[in] item The item whose pathname is to be returned. @@ -218,7 +248,12 @@ int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item * const char *name = item->label() ? item->label() : "???"; // name for this item int len = strlen(name); // Add name to end of pathname[] - for ( --len; len>=0; len-- ) { SAFE_RCAT(name[len]); } // rcat name of item + for ( --len; len>=0; len-- ) { + SAFE_RCAT(name[len]); // rcat name of item + if ( name[len] == '/' || name[len] == '\\' ) { + SAFE_RCAT('\\'); // escape front or back slashes within name + } + } SAFE_RCAT('/'); // rcat leading slash item = item->parent(); // move up tree (NULL==root) } @@ -258,7 +293,7 @@ void Fl_Tree::draw() { // Show vertical scrollbar? int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree - int ytoofar = (cy+ch) - Y; // ytoofar -- scrolled beyond bottom (eg. stow) + int ytoofar = (cy+ch) - Y; // ytoofar -- scrolled beyond bottom (e.g. stow) //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n", //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value())); @@ -813,7 +848,7 @@ int Fl_Tree::displayed(Fl_Tree_Item *item) { } /// Adjust the vertical scroll bar to show \p item at the top -/// of the display IF it is currently off-screen (eg. show_item_top()). +/// of the display IF it is currently off-screen (e.g. show_item_top()). /// If it is already on-screen, no change is made. /// /// \param[in] item The item to be shown. If NULL, first() is used. |
