From 7d44e7f378fcd803c24747bf6e7ecebc2d796a19 Mon Sep 17 00:00:00 2001 From: Greg Ercolano Date: Thu, 29 Sep 2011 20:08:54 +0000 Subject: [PATCH] Solve Fl_Tree issues w/ STR#2714 (shadow variable issue for apps) by moving code implementations from Fl_Tree.H -> Fl_Tree.cxx. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9085 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- FL/Fl_Tree.H | 826 +++--------------------- src/Fl_Tree.cxx | 1649 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 1300 insertions(+), 1175 deletions(-) diff --git a/FL/Fl_Tree.H b/FL/Fl_Tree.H index eeb675504..ab5559446 100644 --- a/FL/Fl_Tree.H +++ b/FL/Fl_Tree.H @@ -200,17 +200,9 @@ class FL_EXPORT Fl_Tree : public Fl_Group { int _scrollbar_size; // size of scrollbar trough protected: - /// Vertical scrollbar - Fl_Scrollbar *_vscroll; - -protected: + Fl_Scrollbar *_vscroll; /// Vertical scrollbar void item_clicked(Fl_Tree_Item* val); - /// Do the callback for the item, setting the item and reason - void do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason) { - callback_reason(reason); - callback_item(item); - do_callback((Fl_Widget*)this, user_data()); - } + void do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason); Fl_Tree_Item *next_visible_item(Fl_Tree_Item *start, int dir); public: @@ -218,24 +210,14 @@ public: ~Fl_Tree(); int handle(int e); void draw(); + void show_self(); /////////////////////// // root methods /////////////////////// - - /// Set the label for the root item. - /// - /// Makes an internally managed copy of 'new_label'. - /// - void root_label(const char *new_label) { - if ( ! _root ) return; - _root->label(new_label); - } - /// Returns the root item. - Fl_Tree_Item* root() { - return(_root); - } - + void root_label(const char *new_label); + Fl_Tree_Item* root(); + //////////////////////////////// // Item creation/removal methods //////////////////////////////// @@ -243,61 +225,18 @@ public: Fl_Tree_Item* add(Fl_Tree_Item *item, const char *name); Fl_Tree_Item *insert_above(Fl_Tree_Item *above, const char *name); Fl_Tree_Item* insert(Fl_Tree_Item *item, const char *name, int pos); - - /// Remove the specified \p item from the tree. - /// \p item may not be NULL. - /// If it has children, all those are removed too. - /// \returns 0 if done, -1 if 'item' not found. - /// - int remove(Fl_Tree_Item *item) { - if ( item == _root ) { - clear(); - } else { - Fl_Tree_Item *parent = item->parent(); // find item's parent - if ( ! parent ) return(-1); - parent->remove_child(item); // remove child + children - } - return(0); - } - /// Clear all children from the tree. - /// The tree will be left completely empty. - /// - void clear() { - if ( ! _root ) return; - _root->clear_children(); - delete _root; _root = 0; - } - /// Clear all the children of a particular node in the tree specified by \p item. - /// Item may not be NULL. - /// - void clear_children(Fl_Tree_Item *item) { - if ( item->has_children() ) { - item->clear_children(); - redraw(); // redraw only if there were children to clear - } - } - + int remove(Fl_Tree_Item *item); + void clear(); + void clear_children(Fl_Tree_Item *item); + //////////////////////// // Item lookup methods //////////////////////// Fl_Tree_Item *find_item(const char *path); const Fl_Tree_Item *find_item(const char *path) const; int item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item *item) const; - const Fl_Tree_Item *find_clicked() const; - - /// Return the item that was last clicked. - /// - /// Valid only from within the callback(). - /// - /// Deprecated: use callback_item() instead. - /// - /// \returns the item clicked, or 0 if none. - /// 0 may also be used to indicate several items were clicked/changed. - /// - Fl_Tree_Item *item_clicked() { - return(_callback_item); - } + Fl_Tree_Item *item_clicked(); Fl_Tree_Item *first(); Fl_Tree_Item *next(Fl_Tree_Item *item=0); Fl_Tree_Item *prev(Fl_Tree_Item *item=0); @@ -308,697 +247,92 @@ public: ////////////////////////// // Item open/close methods ////////////////////////// - - /// Open the specified 'item'. - /// This causes the item's children (if any) to be shown. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item to be opened. Must not be NULL. - /// \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, - /// callback_reason() will be FL_TREE_REASON_OPENED - /// \returns - /// - 1 -- item was opened - /// - 0 -- item was already open, no change - /// - /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() - /// - int open(Fl_Tree_Item *item, int docallback=1) { - if ( item->is_open() ) return(0); - item->open(); - redraw(); - if ( docallback ) { - do_callback_for_item(item, FL_TREE_REASON_OPENED); - } - return(1); - } - /// Opens the item specified by \p path (eg: "Parent/child/item"). - /// This causes the item's children (if any) to be shown. - /// 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 (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, - /// callback_reason() will be FL_TREE_REASON_OPENED - /// \returns - /// - 1 -- OK: item opened - /// - 0 -- OK: item was already open, no change - /// - -1 -- ERROR: item was not found - /// - /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() - /// - int open(const char *path, int docallback=1) { - Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(open(item, docallback)); - } - /// Toggle the open state of \p item. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item whose open state is to be toggled. Must not be NULL. - /// \param[in] docallback -- A flag that determines if the callback() is invoked or not: - /// - 0 - callback() is not invoked - /// - 1 - callback() is invoked, callback_reason() will be either - /// FL_TREE_REASON_OPENED or FL_TREE_REASON_CLOSED - /// - /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() - /// - void open_toggle(Fl_Tree_Item *item, int docallback=1) { - if ( item->is_open() ) { - close(item, docallback); - } else { - open(item, docallback); - } - } - /// Closes the specified \p item. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item to be closed. Must not be NULL. - /// \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, - /// callback_reason() will be FL_TREE_REASON_CLOSED - /// \returns - /// - 1 -- item was closed - /// - 0 -- item was already closed, no change - /// - /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() - /// - int close(Fl_Tree_Item *item, int docallback=1) { - if ( item->is_close() ) return(0); - item->close(); - redraw(); - if ( docallback ) { - do_callback_for_item(item, FL_TREE_REASON_CLOSED); - } - return(1); - } - /// Closes the item specified by \p path, eg: "Parent/child/item". - /// 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 (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, - /// callback_reason() will be FL_TREE_REASON_CLOSED - /// \returns - /// - 1 -- OK: item closed - /// - 0 -- OK: item was already closed, no change - /// - -1 -- ERROR: item was not found - /// - /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() - /// - int close(const char *path, int docallback=1) { - Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(close(item, docallback)); - } - /// See if \p item is open. - /// - /// Items that are 'open' are themselves not necessarily visible; - /// one of the item's parents might be closed. - /// - /// \param[in] item -- the item to be tested. Must not be NULL. - /// \returns - /// - 1 : item is open - /// - 0 : item is closed - /// - int is_open(Fl_Tree_Item *item) const { - return(item->is_open()?1:0); - } - /// 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 (e.g. "Flintstones/Fred") - /// \returns - /// - 1 - OK: item is open - /// - 0 - OK: item is closed - /// - -1 - ERROR: item was not found - /// - int is_open(const char *path) const { - const Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(item->is_open()?1:0); - } - /// See if the specified \p item is closed. - /// - /// \param[in] item -- the item to be tested. Must not be NULL. - /// \returns - /// - 1 : item is open - /// - 0 : item is closed - /// - int is_close(Fl_Tree_Item *item) const { - return(item->is_close()); - } - /// See if item specified by \p path (eg: "Parent/child/item") is closed. - /// - /// 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 - /// - -1 - ERROR: item was not found - /// - int is_close(const char *path) const { - const Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(item->is_close()?1:0); - } - - /// Select the specified \p item. Use 'deselect()' to de-select it. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item to be selected. Must not be NULL. - /// \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, - /// callback_reason() will be FL_TREE_REASON_SELECTED - /// \returns - /// - 1 - item's state was changed - /// - 0 - item was already selected, no change was made - /// - int select(Fl_Tree_Item *item, int docallback=1) { - if ( ! item->is_selected() ) { - item->select(); - set_changed(); - if ( docallback ) { - do_callback_for_item(item, FL_TREE_REASON_SELECTED); - } - redraw(); - return(1); - } - return(0); - } - /// Select the item specified by \p path (eg: "Parent/child/item"). - /// 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 (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, - /// callback_reason() will be FL_TREE_REASON_SELECTED - /// \returns - /// - 1 : OK: item's state was changed - /// - 0 : OK: item was already selected, no change was made - /// - -1 : ERROR: item was not found - /// - int select(const char *path, int docallback=1) { - Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(select(item, docallback)); - } - /// Toggle the select state of the specified \p item. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item to be selected. Must not be NULL. - /// \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, callback_reason() will be - /// either FL_TREE_REASON_SELECTED or FL_TREE_REASON_DESELECTED - /// - void select_toggle(Fl_Tree_Item *item, int docallback=1) { - item->select_toggle(); - set_changed(); - if ( docallback ) { - do_callback_for_item(item, item->is_selected() ? FL_TREE_REASON_SELECTED - : FL_TREE_REASON_DESELECTED); - } - redraw(); - } - /// De-select the specified \p item. - /// Handles redrawing if anything was actually changed. - /// Invokes the callback depending on the value of optional parameter \p docallback. - /// - /// The callback can use callback_item() and callback_reason() respectively to determine - /// the item changed and the reason the callback was called. - /// - /// \param[in] item -- the item to be selected. Must not be NULL. - /// \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, - /// callback_reason() will be FL_TREE_REASON_DESELECTED - /// \returns - /// - 0 - item was already deselected, no change was made - /// - 1 - item's state was changed - /// - int deselect(Fl_Tree_Item *item, int docallback=1) { - if ( item->is_selected() ) { - item->deselect(); - set_changed(); - if ( docallback ) { - do_callback_for_item(item, FL_TREE_REASON_DESELECTED); - } - redraw(); - return(1); - } - return(0); - } - /// Deselect an item specified by \p path (eg: "Parent/child/item"). - /// 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 (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, - /// callback_reason() will be FL_TREE_REASON_DESELECTED - /// \returns - /// - 1 - OK: item's state was changed - /// - 0 - OK: item was already deselected, no change was made - /// - -1 - ERROR: item was not found - /// - int deselect(const char *path, int docallback=1) { - Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(deselect(item, docallback)); - } - + int open(Fl_Tree_Item *item, int docallback=1); + int open(const char *path, int docallback=1); + void open_toggle(Fl_Tree_Item *item, int docallback=1); + int close(Fl_Tree_Item *item, int docallback=1); + int close(const char *path, int docallback=1); + int is_open(Fl_Tree_Item *item) const; + int is_open(const char *path) const; + int is_close(Fl_Tree_Item *item) const; + int is_close(const char *path) const; + + ///////////////////////// + // Item selection methods + ///////////////////////// + int select(Fl_Tree_Item *item, int docallback=1); + int select(const char *path, int docallback=1); + void select_toggle(Fl_Tree_Item *item, int docallback=1); + int deselect(Fl_Tree_Item *item, int docallback=1); + int deselect(const char *path, int docallback=1); int deselect_all(Fl_Tree_Item *item=0, int docallback=1); int select_only(Fl_Tree_Item *selitem, int docallback=1); int select_all(Fl_Tree_Item *item=0, int docallback=1); - void set_item_focus(Fl_Tree_Item *o); - - /// See if the specified \p item is selected. - /// - /// \param[in] item -- the item to be tested. Must not be NULL. - /// - /// \return - /// - 1 : item selected - /// - 0 : item deselected - /// - int is_selected(Fl_Tree_Item *item) const { - return(item->is_selected()?1:0); - } - /// See if item specified by \p path (eg: "Parent/child/item") is selected. - /// - /// 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 - /// - -1 : item was not found - /// - int is_selected(const char *path) { - Fl_Tree_Item *item = find_item(path); - if ( ! item ) return(-1); - return(is_selected(item)); - } - /// Print the tree as 'ascii art' to stdout. - /// Used mainly for debugging. - /// - void show_self() { - if ( ! _root ) return; - _root->show_self(); - } - + void set_item_focus(Fl_Tree_Item *item); + int is_selected(Fl_Tree_Item *item) const; + int is_selected(const char *path); + ///////////////////////////////// // Item attribute related methods ///////////////////////////////// - - /// Get the default label fontsize used for creating new items. - Fl_Fontsize item_labelsize() const { - return(_prefs.labelsize()); - } - /// Set the default label font size used for creating new items. - /// To change the font size on a per-item basis, use Fl_Tree_Item::labelsize(Fl_Fontsize) - /// - void item_labelsize(Fl_Fontsize val) { - _prefs.labelsize(val); - } - /// Get the default font face used for creating new items. - Fl_Font item_labelfont() const { - return(_prefs.labelfont()); - } - /// Set the default font face used for creating new items. - /// To change the font face on a per-item basis, use Fl_Tree_Item::labelfont(Fl_Font) - /// - void item_labelfont(Fl_Font val) { - _prefs.labelfont(val); - } - /// Get the default label foreground color used for creating new items. - Fl_Color item_labelfgcolor(void) const { - return(_prefs.labelfgcolor()); - } - /// Set the default label foreground color used for creating new items. - /// To change the foreground color on a per-item basis, use Fl_Tree_Item::labelfgcolor(Fl_Color) - /// - void item_labelfgcolor(Fl_Color val) { - _prefs.labelfgcolor(val); - } - /// Get the default label background color used for creating new items. - Fl_Color item_labelbgcolor(void) const { - return(_prefs.labelbgcolor()); - } - /// Set the default label background color used for creating new items. - /// To change the background color on a per-item basis, use Fl_Tree_Item::labelbgcolor(Fl_Color) - /// - void item_labelbgcolor(Fl_Color val) { - _prefs.labelbgcolor(val); - } - /// Get the connector color used for tree connection lines. - Fl_Color connectorcolor() const { - return(_prefs.connectorcolor()); - } - /// Set the connector color used for tree connection lines. - void connectorcolor(Fl_Color val) { - _prefs.connectorcolor(val); - } - /// Get the amount of white space (in pixels) that should appear - /// between the widget's left border and the tree's contents. - /// - int marginleft() const { - return(_prefs.marginleft()); - } - /// Set the amount of white space (in pixels) that should appear - /// between the widget's left border and the left side of the tree's contents. - /// - void marginleft(int val) { - _prefs.marginleft(val); - redraw(); - } - /// Get the amount of white space (in pixels) that should appear - /// between the widget's top border and the top of the tree's contents. - /// - int margintop() const { - return(_prefs.margintop()); - } - /// Sets the amount of white space (in pixels) that should appear - /// between the widget's top border and the top of the tree's contents. - /// - void margintop(int val) { - _prefs.margintop(val); - redraw(); - } - /// Get the amount of white space (in pixels) that should appear - /// below an open child tree's contents. - /// - int openchild_marginbottom() const { - return(_prefs.openchild_marginbottom()); - } - /// Set the amount of white space (in pixels) that should appear - /// below an open child tree's contents. - /// - void openchild_marginbottom(int val) { - _prefs.openchild_marginbottom(val); - redraw(); - } - /// Gets the width of the horizontal connection lines (in pixels) - /// that appear to the left of each tree item's label. - /// - int connectorwidth() const { - return(_prefs.connectorwidth()); - } - /// Sets the width of the horizontal connection lines (in pixels) - /// that appear to the left of each tree item's label. - /// - void connectorwidth(int val) { - _prefs.connectorwidth(val); - redraw(); - } - /// Returns the Fl_Image being used as the default user icon for all - /// newly created items. - /// Returns zero if no icon has been set, which is the default. - /// - Fl_Image *usericon() const { - return(_prefs.usericon()); - } - /// Sets the Fl_Image to be used as the default user icon for all - /// newly created items. - /// - /// If you want to specify user icons on a per-item basis, - /// use Fl_Tree_Item::usericon() instead. - /// - /// \param[in] val -- The new image to be used, or - /// zero to disable user icons. - /// - void usericon(Fl_Image *val) { - _prefs.usericon(val); - redraw(); - } - /// Returns the icon to be used as the 'open' icon. - /// If none was set, the internal default is returned, - /// a simple '[+]' icon. - /// - Fl_Image *openicon() const { - return(_prefs.openicon()); - } - /// Sets the icon to be used as the 'open' icon. - /// This overrides the built in default '[+]' icon. - /// - /// \param[in] val -- The new image, or zero to use the default [+] icon. - /// - void openicon(Fl_Image *val) { - _prefs.openicon(val); - redraw(); - } - /// Returns the icon to be used as the 'close' icon. - /// If none was set, the internal default is returned, - /// a simple '[-]' icon. - /// - Fl_Image *closeicon() const { - return(_prefs.closeicon()); - } - /// Sets the icon to be used as the 'close' icon. - /// This overrides the built in default '[-]' icon. - /// - /// \param[in] val -- The new image, or zero to use the default [-] icon. - /// - void closeicon(Fl_Image *val) { - _prefs.closeicon(val); - redraw(); - } - /// Returns 1 if the collapse icon is enabled, 0 if not. - int showcollapse() const { - return(_prefs.showcollapse()); - } - /// Set if we should show the collapse icon or not. - /// If collapse icons are disabled, the user will not be able - /// to interactively collapse items in the tree, unless the application - /// provides some other means via open() and close(). - /// - /// \param[in] val 1: shows collapse icons (default),\n - /// 0: hides collapse icons. - /// - void showcollapse(int val) { - _prefs.showcollapse(val); - redraw(); - } - /// Returns 1 if the root item is to be shown, or 0 if not. - int showroot() const { - return(_prefs.showroot()); - } - /// Set if the root item should be shown or not. - /// \param[in] val 1 -- show the root item (default)\n - /// 0 -- hide the root item. - /// - void showroot(int val) { - _prefs.showroot(val); - redraw(); - } - /// Returns the line drawing style for inter-connecting items. - Fl_Tree_Connector connectorstyle() const { - return(_prefs.connectorstyle()); - } - /// Sets the line drawing style for inter-connecting items. - void connectorstyle(Fl_Tree_Connector val) { - _prefs.connectorstyle(val); - redraw(); - } - /// Set the default sort order used when items are added to the tree. - /// See Fl_Tree_Sort for possible values. - /// - Fl_Tree_Sort sortorder() const { - return(_prefs.sortorder()); - } - /// Gets the sort order used to add items to the tree. - void sortorder(Fl_Tree_Sort val) { - _prefs.sortorder(val); - // no redraw().. only affects new add()itions - } - /// Sets the style of box used to draw selected items. - /// This is an fltk Fl_Boxtype. - /// The default is influenced by FLTK's current Fl::scheme() - /// - Fl_Boxtype selectbox() const { - return(_prefs.selectbox()); - } - /// Gets the style of box used to draw selected items. - /// This is an fltk Fl_Boxtype. - /// The default is influenced by FLTK's current Fl::scheme() - /// - void selectbox(Fl_Boxtype val) { - _prefs.selectbox(val); - redraw(); - } - /// Gets the tree's current selection mode. - Fl_Tree_Select selectmode() const { - return(_prefs.selectmode()); - } - /// Sets the tree's selection mode. - void selectmode(Fl_Tree_Select val) { - _prefs.selectmode(val); - } + Fl_Fontsize item_labelsize() const; + void item_labelsize(Fl_Fontsize val); + Fl_Font item_labelfont() const; + void item_labelfont(Fl_Font val); + Fl_Color item_labelfgcolor(void) const; + void item_labelfgcolor(Fl_Color val); + Fl_Color item_labelbgcolor(void) const; + void item_labelbgcolor(Fl_Color val); + Fl_Color connectorcolor() const; + void connectorcolor(Fl_Color val); + int marginleft() const; + void marginleft(int val); + int margintop() const; + void margintop(int val); + int openchild_marginbottom() const; + void openchild_marginbottom(int val); + int connectorwidth() const; + void connectorwidth(int val); + Fl_Image* usericon() const; + void usericon(Fl_Image *val); + Fl_Image* openicon() const; + void openicon(Fl_Image *val); + Fl_Image* closeicon() const; + void closeicon(Fl_Image *val); + int showcollapse() const; + void showcollapse(int val); + int showroot() const; + void showroot(int val); + Fl_Tree_Connector connectorstyle() const; + void connectorstyle(Fl_Tree_Connector val); + Fl_Tree_Sort sortorder() const; + void sortorder(Fl_Tree_Sort val); + Fl_Boxtype selectbox() const; + void selectbox(Fl_Boxtype val); + Fl_Tree_Select selectmode() const; + void selectmode(Fl_Tree_Select val); + int displayed(Fl_Tree_Item *item); void show_item(Fl_Tree_Item *item, int yoff); void show_item(Fl_Tree_Item *item); - void show_item_bottom(Fl_Tree_Item *item); - void show_item_middle(Fl_Tree_Item *item); void show_item_top(Fl_Tree_Item *item); + void show_item_middle(Fl_Tree_Item *item); + void show_item_bottom(Fl_Tree_Item *item); void display(Fl_Tree_Item *item); int vposition() const; - void vposition(int ypos); + void vposition(int pos); - /// See if widget \p w is one of the Fl_Tree widget's scrollbars. - /// Use this to skip over the scrollbars when walking the child() array. Example: - /// \code - /// for ( int i=0; ichildren(); i++ ) { // walk children - /// Fl_Widget *w= tree->child(i); - /// if ( brow->is_scrollbar(w) ) continue; // skip scrollbars - /// ..do work here.. - /// } - /// \endcode - /// \param[in] w Widget to test - /// \returns 1 if \p w is a scrollbar, 0 if not. - /// - int is_scrollbar(Fl_Widget *w) { - return( ( w == _vscroll ) ? 1 : 0 ); - } - /// Gets the current size of the scrollbars' troughs, in pixels. - /// - /// If this value is zero (default), this widget will use the global - /// Fl::scrollbar_size() value as the scrollbar's width. - /// - /// \returns Scrollbar size in pixels, or 0 if the global Fl::scrollsize() is being used. - /// \see Fl::scrollbar_size(int) - /// - int scrollbar_size() const { - return(_scrollbar_size); - } - /// Sets the pixel size of the scrollbars' troughs to the \p size, in pixels. - /// - /// Normally you should not need this method, and should use the global - /// Fl::scrollbar_size(int) instead to manage the size of ALL - /// your widgets' scrollbars. This ensures your application - /// has a consistent UI, is the default behavior, and is normally - /// what you want. - /// - /// Only use THIS method if you really need to override the global - /// scrollbar size. The need for this should be rare. - /// - /// Setting \p size to the special value of 0 causes the widget to - /// track the global Fl::scrollbar_size(), which is the default. - /// - /// \param[in] size Sets the scrollbar size in pixels.\n - /// If 0 (default), scrollbar size tracks the global Fl::scrollbar_size() - /// \see Fl::scrollbar_size() - /// - void scrollbar_size(int size) { - _scrollbar_size = size; - int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); - if ( _vscroll->w() != scrollsize ) { - _vscroll->resize(x()+w()-scrollsize, h(), scrollsize, _vscroll->h()); - } - } + int is_scrollbar(Fl_Widget *w); + int scrollbar_size() const; + void scrollbar_size(int size); /////////////////////// // callback related /////////////////////// - - /// Sets the item that was changed for this callback. - /// Used internally to pass the item that invoked the callback. - /// - void callback_item(Fl_Tree_Item* item) { - _callback_item = item; - } - /// Gets the item that caused the callback. - /// The callback() can use this value to see which item changed. - /// - Fl_Tree_Item* callback_item() { - return(_callback_item); - } - /// Sets the reason for this callback. - /// Used internally to pass the reason the callback was invoked. - /// - void callback_reason(Fl_Tree_Reason reason) { - _callback_reason = reason; - } - /// Gets the reason for this callback. - /// - /// The callback() can use this value to see why it was called. Example: - /// \code - /// void MyTreeCallback(Fl_Widget *w, void *userdata) { - /// Fl_Tree *tree = (Fl_Tree*)w; - /// Fl_Tree_Item *item = tree->callback_item(); // the item changed (can be NULL if more than one item was changed!) - /// switch ( tree->callback_reason() ) { // reason callback was invoked - /// case FL_TREE_REASON_OPENED: ..item was opened.. - /// case FL_TREE_REASON_CLOSED: ..item was closed.. - /// case FL_TREE_REASON_SELECTED: ..item was selected.. - /// case FL_TREE_REASON_DESELECTED: ..item was deselected.. - /// } - /// } - /// \endcode - /// - Fl_Tree_Reason callback_reason() const { - return(_callback_reason); - } + void callback_item(Fl_Tree_Item* item); + Fl_Tree_Item* callback_item(); + void callback_reason(Fl_Tree_Reason reason); + Fl_Tree_Reason callback_reason() const; /// Load FLTK preferences void load(class Fl_Preferences&); diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx index e95e183af..5548bba68 100644 --- a/src/Fl_Tree.cxx +++ b/src/Fl_Tree.cxx @@ -114,384 +114,6 @@ Fl_Tree::~Fl_Tree() { if ( _root ) { delete _root; _root = 0; } } -/// 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) { - if ( ! _root ) { // Create root if none - _root = new Fl_Tree_Item(_prefs); - _root->parent(0); - _root->label("ROOT"); - } - char **arr = parse_path(path); - Fl_Tree_Item *item = _root->add(_prefs, arr); - free_path(arr); - return(item); -} - -/// Inserts a new item above the specified Fl_Tree_Item, with the label set to 'name'. -/// \param[in] above -- the item above which to insert the new item. Must not be NULL. -/// \param[in] name -- the name of the new item -/// \returns the item that was added, or 0 if 'above' could not be found. -/// -Fl_Tree_Item* Fl_Tree::insert_above(Fl_Tree_Item *above, const char *name) { - return(above->insert_above(_prefs, name)); -} - -/// Insert a new item into a tree-item's children at a specified position. -/// -/// \param[in] item The existing item to insert new child into. Must not be NULL. -/// \param[in] name The label for the new item -/// \param[in] pos The position of the new item in the child list -/// \returns the item that was added. -/// -Fl_Tree_Item* Fl_Tree::insert(Fl_Tree_Item *item, const char *name, int pos) { - return(item->insert(_prefs, name, pos)); -} - -/// Add a new child to a tree-item. -/// -/// \param[in] item The existing item to add new child to. Must not be NULL. -/// \param[in] name The label for the new item -/// \returns the item that was added. -/// -Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) { - return(item->add(_prefs, 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. -/// -/// 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() -/// -Fl_Tree_Item *Fl_Tree::find_item(const char *path) { - if ( ! _root ) return(NULL); - char **arr = parse_path(path); - Fl_Tree_Item *item = _root->find_item(arr); - free_path(arr); - return(item); -} - -/// A const version of Fl_Tree::find_item(const char *path) -const Fl_Tree_Item *Fl_Tree::find_item(const char *path) const { - if ( ! _root ) return(NULL); - char **arr = parse_path(path); - const Fl_Tree_Item *item = _root->find_item(arr); - free_path(arr); - return(item); -} - -// Handle safe 'reverse string concatenation'. -// In the following we build the pathname from right-to-left, -// since we start at the child and work our way up to the root. -// -#define SAFE_RCAT(c) { \ - slen += 1; if ( slen >= pathnamelen ) { pathname[0] = '\0'; return(-2); } \ - *s-- = c; \ - } - -/// 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. -/// \returns -/// - 0 : OK (\p pathname returns the item's pathname) -/// - -1 : item not found (pathname="") -/// - -2 : pathname not large enough (pathname="") -/// \see find_item() -/// -int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item *item) const { - pathname[0] = '\0'; - item = item ? item : _root; - if ( !item ) return(-1); - // Build pathname starting at end - char *s = (pathname+pathnamelen-1); - int slen = 0; // length of string compiled so far (including NULL) - SAFE_RCAT('\0'); - while ( item ) { - if ( item->is_root() && showroot() == 0 ) break; // don't include root in path if showroot() off - // Find name of current 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 - 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) - } - if ( *(++s) == '/' ) ++s; // leave off leading slash from pathname - if ( s != pathname ) memmove(pathname, s, slen); // Shift down right-aligned string - return(0); -} - -/// Standard FLTK draw() method, handles draws the tree widget. -void Fl_Tree::draw() { - // Let group draw box+label but *NOT* children. - // We handle drawing children ourselves by calling each item's draw() - // - // Handle group's bg - Fl_Group::draw_box(); - Fl_Group::draw_label(); - // Handle tree - if ( ! _root ) return; - int cx = x() + Fl::box_dx(box()); - int cy = y() + Fl::box_dy(box()); - int cw = w() - Fl::box_dw(box()); - int ch = h() - Fl::box_dh(box()); - // These values are changed during drawing - // 'Y' will be the lowest point on the tree - int X = cx + _prefs.marginleft(); - int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0); - int W = cw - _prefs.marginleft(); // - _prefs.marginright(); - int Ysave = Y; - fl_push_clip(cx,cy,cw,ch); - { - fl_font(_prefs.labelfont(), _prefs.labelsize()); - _root->draw(X, Y, W, this, - (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus - _prefs); - } - fl_pop_clip(); - - // Show vertical scrollbar? - int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree - 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())); - - if ( ytoofar > 0 ) ydiff += ytoofar; - if ( Ysave ch || int(_vscroll->value()) > 1 ) { - _vscroll->visible(); - - int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); - int sx = x()+w()-Fl::box_dx(box())-scrollsize; - int sy = y()+Fl::box_dy(box()); - int sw = scrollsize; - int sh = h()-Fl::box_dh(box()); - _vscroll->show(); - _vscroll->range(0.0,ydiff-ch); - _vscroll->resize(sx,sy,sw,sh); - _vscroll->slider_size(float(ch)/float(ydiff)); - } else { - _vscroll->Fl_Slider::value(0); - _vscroll->hide(); - } - fl_push_clip(cx,cy,cw,ch); - Fl_Group::draw_children(); // draws any FLTK children set via Fl_Tree::widget() - fl_pop_clip(); -} - -/// Returns next visible item above (dir==Fl_Up) or below (dir==Fl_Down) the specified \p item. -/// If \p item is 0, returns first() if \p dir is Fl_Up, or last() if \p dir is FL_Down. -/// -/// \param[in] item The item above/below which we'll find the next visible item -/// \param[in] dir The direction to search. Can be FL_Up or FL_Down. -/// \returns The item found, or 0 if there's no visible items above/below the specified \p item. -/// -Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) { - if ( ! item ) { // no start item? - item = ( dir == FL_Up ) ? last() : first(); // start at top or bottom - if ( ! item ) return(0); - if ( item->visible_r() ) return(item); // return first/last visible item - } - switch ( dir ) { - case FL_Up: return(item->prev_displayed(_prefs)); - case FL_Down: return(item->next_displayed(_prefs)); - default: return(item->next_displayed(_prefs)); - } -} - -/// Set the item that currently should have keyboard focus. -/// Handles calling redraw() to update the focus box (if it is visible). -/// -/// \param[in] item The item that should take focus. If NULL, none will have focus. -/// -void Fl_Tree::set_item_focus(Fl_Tree_Item *item) { - if ( _item_focus != item ) { // changed? - _item_focus = item; // update - if ( visible_focus() ) redraw(); // redraw to update focus box - } -} - -/// Find the item that was clicked. -/// You should use callback_item() instead, which is fast, -/// and is meant to be used within a callback to determine the item clicked. -/// -/// This method walks the entire tree looking for the first item that is -/// under the mouse (ie. at Fl::event_x()/Fl:event_y(). -/// -/// Use this method /only/ if you've subclassed Fl_Tree, and are receiving -/// events before Fl_Tree has been able to process and update callback_item(). -/// -/// \returns the item clicked, or 0 if no item was under the current event. -/// -const Fl_Tree_Item* Fl_Tree::find_clicked() const { - if ( ! _root ) return(NULL); - return(_root->find_clicked(_prefs)); -} - -/// Set the item that was last clicked. -/// Should only be used by subclasses needing to change this value. -/// Normally Fl_Tree manages this value. -/// -/// Deprecated: use callback_item() instead. -/// -void Fl_Tree::item_clicked(Fl_Tree_Item* val) { - _callback_item = val; -} - -/// Returns the first item in the tree. -/// -/// Use this to walk the tree in the forward direction, eg: -/// \code -/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \returns first item in tree, or 0 if none (tree empty). -/// \see first(),next(),last(),prev() -/// -Fl_Tree_Item* Fl_Tree::first() { - return(_root); // first item always root -} - -/// Return the next item after \p item, or 0 if no more items. -/// -/// Use this code to walk the entire tree: -/// \code -/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \param[in] item The item to use to find the next item. If NULL, returns 0. -/// \returns Next item in tree, or 0 if at last item. -/// -/// \see first(),next(),last(),prev() -/// -Fl_Tree_Item *Fl_Tree::next(Fl_Tree_Item *item) { - if ( ! item ) return(0); - return(item->next()); -} - -/// Return the previous item before \p item, or 0 if no more items. -/// -/// This can be used to walk the tree in reverse, eg: -/// -/// \code -/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->prev(item) ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \param[in] item The item to use to find the previous item. If NULL, returns 0. -/// \returns Previous item in tree, or 0 if at first item. -/// -/// \see first(),next(),last(),prev() -/// -Fl_Tree_Item *Fl_Tree::prev(Fl_Tree_Item *item) { - if ( ! item ) return(0); - return(item->prev()); -} - -/// Returns the last item in the tree. -/// -/// This can be used to walk the tree in reverse, eg: -/// -/// \code -/// for ( Fl_Tree_Item *item = tree->last(); item; item = tree->prev() ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \returns last item in the tree, or 0 if none (tree empty). -/// -/// \see first(),next(),last(),prev() -/// -Fl_Tree_Item* Fl_Tree::last() { - if ( ! _root ) return(0); - Fl_Tree_Item *item = _root; - while ( item->has_children() ) { - item = item->child(item->children()-1); - } - return(item); -} - -/// Returns the first selected item in the tree. -/// -/// Use this to walk the tree looking for all the selected items, eg: -/// -/// \code -/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \returns The next selected item, or 0 if there are no more selected items. -/// -Fl_Tree_Item *Fl_Tree::first_selected_item() { - return(next_selected_item(0)); -} - -/// Returns the next selected item after \p item. -/// If \p item is 0, search starts at the first item (root). -/// -/// Use this to walk the tree looking for all the selected items, eg: -/// \code -/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) { -/// printf("Item: %s\n", item->label()); -/// } -/// \endcode -/// -/// \param[in] item The item to use to find the next selected item. If NULL, first() is used. -/// \returns The next selected item, or 0 if there are no more selected items. -/// -Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) { - if ( ! item ) { - if ( ! (item = first()) ) return(0); - if ( item->is_selected() ) return(item); - } - while ( (item = item->next()) ) - if ( item->is_selected() ) - return(item); - return(0); -} - /// Standard FLTK event handler for this widget. int Fl_Tree::handle(int e) { int ret = 0; @@ -705,6 +327,773 @@ int Fl_Tree::handle(int e) { return(ret); } +/// Standard FLTK draw() method, handles draws the tree widget. +void Fl_Tree::draw() { + // Let group draw box+label but *NOT* children. + // We handle drawing children ourselves by calling each item's draw() + // + // Handle group's bg + Fl_Group::draw_box(); + Fl_Group::draw_label(); + // Handle tree + if ( ! _root ) return; + int cx = x() + Fl::box_dx(box()); + int cy = y() + Fl::box_dy(box()); + int cw = w() - Fl::box_dw(box()); + int ch = h() - Fl::box_dh(box()); + // These values are changed during drawing + // 'Y' will be the lowest point on the tree + int X = cx + _prefs.marginleft(); + int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0); + int W = cw - _prefs.marginleft(); // - _prefs.marginright(); + int Ysave = Y; + fl_push_clip(cx,cy,cw,ch); + { + fl_font(_prefs.labelfont(), _prefs.labelsize()); + _root->draw(X, Y, W, this, + (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus + _prefs); + } + fl_pop_clip(); + + // Show vertical scrollbar? + int ydiff = (Y+_prefs.margintop())-Ysave; // ydiff=size of tree + 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())); + + if ( ytoofar > 0 ) ydiff += ytoofar; + if ( Ysave ch || int(_vscroll->value()) > 1 ) { + _vscroll->visible(); + + int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); + int sx = x()+w()-Fl::box_dx(box())-scrollsize; + int sy = y()+Fl::box_dy(box()); + int sw = scrollsize; + int sh = h()-Fl::box_dh(box()); + _vscroll->show(); + _vscroll->range(0.0,ydiff-ch); + _vscroll->resize(sx,sy,sw,sh); + _vscroll->slider_size(float(ch)/float(ydiff)); + } else { + _vscroll->Fl_Slider::value(0); + _vscroll->hide(); + } + fl_push_clip(cx,cy,cw,ch); + Fl_Group::draw_children(); // draws any FLTK children set via Fl_Tree::widget() + fl_pop_clip(); +} + +/// Print the tree as 'ascii art' to stdout. +/// Used mainly for debugging. +/// +void Fl_Tree::show_self() { + if ( ! _root ) return; + _root->show_self(); +} + +/// Set the label for the root item. +/// +/// Makes an internally managed copy of 'new_label'. +/// +void Fl_Tree::root_label(const char *new_label) { + if ( ! _root ) return; + _root->label(new_label); +} + +/// Returns the root item. +Fl_Tree_Item* Fl_Tree::root() { + return(_root); +} + +/// 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) { + if ( ! _root ) { // Create root if none + _root = new Fl_Tree_Item(_prefs); + _root->parent(0); + _root->label("ROOT"); + } + char **arr = parse_path(path); + Fl_Tree_Item *item = _root->add(_prefs, arr); + free_path(arr); + return(item); +} + +/// Add a new child to a tree-item. +/// +/// \param[in] item The existing item to add new child to. Must not be NULL. +/// \param[in] name The label for the new item +/// \returns the item that was added. +/// +Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *item, const char *name) { + return(item->add(_prefs, name)); +} + +/// Inserts a new item above the specified Fl_Tree_Item, with the label set to 'name'. +/// \param[in] above -- the item above which to insert the new item. Must not be NULL. +/// \param[in] name -- the name of the new item +/// \returns the item that was added, or 0 if 'above' could not be found. +/// +Fl_Tree_Item* Fl_Tree::insert_above(Fl_Tree_Item *above, const char *name) { + return(above->insert_above(_prefs, name)); +} + +/// Insert a new item into a tree-item's children at a specified position. +/// +/// \param[in] item The existing item to insert new child into. Must not be NULL. +/// \param[in] name The label for the new item +/// \param[in] pos The position of the new item in the child list +/// \returns the item that was added. +/// +Fl_Tree_Item* Fl_Tree::insert(Fl_Tree_Item *item, const char *name, int pos) { + return(item->insert(_prefs, name, pos)); +} + +/// Remove the specified \p item from the tree. +/// \p item may not be NULL. +/// If it has children, all those are removed too. +/// \returns 0 if done, -1 if 'item' not found. +/// +int Fl_Tree::remove(Fl_Tree_Item *item) { + if ( item == _root ) { + clear(); + } else { + Fl_Tree_Item *parent = item->parent(); // find item's parent + if ( ! parent ) return(-1); + parent->remove_child(item); // remove child + children + } + return(0); +} + +/// Clear all children from the tree. +/// The tree will be left completely empty. +/// +void Fl_Tree::clear() { + if ( ! _root ) return; + _root->clear_children(); + delete _root; _root = 0; +} +/// Clear all the children of a particular node in the tree specified by \p item. +/// Item may not be NULL. +/// +void Fl_Tree::clear_children(Fl_Tree_Item *item) { + if ( item->has_children() ) { + item->clear_children(); + redraw(); // redraw only if there were children to clear + } +} + +/// 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. +/// +/// 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() +/// +Fl_Tree_Item *Fl_Tree::find_item(const char *path) { + if ( ! _root ) return(NULL); + char **arr = parse_path(path); + Fl_Tree_Item *item = _root->find_item(arr); + free_path(arr); + return(item); +} + +/// A const version of Fl_Tree::find_item(const char *path) +const Fl_Tree_Item *Fl_Tree::find_item(const char *path) const { + if ( ! _root ) return(NULL); + char **arr = parse_path(path); + const Fl_Tree_Item *item = _root->find_item(arr); + free_path(arr); + return(item); +} + +// Handle safe 'reverse string concatenation'. +// In the following we build the pathname from right-to-left, +// since we start at the child and work our way up to the root. +// +#define SAFE_RCAT(c) { \ + slen += 1; if ( slen >= pathnamelen ) { pathname[0] = '\0'; return(-2); } \ + *s-- = c; \ + } + +/// 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. +/// \returns +/// - 0 : OK (\p pathname returns the item's pathname) +/// - -1 : item not found (pathname="") +/// - -2 : pathname not large enough (pathname="") +/// \see find_item() +/// +int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item *item) const { + pathname[0] = '\0'; + item = item ? item : _root; + if ( !item ) return(-1); + // Build pathname starting at end + char *s = (pathname+pathnamelen-1); + int slen = 0; // length of string compiled so far (including NULL) + SAFE_RCAT('\0'); + while ( item ) { + if ( item->is_root() && showroot() == 0 ) break; // don't include root in path if showroot() off + // Find name of current 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 + 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) + } + if ( *(++s) == '/' ) ++s; // leave off leading slash from pathname + if ( s != pathname ) memmove(pathname, s, slen); // Shift down right-aligned string + return(0); +} + +/// Find the item that was clicked. +/// You should use callback_item() instead, which is fast, +/// and is meant to be used within a callback to determine the item clicked. +/// +/// This method walks the entire tree looking for the first item that is +/// under the mouse (ie. at Fl::event_x()/Fl:event_y(). +/// +/// Use this method /only/ if you've subclassed Fl_Tree, and are receiving +/// events before Fl_Tree has been able to process and update callback_item(). +/// +/// \returns the item clicked, or 0 if no item was under the current event. +/// +const Fl_Tree_Item* Fl_Tree::find_clicked() const { + if ( ! _root ) return(NULL); + return(_root->find_clicked(_prefs)); +} + +/// Set the item that was last clicked. +/// Should only be used by subclasses needing to change this value. +/// Normally Fl_Tree manages this value. +/// +/// Deprecated: use callback_item() instead. +/// +void Fl_Tree::item_clicked(Fl_Tree_Item* val) { + _callback_item = val; +} + +/// Return the item that was last clicked. +/// +/// Valid only from within the callback(). +/// +/// Deprecated: use callback_item() instead. +/// +/// \returns the item clicked, or 0 if none. +/// 0 may also be used to indicate several items were clicked/changed. +/// +Fl_Tree_Item* Fl_Tree::item_clicked() { + return(_callback_item); +} + +/// Returns next visible item above (dir==Fl_Up) or below (dir==Fl_Down) the specified \p item. +/// If \p item is 0, returns first() if \p dir is Fl_Up, or last() if \p dir is FL_Down. +/// +/// \param[in] item The item above/below which we'll find the next visible item +/// \param[in] dir The direction to search. Can be FL_Up or FL_Down. +/// \returns The item found, or 0 if there's no visible items above/below the specified \p item. +/// +Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) { + if ( ! item ) { // no start item? + item = ( dir == FL_Up ) ? last() : first(); // start at top or bottom + if ( ! item ) return(0); + if ( item->visible_r() ) return(item); // return first/last visible item + } + switch ( dir ) { + case FL_Up: return(item->prev_displayed(_prefs)); + case FL_Down: return(item->next_displayed(_prefs)); + default: return(item->next_displayed(_prefs)); + } +} + +/// Returns the first item in the tree. +/// +/// Use this to walk the tree in the forward direction, eg: +/// \code +/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \returns first item in tree, or 0 if none (tree empty). +/// \see first(),next(),last(),prev() +/// +Fl_Tree_Item* Fl_Tree::first() { + return(_root); // first item always root +} + +/// Return the next item after \p item, or 0 if no more items. +/// +/// Use this code to walk the entire tree: +/// \code +/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \param[in] item The item to use to find the next item. If NULL, returns 0. +/// \returns Next item in tree, or 0 if at last item. +/// +/// \see first(),next(),last(),prev() +/// +Fl_Tree_Item *Fl_Tree::next(Fl_Tree_Item *item) { + if ( ! item ) return(0); + return(item->next()); +} + +/// Return the previous item before \p item, or 0 if no more items. +/// +/// This can be used to walk the tree in reverse, eg: +/// +/// \code +/// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->prev(item) ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \param[in] item The item to use to find the previous item. If NULL, returns 0. +/// \returns Previous item in tree, or 0 if at first item. +/// +/// \see first(),next(),last(),prev() +/// +Fl_Tree_Item *Fl_Tree::prev(Fl_Tree_Item *item) { + if ( ! item ) return(0); + return(item->prev()); +} + +/// Returns the last item in the tree. +/// +/// This can be used to walk the tree in reverse, eg: +/// +/// \code +/// for ( Fl_Tree_Item *item = tree->last(); item; item = tree->prev() ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \returns last item in the tree, or 0 if none (tree empty). +/// +/// \see first(),next(),last(),prev() +/// +Fl_Tree_Item* Fl_Tree::last() { + if ( ! _root ) return(0); + Fl_Tree_Item *item = _root; + while ( item->has_children() ) { + item = item->child(item->children()-1); + } + return(item); +} + +/// Returns the first selected item in the tree. +/// +/// Use this to walk the tree looking for all the selected items, eg: +/// +/// \code +/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \returns The next selected item, or 0 if there are no more selected items. +/// +Fl_Tree_Item *Fl_Tree::first_selected_item() { + return(next_selected_item(0)); +} + +/// Returns the next selected item after \p item. +/// If \p item is 0, search starts at the first item (root). +/// +/// Use this to walk the tree looking for all the selected items, eg: +/// \code +/// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) { +/// printf("Item: %s\n", item->label()); +/// } +/// \endcode +/// +/// \param[in] item The item to use to find the next selected item. If NULL, first() is used. +/// \returns The next selected item, or 0 if there are no more selected items. +/// +Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) { + if ( ! item ) { + if ( ! (item = first()) ) return(0); + if ( item->is_selected() ) return(item); + } + while ( (item = item->next()) ) + if ( item->is_selected() ) + return(item); + return(0); +} + +/// Open the specified 'item'. +/// This causes the item's children (if any) to be shown. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be opened. Must not be NULL. +/// \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, +/// callback_reason() will be FL_TREE_REASON_OPENED +/// \returns +/// - 1 -- item was opened +/// - 0 -- item was already open, no change +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::open(Fl_Tree_Item *item, int docallback) { + if ( item->is_open() ) return(0); + item->open(); + redraw(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_OPENED); + } + return(1); +} + +/// Opens the item specified by \p path (eg: "Parent/child/item"). +/// This causes the item's children (if any) to be shown. +/// 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 (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, +/// callback_reason() will be FL_TREE_REASON_OPENED +/// \returns +/// - 1 -- OK: item opened +/// - 0 -- OK: item was already open, no change +/// - -1 -- ERROR: item was not found +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::open(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(open(item, docallback)); +} + +/// Toggle the open state of \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item whose open state is to be toggled. Must not be NULL. +/// \param[in] docallback -- A flag that determines if the callback() is invoked or not: +/// - 0 - callback() is not invoked +/// - 1 - callback() is invoked, callback_reason() will be either +/// FL_TREE_REASON_OPENED or FL_TREE_REASON_CLOSED +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +void Fl_Tree::open_toggle(Fl_Tree_Item *item, int docallback) { + if ( item->is_open() ) { + close(item, docallback); + } else { + open(item, docallback); + } +} + +/// Closes the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be closed. Must not be NULL. +/// \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, +/// callback_reason() will be FL_TREE_REASON_CLOSED +/// \returns +/// - 1 -- item was closed +/// - 0 -- item was already closed, no change +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::close(Fl_Tree_Item *item, int docallback) { + if ( item->is_close() ) return(0); + item->close(); + redraw(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_CLOSED); + } + return(1); +} + +/// Closes the item specified by \p path, eg: "Parent/child/item". +/// 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 (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, +/// callback_reason() will be FL_TREE_REASON_CLOSED +/// \returns +/// - 1 -- OK: item closed +/// - 0 -- OK: item was already closed, no change +/// - -1 -- ERROR: item was not found +/// +/// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason() +/// +int Fl_Tree::close(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(close(item, docallback)); +} + +/// See if \p item is open. +/// +/// Items that are 'open' are themselves not necessarily visible; +/// one of the item's parents might be closed. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// \returns +/// - 1 : item is open +/// - 0 : item is closed +/// +int Fl_Tree::is_open(Fl_Tree_Item *item) const { + return(item->is_open()?1:0); +} + +/// 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 (e.g. "Flintstones/Fred") +/// \returns +/// - 1 - OK: item is open +/// - 0 - OK: item is closed +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::is_open(const char *path) const { + const Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(item->is_open()?1:0); +} + +/// See if the specified \p item is closed. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// \returns +/// - 1 : item is open +/// - 0 : item is closed +/// +int Fl_Tree::is_close(Fl_Tree_Item *item) const { + return(item->is_close()); +} + +/// See if item specified by \p path (eg: "Parent/child/item") is closed. +/// +/// 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 +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::is_close(const char *path) const { + const Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(item->is_close()?1:0); +} + +/// Select the specified \p item. Use 'deselect()' to de-select it. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \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, +/// callback_reason() will be FL_TREE_REASON_SELECTED +/// \returns +/// - 1 - item's state was changed +/// - 0 - item was already selected, no change was made +/// +int Fl_Tree::select(Fl_Tree_Item *item, int docallback) { + if ( ! item->is_selected() ) { + item->select(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_SELECTED); + } + redraw(); + return(1); + } + return(0); +} + +/// Select the item specified by \p path (eg: "Parent/child/item"). +/// 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 (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, +/// callback_reason() will be FL_TREE_REASON_SELECTED +/// \returns +/// - 1 : OK: item's state was changed +/// - 0 : OK: item was already selected, no change was made +/// - -1 : ERROR: item was not found +/// +int Fl_Tree::select(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(select(item, docallback)); +} + +/// Toggle the select state of the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \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, callback_reason() will be +/// either FL_TREE_REASON_SELECTED or FL_TREE_REASON_DESELECTED +/// +void Fl_Tree::select_toggle(Fl_Tree_Item *item, int docallback) { + item->select_toggle(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, item->is_selected() ? FL_TREE_REASON_SELECTED + : FL_TREE_REASON_DESELECTED); + } + redraw(); +} + +/// De-select the specified \p item. +/// Handles redrawing if anything was actually changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item -- the item to be selected. Must not be NULL. +/// \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, +/// callback_reason() will be FL_TREE_REASON_DESELECTED +/// \returns +/// - 0 - item was already deselected, no change was made +/// - 1 - item's state was changed +/// +int Fl_Tree::deselect(Fl_Tree_Item *item, int docallback) { + if ( item->is_selected() ) { + item->deselect(); + set_changed(); + if ( docallback ) { + do_callback_for_item(item, FL_TREE_REASON_DESELECTED); + } + redraw(); + return(1); + } + return(0); +} + +/// Deselect an item specified by \p path (eg: "Parent/child/item"). +/// 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 (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, +/// callback_reason() will be FL_TREE_REASON_DESELECTED +/// \returns +/// - 1 - OK: item's state was changed +/// - 0 - OK: item was already deselected, no change was made +/// - -1 - ERROR: item was not found +/// +int Fl_Tree::deselect(const char *path, int docallback) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(deselect(item, docallback)); +} + /// Deselect \p item and all its children. /// If item is NULL, first() is used. /// Handles calling redraw() if anything was changed. @@ -737,37 +1126,6 @@ int Fl_Tree::deselect_all(Fl_Tree_Item *item, int docallback) { return(count); } -/// Select \p item and all its children. -/// If item is NULL, first() is used. -/// Handles calling redraw() if anything was changed. -/// Invokes the callback depending on the value of optional parameter \p docallback. -/// -/// The callback can use callback_item() and callback_reason() respectively to determine -/// the item changed and the reason the callback was called. -/// -/// \param[in] item The item that will be selected (along with all its children). -/// If NULL, first() is used. -/// \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 for each item that changed state, -/// callback_reason() will be FL_TREE_REASON_SELECTED -/// \returns count of how many items were actually changed to the selected state. -/// -int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) { - item = item ? item : first(); // NULL? use first() - if ( ! item ) return(0); - int count = 0; - // Select item - if ( !item->is_selected() ) - if ( select(item, docallback) ) - ++count; - // Select its children - for ( int t=0; tchildren(); t++ ) { - count += select_all(item->child(t), docallback); // recurse - } - return(count); -} - /// Select only the specified \p item, deselecting all others that might be selected. /// If item is 0, first() is used. /// Handles calling redraw() if anything was changed. @@ -803,6 +1161,352 @@ int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) { return(changed); } +/// Select \p item and all its children. +/// If item is NULL, first() is used. +/// Handles calling redraw() if anything was changed. +/// Invokes the callback depending on the value of optional parameter \p docallback. +/// +/// The callback can use callback_item() and callback_reason() respectively to determine +/// the item changed and the reason the callback was called. +/// +/// \param[in] item The item that will be selected (along with all its children). +/// If NULL, first() is used. +/// \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 for each item that changed state, +/// callback_reason() will be FL_TREE_REASON_SELECTED +/// \returns count of how many items were actually changed to the selected state. +/// +int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) { + item = item ? item : first(); // NULL? use first() + if ( ! item ) return(0); + int count = 0; + // Select item + if ( !item->is_selected() ) + if ( select(item, docallback) ) + ++count; + // Select its children + for ( int t=0; tchildren(); t++ ) { + count += select_all(item->child(t), docallback); // recurse + } + return(count); +} + +/// Set the item that currently should have keyboard focus. +/// Handles calling redraw() to update the focus box (if it is visible). +/// +/// \param[in] item The item that should take focus. If NULL, none will have focus. +/// +void Fl_Tree::set_item_focus(Fl_Tree_Item *item) { + if ( _item_focus != item ) { // changed? + _item_focus = item; // update + if ( visible_focus() ) redraw(); // redraw to update focus box + } +} + +/// See if the specified \p item is selected. +/// +/// \param[in] item -- the item to be tested. Must not be NULL. +/// +/// \return +/// - 1 : item selected +/// - 0 : item deselected +/// +int Fl_Tree::is_selected(Fl_Tree_Item *item) const { + return(item->is_selected()?1:0); +} + +/// See if item specified by \p path (eg: "Parent/child/item") is selected. +/// +/// 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 +/// - -1 : item was not found +/// +int Fl_Tree::is_selected(const char *path) { + Fl_Tree_Item *item = find_item(path); + if ( ! item ) return(-1); + return(is_selected(item)); +} + +/// Get the default label fontsize used for creating new items. +Fl_Fontsize Fl_Tree::item_labelsize() const { + return(_prefs.labelsize()); +} + +/// Set the default label font size used for creating new items. +/// To change the font size on a per-item basis, use Fl_Tree_Item::labelsize(Fl_Fontsize) +/// +void Fl_Tree::item_labelsize(Fl_Fontsize val) { + _prefs.labelsize(val); +} + +/// Get the default font face used for creating new items. +Fl_Font Fl_Tree::item_labelfont() const { + return(_prefs.labelfont()); +} + +/// Set the default font face used for creating new items. +/// To change the font face on a per-item basis, use Fl_Tree_Item::labelfont(Fl_Font) +/// +void Fl_Tree::item_labelfont(Fl_Font val) { + _prefs.labelfont(val); +} + +/// Get the default label foreground color used for creating new items. +Fl_Color Fl_Tree::item_labelfgcolor(void) const { + return(_prefs.labelfgcolor()); +} + +/// Set the default label foreground color used for creating new items. +/// To change the foreground color on a per-item basis, use Fl_Tree_Item::labelfgcolor(Fl_Color) +/// +void Fl_Tree::item_labelfgcolor(Fl_Color val) { + _prefs.labelfgcolor(val); +} + +/// Get the default label background color used for creating new items. +Fl_Color Fl_Tree::item_labelbgcolor(void) const { + return(_prefs.labelbgcolor()); +} + +/// Set the default label background color used for creating new items. +/// To change the background color on a per-item basis, use Fl_Tree_Item::labelbgcolor(Fl_Color) +/// +void Fl_Tree::item_labelbgcolor(Fl_Color val) { + _prefs.labelbgcolor(val); +} + +/// Get the connector color used for tree connection lines. +Fl_Color Fl_Tree::connectorcolor() const { + return(_prefs.connectorcolor()); +} + +/// Set the connector color used for tree connection lines. +void Fl_Tree::connectorcolor(Fl_Color val) { + _prefs.connectorcolor(val); +} + +/// Get the amount of white space (in pixels) that should appear +/// between the widget's left border and the tree's contents. +/// +int Fl_Tree::marginleft() const { + return(_prefs.marginleft()); +} + +/// Set the amount of white space (in pixels) that should appear +/// between the widget's left border and the left side of the tree's contents. +/// +void Fl_Tree::marginleft(int val) { + _prefs.marginleft(val); + redraw(); +} + +/// Get the amount of white space (in pixels) that should appear +/// between the widget's top border and the top of the tree's contents. +/// +int Fl_Tree::margintop() const { + return(_prefs.margintop()); +} + +/// Sets the amount of white space (in pixels) that should appear +/// between the widget's top border and the top of the tree's contents. +/// +void Fl_Tree::margintop(int val) { + _prefs.margintop(val); + redraw(); +} + +/// Get the amount of white space (in pixels) that should appear +/// below an open child tree's contents. +/// +int Fl_Tree::openchild_marginbottom() const { + return(_prefs.openchild_marginbottom()); +} + +/// Set the amount of white space (in pixels) that should appear +/// below an open child tree's contents. +/// +void Fl_Tree::openchild_marginbottom(int val) { + _prefs.openchild_marginbottom(val); + redraw(); +} + +/// Gets the width of the horizontal connection lines (in pixels) +/// that appear to the left of each tree item's label. +/// +int Fl_Tree::connectorwidth() const { + return(_prefs.connectorwidth()); +} + +/// Sets the width of the horizontal connection lines (in pixels) +/// that appear to the left of each tree item's label. +/// +void Fl_Tree::connectorwidth(int val) { + _prefs.connectorwidth(val); + redraw(); +} + +/// Returns the Fl_Image being used as the default user icon for all +/// newly created items. +/// Returns zero if no icon has been set, which is the default. +/// +Fl_Image* Fl_Tree::usericon() const { + return(_prefs.usericon()); +} + +/// Sets the Fl_Image to be used as the default user icon for all +/// newly created items. +/// +/// If you want to specify user icons on a per-item basis, +/// use Fl_Tree_Item::usericon() instead. +/// +/// \param[in] val -- The new image to be used, or +/// zero to disable user icons. +/// +void Fl_Tree::usericon(Fl_Image *val) { + _prefs.usericon(val); + redraw(); +} + +/// Returns the icon to be used as the 'open' icon. +/// If none was set, the internal default is returned, +/// a simple '[+]' icon. +/// +Fl_Image* Fl_Tree::openicon() const { + return(_prefs.openicon()); +} + +/// Sets the icon to be used as the 'open' icon. +/// This overrides the built in default '[+]' icon. +/// +/// \param[in] val -- The new image, or zero to use the default [+] icon. +/// +void Fl_Tree::openicon(Fl_Image *val) { + _prefs.openicon(val); + redraw(); +} + +/// Returns the icon to be used as the 'close' icon. +/// If none was set, the internal default is returned, +/// a simple '[-]' icon. +/// +Fl_Image* Fl_Tree::closeicon() const { + return(_prefs.closeicon()); +} + +/// Sets the icon to be used as the 'close' icon. +/// This overrides the built in default '[-]' icon. +/// +/// \param[in] val -- The new image, or zero to use the default [-] icon. +/// +void Fl_Tree::closeicon(Fl_Image *val) { + _prefs.closeicon(val); + redraw(); +} + +/// Returns 1 if the collapse icon is enabled, 0 if not. +int Fl_Tree::showcollapse() const { + return(_prefs.showcollapse()); +} + +/// Set if we should show the collapse icon or not. +/// If collapse icons are disabled, the user will not be able +/// to interactively collapse items in the tree, unless the application +/// provides some other means via open() and close(). +/// +/// \param[in] val 1: shows collapse icons (default),\n +/// 0: hides collapse icons. +/// +void Fl_Tree::showcollapse(int val) { + _prefs.showcollapse(val); + redraw(); +} + +/// Returns 1 if the root item is to be shown, or 0 if not. +int Fl_Tree::showroot() const { + return(_prefs.showroot()); +} + +/// Set if the root item should be shown or not. +/// \param[in] val 1 -- show the root item (default)\n +/// 0 -- hide the root item. +/// +void Fl_Tree::showroot(int val) { + _prefs.showroot(val); + redraw(); +} + +/// Returns the line drawing style for inter-connecting items. +Fl_Tree_Connector Fl_Tree::connectorstyle() const { + return(_prefs.connectorstyle()); +} + +/// Sets the line drawing style for inter-connecting items. +void Fl_Tree::connectorstyle(Fl_Tree_Connector val) { + _prefs.connectorstyle(val); + redraw(); +} + +/// Set the default sort order used when items are added to the tree. +/// See Fl_Tree_Sort for possible values. +/// +Fl_Tree_Sort Fl_Tree::sortorder() const { + return(_prefs.sortorder()); +} + +/// Gets the sort order used to add items to the tree. +void Fl_Tree::sortorder(Fl_Tree_Sort val) { + _prefs.sortorder(val); + // no redraw().. only affects new add()itions +} + +/// Sets the style of box used to draw selected items. +/// This is an fltk Fl_Boxtype. +/// The default is influenced by FLTK's current Fl::scheme() +/// +Fl_Boxtype Fl_Tree::selectbox() const { + return(_prefs.selectbox()); +} + +/// Gets the style of box used to draw selected items. +/// This is an fltk Fl_Boxtype. +/// The default is influenced by FLTK's current Fl::scheme() +/// +void Fl_Tree::selectbox(Fl_Boxtype val) { + _prefs.selectbox(val); + redraw(); +} + +/// Gets the tree's current selection mode. +Fl_Tree_Select Fl_Tree::selectmode() const { + return(_prefs.selectmode()); +} + +/// Sets the tree's selection mode. +void Fl_Tree::selectmode(Fl_Tree_Select val) { + _prefs.selectmode(val); +} + +/// See if \p item is currently displayed on-screen (visible within the widget). +/// This can be used to detect if the item is scrolled off-screen. +/// Checks to see if the item's vertical position is within the top and bottom +/// edges of the display window. This does NOT take into account the hide()/show() +/// or open()/close() status of the item. +/// +/// \param[in] item The item to be checked. If NULL, first() is used. +/// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree. +/// +int Fl_Tree::displayed(Fl_Tree_Item *item) { + item = item ? item : first(); + if (!item) return(0); + return( (item->y() >= y()) && (item->y() <= (y()+h()-item->h())) ? 1 : 0); +} + /// Adjust the vertical scroll bar so that \p item is visible /// \p yoff pixels from the top of the Fl_Tree widget's display. /// @@ -827,21 +1531,6 @@ void Fl_Tree::show_item(Fl_Tree_Item *item, int yoff) { redraw(); } -/// See if \p item is currently displayed on-screen (visible within the widget). -/// This can be used to detect if the item is scrolled off-screen. -/// Checks to see if the item's vertical position is within the top and bottom -/// edges of the display window. This does NOT take into account the hide()/show() -/// or open()/close() status of the item. -/// -/// \param[in] item The item to be checked. If NULL, first() is used. -/// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree. -/// -int Fl_Tree::displayed(Fl_Tree_Item *item) { - item = item ? item : first(); - if (!item) return(0); - return( (item->y() >= y()) && (item->y() <= (y()+h()-item->h())) ? 1 : 0); -} - /// Adjust the vertical scroll bar to show \p item at the 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. @@ -884,6 +1573,14 @@ void Fl_Tree::show_item_bottom(Fl_Tree_Item *item) { if (item) show_item(item, h()-item->h()); } +/// Displays \p item, scrolling the tree as necessary. +/// \param[in] item The item to be displayed. If NULL, first() is used. +/// +void Fl_Tree::display(Fl_Tree_Item *item) { + item = item ? item : first(); + if (item) show_item_middle(item); +} + /// Returns the vertical scroll position as a pixel offset. /// The position returned is how many pixels of the tree are scrolled off the top edge /// of the screen. Example: A position of '3' indicates the top 3 pixels of @@ -908,12 +1605,106 @@ void Fl_Tree::vposition(int pos) { redraw(); } -/// Displays \p item, scrolling the tree as necessary. -/// \param[in] item The item to be displayed. If NULL, first() is used. +/// See if widget \p w is one of the Fl_Tree widget's scrollbars. +/// Use this to skip over the scrollbars when walking the child() array. Example: +/// \code +/// for ( int i=0; ichildren(); i++ ) { // walk children +/// Fl_Widget *w= tree->child(i); +/// if ( brow->is_scrollbar(w) ) continue; // skip scrollbars +/// ..do work here.. +/// } +/// \endcode +/// \param[in] w Widget to test +/// \returns 1 if \p w is a scrollbar, 0 if not. /// -void Fl_Tree::display(Fl_Tree_Item *item) { - item = item ? item : first(); - if (item) show_item_middle(item); +int Fl_Tree::is_scrollbar(Fl_Widget *w) { + return( ( w == _vscroll ) ? 1 : 0 ); +} + +/// Gets the current size of the scrollbars' troughs, in pixels. +/// +/// If this value is zero (default), this widget will use the global +/// Fl::scrollbar_size() value as the scrollbar's width. +/// +/// \returns Scrollbar size in pixels, or 0 if the global Fl::scrollsize() is being used. +/// \see Fl::scrollbar_size(int) +/// +int Fl_Tree::scrollbar_size() const { + return(_scrollbar_size); +} + +/// Sets the pixel size of the scrollbars' troughs to the \p size, in pixels. +/// +/// Normally you should not need this method, and should use the global +/// Fl::scrollbar_size(int) instead to manage the size of ALL +/// your widgets' scrollbars. This ensures your application +/// has a consistent UI, is the default behavior, and is normally +/// what you want. +/// +/// Only use THIS method if you really need to override the global +/// scrollbar size. The need for this should be rare. +/// +/// Setting \p size to the special value of 0 causes the widget to +/// track the global Fl::scrollbar_size(), which is the default. +/// +/// \param[in] size Sets the scrollbar size in pixels.\n +/// If 0 (default), scrollbar size tracks the global Fl::scrollbar_size() +/// \see Fl::scrollbar_size() +/// +void Fl_Tree::scrollbar_size(int size) { + _scrollbar_size = size; + int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size(); + if ( _vscroll->w() != scrollsize ) { + _vscroll->resize(x()+w()-scrollsize, h(), scrollsize, _vscroll->h()); + } +} + +/// Do the callback for the item, setting the item and reason +void Fl_Tree::do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason) { + callback_reason(reason); + callback_item(item); + do_callback((Fl_Widget*)this, user_data()); +} + +/// Sets the item that was changed for this callback. +/// Used internally to pass the item that invoked the callback. +/// +void Fl_Tree::callback_item(Fl_Tree_Item* item) { + _callback_item = item; +} + +/// Gets the item that caused the callback. +/// The callback() can use this value to see which item changed. +/// +Fl_Tree_Item* Fl_Tree::callback_item() { + return(_callback_item); +} + +/// Sets the reason for this callback. +/// Used internally to pass the reason the callback was invoked. +/// +void Fl_Tree::callback_reason(Fl_Tree_Reason reason) { + _callback_reason = reason; +} + +/// Gets the reason for this callback. +/// +/// The callback() can use this value to see why it was called. Example: +/// \code +/// void MyTreeCallback(Fl_Widget *w, void *userdata) { +/// Fl_Tree *tree = (Fl_Tree*)w; +/// Fl_Tree_Item *item = tree->callback_item(); // the item changed (can be NULL if more than one item was changed!) +/// switch ( tree->callback_reason() ) { // reason callback was invoked +/// case FL_TREE_REASON_OPENED: ..item was opened.. +/// case FL_TREE_REASON_CLOSED: ..item was closed.. +/// case FL_TREE_REASON_SELECTED: ..item was selected.. +/// case FL_TREE_REASON_DESELECTED: ..item was deselected.. +/// } +/// } +/// \endcode +/// +Fl_Tree_Reason Fl_Tree::callback_reason() const { + return(_callback_reason); } /**