mirror of https://github.com/fltk/fltk
Fl_Tree mods for handling escaped item menu names.
Docs clarified. Also, tree-simple example's callback handler brought up to date. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@8632 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
parent
14839338ec
commit
6871a6512a
56
FL/Fl_Tree.H
56
FL/Fl_Tree.H
|
@ -115,7 +115,26 @@
|
||||||
///
|
///
|
||||||
/// The tree's callback() will be invoked when items change state or are open/closed.
|
/// The tree's callback() will be invoked when items change state or are open/closed.
|
||||||
/// when() controls when mouse/keyboard events invoke the callback.
|
/// 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:
|
/// To walk all the items of the tree from top to bottom:
|
||||||
/// \code
|
/// \code
|
||||||
|
@ -328,10 +347,13 @@ public:
|
||||||
/// Handles redrawing if anything was actually changed.
|
/// Handles redrawing if anything was actually changed.
|
||||||
/// Invokes the callback depending on the value of optional parameter \p docallback.
|
/// 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 callback can use callback_item() and callback_reason() respectively to determine
|
||||||
/// the item changed and the reason the callback was called.
|
/// 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:
|
/// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
|
||||||
/// - 0 - callback() is not invoked
|
/// - 0 - callback() is not invoked
|
||||||
/// - 1 - callback() is invoked if item changed,
|
/// - 1 - callback() is invoked if item changed,
|
||||||
|
@ -401,10 +423,13 @@ public:
|
||||||
/// Handles redrawing if anything was actually changed.
|
/// Handles redrawing if anything was actually changed.
|
||||||
/// Invokes the callback depending on the value of optional parameter \p docallback.
|
/// 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 callback can use callback_item() and callback_reason() respectively to determine
|
||||||
/// the item changed and the reason the callback was called.
|
/// 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:
|
/// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
|
||||||
/// - 0 - callback() is not invoked
|
/// - 0 - callback() is not invoked
|
||||||
/// - 1 - callback() is invoked if item changed,
|
/// - 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.
|
/// 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;
|
/// Items that are 'open' are themselves not necessarily visible;
|
||||||
/// one of the item's parents might be closed.
|
/// 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
|
/// \returns
|
||||||
/// - 1 - OK: item is open
|
/// - 1 - OK: item is open
|
||||||
/// - 0 - OK: item is closed
|
/// - 0 - OK: item is closed
|
||||||
|
@ -462,7 +490,10 @@ public:
|
||||||
}
|
}
|
||||||
/// See if item specified by \p path (eg: "Parent/child/item") is closed.
|
/// 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
|
/// \returns
|
||||||
/// - 1 - OK: item is closed
|
/// - 1 - OK: item is closed
|
||||||
/// - 0 - OK: item is open
|
/// - 0 - OK: item is open
|
||||||
|
@ -506,10 +537,13 @@ public:
|
||||||
/// Handles redrawing if anything was actually changed.
|
/// Handles redrawing if anything was actually changed.
|
||||||
/// Invokes the callback depending on the value of optional parameter \p docallback.
|
/// 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 callback can use callback_item() and callback_reason() respectively to determine
|
||||||
/// the item changed and the reason the callback was called.
|
/// 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:
|
/// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
|
||||||
/// - 0 - the callback() is not invoked
|
/// - 0 - the callback() is not invoked
|
||||||
/// - 1 - the callback() is invoked if item changed state,
|
/// - 1 - the callback() is invoked if item changed state,
|
||||||
|
@ -578,10 +612,13 @@ public:
|
||||||
/// Handles redrawing if anything was actually changed.
|
/// Handles redrawing if anything was actually changed.
|
||||||
/// Invokes the callback depending on the value of optional parameter \p docallback.
|
/// 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 callback can use callback_item() and callback_reason() respectively to determine
|
||||||
/// the item changed and the reason the callback was called.
|
/// 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:
|
/// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
|
||||||
/// - 0 - the callback() is not invoked
|
/// - 0 - the callback() is not invoked
|
||||||
/// - 1 - the callback() is invoked if item changed state,
|
/// - 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.
|
/// 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
|
/// \returns
|
||||||
/// - 1 : item selected
|
/// - 1 : item selected
|
||||||
/// - 0 : item deselected
|
/// - 0 : item deselected
|
||||||
|
|
|
@ -31,12 +31,30 @@
|
||||||
#include <FL/Fl_Tree.H>
|
#include <FL/Fl_Tree.H>
|
||||||
|
|
||||||
// Tree's callback
|
// Tree's callback
|
||||||
// Invoked whenever someone clicks an item.
|
// Invoked whenever an item's state changes.
|
||||||
//
|
//
|
||||||
void TreeCallback(Fl_Widget *w, void *data) {
|
void TreeCallback(Fl_Widget *w, void *data) {
|
||||||
Fl_Tree *tree = (Fl_Tree*)w;
|
Fl_Tree *tree = (Fl_Tree*)w;
|
||||||
Fl_Tree_Item *item = (Fl_Tree_Item*)tree->item_clicked();
|
Fl_Tree_Item *item = (Fl_Tree_Item*)tree->callback_item();
|
||||||
fprintf(stderr, "TreeCallback: item clicked='%s'\n", (item)?item->label():"???");
|
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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -57,9 +75,15 @@ int main(int argc, char *argv[]) {
|
||||||
tree->add("Simpsons/Marge");
|
tree->add("Simpsons/Marge");
|
||||||
tree->add("Simpsons/Bart");
|
tree->add("Simpsons/Bart");
|
||||||
tree->add("Simpsons/Lisa");
|
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("Simpsons");
|
||||||
|
tree->close("Holidays");
|
||||||
}
|
}
|
||||||
win->end();
|
win->end();
|
||||||
win->resizable(win);
|
win->resizable(win);
|
||||||
|
|
|
@ -38,6 +38,7 @@ static void scroll_cb(Fl_Widget*,void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERNAL: Parse elements from path into an array of null terminated strings
|
// INTERNAL: Parse elements from path into an array of null terminated strings
|
||||||
|
// Handles escape characters.
|
||||||
// Path="/aa/bb"
|
// Path="/aa/bb"
|
||||||
// Return: arr[0]="aa", arr[1]="bb", arr[2]=0
|
// Return: arr[0]="aa", arr[1]="bb", arr[2]=0
|
||||||
// Caller must call free_path(arr).
|
// 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 seps = 1; // separator count (1: first item)
|
||||||
int arrsize = 1; // array size (1: first item)
|
int arrsize = 1; // array size (1: first item)
|
||||||
char *save = strdup(path); // make copy we can modify
|
char *save = strdup(path); // make copy we can modify
|
||||||
char *s = save;
|
char *sin = save, *sout = save;
|
||||||
while ( ( s = strchr(s, '/') ) ) {
|
while ( *sin ) {
|
||||||
while ( *s == '/' ) { *s++ = 0; seps++; }
|
if ( *sin == '\\' ) { // handle escape character
|
||||||
if ( *s ) { arrsize++; }
|
*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)
|
arrsize++; // (room for terminating NULL)
|
||||||
// Second pass: create array, save nonblank elements
|
// Second pass: create array, save nonblank elements
|
||||||
char **arr = (char**)malloc(sizeof(char*) * arrsize);
|
char **arr = (char**)malloc(sizeof(char*) * arrsize);
|
||||||
int t = 0;
|
int t = 0;
|
||||||
s = save;
|
sin = save;
|
||||||
while ( seps-- > 0 ) {
|
while ( seps-- > 0 ) {
|
||||||
if ( *s ) { arr[t++] = s; } // skips empty fields, eg. '//'
|
if ( *sin ) { arr[t++] = sin; } // skips empty fields, e.g. '//'
|
||||||
s += (strlen(s) + 1);
|
sin += (strlen(sin) + 1);
|
||||||
}
|
}
|
||||||
arr[t] = 0;
|
arr[t] = 0;
|
||||||
return(arr);
|
return(arr);
|
||||||
|
@ -111,6 +122,15 @@ Fl_Tree::~Fl_Tree() {
|
||||||
/// Adds a new item, given a 'menu style' path, eg: "/Parent/Child/item".
|
/// Adds a new item, given a 'menu style' path, eg: "/Parent/Child/item".
|
||||||
/// Any parent nodes that don't already exist are created automatically.
|
/// Any parent nodes that don't already exist are created automatically.
|
||||||
/// Adds the item based on the value of sortorder().
|
/// 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.
|
/// \returns the child item created, or 0 on error.
|
||||||
///
|
///
|
||||||
Fl_Tree_Item* Fl_Tree::add(const char *path) {
|
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".
|
/// Find the item, given a menu style path, eg: "/Parent/Child/item".
|
||||||
///
|
|
||||||
/// There is both a const and non-const version of this method.
|
/// There is both a const and non-const version of this method.
|
||||||
/// Const version allows pure const methods to use this method
|
/// Const version allows pure const methods to use this method
|
||||||
/// to do lookups without causing compiler errors.
|
/// 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.
|
/// \returns the item, or NULL if not found.
|
||||||
///
|
///
|
||||||
/// \see item_pathname()
|
/// \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.
|
/// Find the pathname for the specified \p item.
|
||||||
/// If \p item is NULL, root() is used.
|
/// If \p item is NULL, root() is used.
|
||||||
/// The tree's root will be included in the pathname of showroot() is on.
|
/// 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] 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] pathnamelen The maximum length of the string (including NULL). Must not be zero.
|
||||||
/// \param[in] item The item whose pathname is to be returned.
|
/// \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
|
const char *name = item->label() ? item->label() : "???"; // name for this item
|
||||||
int len = strlen(name);
|
int len = strlen(name);
|
||||||
// Add name to end of pathname[]
|
// 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
|
SAFE_RCAT('/'); // rcat leading slash
|
||||||
item = item->parent(); // move up tree (NULL==root)
|
item = item->parent(); // move up tree (NULL==root)
|
||||||
}
|
}
|
||||||
|
@ -258,7 +293,7 @@ void Fl_Tree::draw() {
|
||||||
|
|
||||||
// Show vertical scrollbar?
|
// Show vertical scrollbar?
|
||||||
int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree
|
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",
|
//printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n",
|
||||||
//int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value()));
|
//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
|
/// 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.
|
/// If it is already on-screen, no change is made.
|
||||||
///
|
///
|
||||||
/// \param[in] item The item to be shown. If NULL, first() is used.
|
/// \param[in] item The item to be shown. If NULL, first() is used.
|
||||||
|
|
Loading…
Reference in New Issue