1 // $Id: Flu_Tree_Browser.h,v 1.91 2004/11/05 17:03:20 jbryan Exp $
2 
3 /***************************************************************
4  *                FLU - FLTK Utility Widgets
5  *  Copyright (C) 2002 Ohio Supercomputer Center, Ohio State University
6  *
7  * This file and its content is protected by a software license.
8  * You should have received a copy of this license with this file.
9  * If not, please contact the Ohio Supercomputer Center immediately:
10  * Attn: Jason Bryan Re: FLU 1224 Kinnear Rd, Columbus, Ohio 43212
11  *
12  ***************************************************************/
13 
14 
15 
16 #ifndef _FLU_TREE_BROWSER_H
17 #define _FLU_TREE_BROWSER_H
18 
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 
23 //#define USE_FLU_DND
24 
25 /* fltk includes */
26 #include <FL/Fl.H>
27 #include <FL/Fl_Box.H>
28 #include <FL/Fl_Pixmap.H>
29 #include <FL/Fl_Image.H>
30 #include <FL/Fl_Scrollbar.H>
31 #include <FL/Fl_Group.H>
32 #include <FL/Fl_Menu_Button.H>
33 
34 /* flu includes */
35 #include "Flu_Enumerations.h"
36 #include "FluSimpleString.h"
37 #ifdef USE_FLU_DND
38 #include "Flu_DND.h"
39 #else
40 typedef struct { bool dummy; } Flu_DND_Event;  // for compatibilty when not compiling DND support
41 #endif
42 
43 //! This class provides a browser for hierarchical data representation (i.e. a "tree")
44 #ifdef USE_FLU_DND
45 class FLU_EXPORT Flu_Tree_Browser : public Fl_Group, public Flu_DND
46 #else
47 class FLU_EXPORT Flu_Tree_Browser : public Fl_Group
48 #endif
49 {
50 
51   static bool USE_FLU_WIDGET_CALLBACK;
52 
53  public:
54 
55   class Node;
56   friend class Node;
57 
58   //! Normal FLTK widget constructor
59   Flu_Tree_Browser( int x, int y, int w, int h, const char *label = 0 );
60 
61   //! Default destructor
62   virtual ~Flu_Tree_Browser();
63 
64   //! Add the entry specified by \b fullpath to the tree. If \b w is not \c NULL then that widget is the entry and its label is visible depending on the value of \b showLabel. Note that the widget is destroyed by the tree/node on clear() or the destructor
65   /*! If \b fullpath ends in a slash ("/"), then the entry is added as a branch, else it is added as a leaf
66     \return a pointer to the Node of the added entry or NULL if the add failed */
67   Node* add( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true );
68 
69   //! Convenience function that is the same as add() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
70   Node* add( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
71 
72   //! Add entry \b name to node \b n. If \b w is not \c NULL then that widget is the entry and its label is visible depending on the value of \b showLabel. Note that the widget is destroyed by the tree/node on clear() or the destructor
73   /*! If \b name ends in a slash ("/"), then the entry is added as a branch, else it is added as a leaf
74     \return a pointer to the Node of the added entry or NULL if the add failed */
75   inline Node* add( Node* n, const char* name, Fl_Widget *w = 0, bool showLabel = true )
76     { return n->add( name, w, showLabel ); }
77 
78   //! Convenience function that is the same as add() except it appends a '/' to \b fullpath if one does not exist
79   Node* add_branch( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true );
80 
81   //! Convenience function that is the same as add() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
82   Node* add_branch( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
83 
84   //! Convenience function that is the same as add() except it appends a '/' to \b name if one does not exist
85   inline Node* add_branch( Node* n, const char* name, Fl_Widget *w = 0, bool showLabel = true )
86     { return n->add_branch( name, w, showLabel ); }
87 
88   //! Convenience function that is the same as add() except it removes any '/' at the end of \b fullpath
89   Node* add_leaf( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true );
90 
91   //! Convenience function that is the same as add() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
92   Node* add_leaf( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
93 
94   //! Convenience function that is the same as add() except it removes any '/' at the end of \b fullpath
95   inline Node* add_leaf( Node* n, const char* name, Fl_Widget *w = 0, bool showLabel = true )
96     { return n->add_leaf( name, w, showLabel ); }
97 
98   //! Set whether all branches are always open. Default value is \c false
all_branches_always_open(bool b)99   inline void all_branches_always_open( bool b )
100     { rdata.allBranchesAlwaysOpen = b; }
101 
102   //! Get whether all branches are always open. Default value is \c false
all_branches_always_open()103   inline bool all_branches_always_open()
104     { return rdata.allBranchesAlwaysOpen; }
105 
106   //! Set whether multiple leaves with the same path are allowed. Default value is \c true
allow_leaf_duplication(bool b)107   inline void allow_leaf_duplication( bool b )
108     { rdata.allowDuplication = b; }
109 
110   //! Get whether multiple leaves with the same path are allowed.
allow_leaf_duplication()111   inline bool allow_leaf_duplication()
112     { return rdata.allowDuplication; }
113 
114   //! \return \c true if drag and drop support has been compiled in, \c false otherwise
have_dnd()115   inline bool have_dnd()
116     {
117 #ifdef USE_FLU_DND
118       return true;
119 #else
120       return false;
121 #endif
122     }
123 
124   //! Set whether drag and drop processing is enabled for the browser. Default is \c false
125   /*! If DND is enabled, either text or instances of Flu_Tree_Browser::DND_Object from outside the tree can be dragged and dropped
126     onto the tree to create a new node. Nodes within the tree can be dragged and dropped (only within the same tree) according to
127     the value of selection_drag_mode(). */
allow_dnd(bool b)128   inline void allow_dnd( bool b )
129     { rdata.dnd = b; }
130 
131   //! Get whether drag and drop processing is enabled for the browser.
allow_dnd()132   inline bool allow_dnd()
133     { return rdata.dnd; }
134 
135   //! Set whether the root node is always open. Shortcut for \c get_root()->always_open(b). Default is \c false
always_open(bool b)136   inline void always_open( bool b )
137     { root.always_open( b ); }
138 
139   //! Get whether the root node is always open
always_open()140   inline bool always_open()
141     { return root.always_open(); }
142 
143   //! Set whether animations of opening/closing branches are enabled. Default is \c false
animate(bool b)144   inline void animate( bool b )
145     { rdata.animate = b; }
146 
147   //! Get whether animations of opening/closing branches are enabled
animate()148   inline bool animate()
149     { return rdata.animate; }
150 
151   //! Set whether the tree automatically determines whether a node is a branch or a leaf based on whether it has any children. Default is \c false
152   void auto_branches( bool b );
153 
154   //! Get whether the tree automatically determines whether a node is a branch or a leaf based on whether it has any children
auto_branches()155   inline bool auto_branches() const
156     { return rdata.autoBranches; }
157 
158   //! Get the default branch text color
branch_color()159   inline Fl_Color branch_color() const
160     { return rdata.defBranchColor; }
161 
162   //! Get the default branch text font
branch_font()163   inline Fl_Font branch_font() const
164     { return rdata.defBranchFont; }
165 
166   //! Get the default branch text size
branch_size()167   inline int branch_size() const
168     { return rdata.defBranchSize; }
169 
170   //! Set the default color, font and size to use for the text of all subsequent branches added to the tree
branch_text(Fl_Color color,Fl_Font font,int size)171   inline void branch_text( Fl_Color color, Fl_Font font, int size )
172     { rdata.defBranchColor = color; rdata.defBranchFont = font; rdata.defBranchSize = size; }
173 
174   //! Set the default branch icons to use for all subsequent branches added to the tree
175   /*! Passing in \c NULL for either icon will disable that icon for the branch */
176   void branch_icons( Fl_Image *closed, Fl_Image *open );
177 
178   //! Get the type of box to draw the browser in
box()179   inline Fl_Boxtype box() const
180     { return _box->box(); }
181 
182   //! Set the type of box to draw the browser in. Default is FL_FLAT_BOX
box(Fl_Boxtype b)183   inline void box( Fl_Boxtype b )
184     { _box->box( b ); }
185 
186   //! Override of Fl_Widget::callback
187   //inline void callback( Fl_Callback *c, void *user_data = 0 )
188   //{ rdata.cb = c; rdata.cbd = user_data; }
189 
190   //! Get the reason why the last callback happened. This can be one of FLU_UNHILIGHTED, FLU_HILIGHTED, FLU_SELECTED, FLU_UNSELECTED, FLU_OPENED, FLU_CLOSED, FLU_DOUBLE_CLICK, FLU_WIDGET_CALLBACK, FLU_MOVED_CALLBACK, FLU_NEW_NODE_CALLBACK
callback_reason()191   inline int callback_reason() const
192     { return rdata.cbReason; }
193 
194   //! Get the node on which the last callback happened.
195   /*! \note this node is only guaranteed to be in the tree \b during the callback. If the callback adds/removes nodes, then this node may have changed */
callback_node()196   inline Node* callback_node() const
197     { return rdata.cbNode; }
198 
199   //! Clear all entries from the tree
200   void clear();
201 
202   //! Set the default collapse icons to use for all subsequent branches added to the tree
203   void collapse_icons( Fl_Image *closed, Fl_Image *open );
204 
205   //! Get the amount of time to take when animating an open/close. Use in conjunction with frame_rate()
collapse_time()206   inline float collapse_time() const
207     { return rdata.collapseTime; }
208 
209   //! Set the amount of time to take when animating an open/close. Use in conjunction with frame_rate(). Default is 0.1 seconds
collapse_time(float t)210   inline void collapse_time( float t )
211     { rdata.collapseTime = t; }
212 
213   //! Get the background color of the browser
color()214   inline Fl_Color color() const
215     { return _box->color(); }
216 
217   //! Set the background color of the browser. Default is FL_WHITE
color(Fl_Color c)218   inline void color( Fl_Color c )
219     { _box->color( c ); }
220 
221   //! Set the color, style, and width of the connector lines. Default is FL_DARK2, FL_DOT, 1
222   inline void connector_style( Fl_Color color, int style, int width = 1 )
223     { rdata.defLineColor = color; rdata.lineStyle = style; rdata.lineWidth = width; }
224 
225   //! Get the color of the connector lines
connector_color()226   inline Fl_Color connector_color() const
227     { return rdata.defLineColor; }
228 
229   //! Get the style of the connector lines
connector_style()230   inline int connector_style() const
231     { return rdata.lineStyle; }
232 
233   //! Get the width of the connector lines
connector_width()234   inline int connector_width() const
235     { return rdata.lineWidth; }
236 
237   //! Set whether double-clicking a branch opens/closes it
double_click_opens(bool b)238   inline void double_click_opens( bool b )
239     { rdata.doubleClickToOpen = b; }
240 
241   //! Get whether double-clicking a branch opens/closes it
double_click_opens()242   inline bool double_click_opens()
243     { return rdata.doubleClickToOpen; }
244 
245   //! Get the color to use for shading even entries
even_shaded_entry_color()246   inline Fl_Color even_shaded_entry_color() const
247     { return rdata.shadedColors[0]; }
248 
249   //! Find the entry identified by \b fullpath
250   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
find(const char * fullpath)251   inline Node* find( const char *fullpath )
252     { return find_next( fullpath ); }
253 
254   //! Find entry \b name in path \b path
255   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
256   Node* find( const char *path, const char *name );
257 
258   //! Find the entry identified by unique id \b id
259   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
260   Node* find( unsigned int id );
261 
262   //! Search for Node \b n in the tree
263   /*! \return a pointer to \b n if it is found, or NULL if it is not in the tree */
find(Node * n)264   inline Node* find( Node *n )
265     { if( !n ) return NULL; else return find( n->id() ); }
266 
267   //! Find the entry containing the widget \b w
268   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
269   Node* find( Fl_Widget *w );
270 
271   //! Find the next entry identified by \b fullpath after \b startNode
272   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
273   Node* find_next( const char *fullpath, Node* startNode = NULL );
274 
275   //! Find the next entry \b name in path \b path after \b startNode
276   /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
277   Node* find_next( const char *path, const char *name );
278 
279   //! \return the number of discovered nodes matching path \b fullpath
280   int find_number( const char *fullpath );
281 
282   //! \return the number of discovered nodes with name \b name in path \b path
283   int find_number( const char *path, const char *name );
284 
285   //! \return the full path of the entry identified by unique id \b id, or the empty string if no matching entry was found
286   /*! \note the returned value is only valid until the next time find_path() is called */
287   const char* find_path( unsigned int id );
288 
289   //! \return the full path of the entry containing the widget \b w, or the empty string if no matching entry was found
290   /*! \note the returned value is only valid until the next time find_path() is called */
291   const char* find_path( Fl_Widget *w );
292 
293   //! \return the full path of Node \b n, or the empty string if \b n is not in the tree
294   /*! \note the returned value is only valid until the next time find_path() is called */
find_path(Node * n)295   inline const char* find_path( Node *n )
296     { if( !n ) return ""; else return find_path( n->id() ); }
297 
298   //! \return the first node in the tree (i.e. the root)
first()299   inline Node* first() { return root.first(); }
300 
301   //! \return the first branch encountered in a depth-first traversal of the tree. NULL means there are no branches
first_branch()302   inline Node* first_branch() { return root.first_branch(); }
303 
304   //! \return the first leaf encountered in a depth-first traversal of the tree. NULL means there are no leaves
first_leaf()305   inline Node* first_leaf() { return root.first_leaf(); }
306 
307   //! Get the frame rate to use during the open/close animation. Use in conjunction with collapse_time()
frame_rate()308   inline float frame_rate() const
309     { return rdata.fps; }
310 
311   //! Set the frame rate to use during the open/close animation. Use in conjunction with collapse_time(). Default is 100 frames per second
frame_rate(float f)312   inline void frame_rate( float f )
313     { if( f <= 0.0f ) rdata.fps = 0.001f; else rdata.fps = f; }
314 
315   //! \return the currently highlighted node
get_hilighted()316   inline Node* get_hilighted()
317     { return rdata.hilighted; }
318 
319   //! \return a pointer to the root node of the tree
get_root()320   inline Node *get_root() { return &root; }
321 
322   //! \return the selected Node that is at \b index among all selected nodes, or \c NULL if no Node is selected
323   /*! For example, \c get_selected(1) will return the first selected node. */
324   Node* get_selected( int index );
325 
326   //! Override of Fl_Widget::handle
327   int handle( int event );
328 
329   //! Set the horizontal icon gap for each entry. Default is 2
horizontal_gap(int g)330   inline void horizontal_gap( int g )
331     { rdata.hGap = g; rdata.forceResize = true; }
332 
333   //! Get the horizontal icon gap for each entry
horizontal_gap()334   inline int horizontal_gap() const
335     { return rdata.hGap; }
336 
337   //! Set how entries are inserted into the tree. This can be one of FLU_INSERT_FRONT, FLU_INSERT_BACK, FLU_INSERT_SORTED, FLU_INSERT_SORTED_REVERSE. Default is FLU_INSERT_SORTED
338   void insertion_mode( int m );
339 
340   //! Get how entries are inserted into the tree.
insertion_mode()341   inline int insertion_mode()
342     { return rdata.insertionMode; }
343 
344   //! \return whether the point \c (x,y) is inside the entry area (not on the scrollbars)
345   bool inside_entry_area( int x, int y );
346 
347   //! Set the title of the Tree (also the label for the root entry)
label(const char * l)348   inline void label( const char *l )
349     { root.text = l; }
350 
351   //! Get the title of the Tree (also the label for the root entry)
label()352   inline const char* label() const
353     { return root.text.c_str(); }
354 
355   //! \return the last node in the tree
last()356   inline Node* last() { return root.last(); }
357 
358   //! \return the last branch encountered in a depth-first traversal of the tree. NULL means there are no branches
last_branch()359   inline Node* last_branch() { return root.last_branch(); }
360 
361   //! \return the last leaf encountered in a depth-first traversal of the tree. NULL means there are no leaves
last_leaf()362   inline Node* last_leaf() { return root.last_leaf(); }
363 
364   //! Get the default leaf text color
leaf_color()365   inline Fl_Color leaf_color() const
366     { return rdata.defLeafColor; }
367 
368   //! Get the default leaf text font
leaf_font()369   inline Fl_Font leaf_font() const
370     { return rdata.defLeafFont; }
371 
372   //! Get the default leaf text size
leaf_size()373   inline int leaf_size() const
374     { return rdata.defLeafSize; }
375 
376   //! Set the default leaf icon to use for all subsequent leaves added to the tree
377   void leaf_icon( Fl_Image *icon );
378 
379   //! Set the default color, font and size to use for the text of all subsequent leaves added to the tree, Default is FL_BLACK, FL_HELVETICA, 12
leaf_text(Fl_Color color,Fl_Font font,int size)380   inline void leaf_text( Fl_Color color, Fl_Font font, int size )
381     { rdata.defLeafColor = color; rdata.defLeafFont = font; rdata.defLeafSize = size; }
382 
383   //! Set whether items can be moved only within their group ( \c true ) or can be moved anywhere in the tree ( \c false ). Default is \c false. Used only when selection_drag_mode() is FLU_DRAG_TO_MOVE.
move_only_same_group(bool b)384   inline void move_only_same_group( bool b )
385     { rdata.moveOnlySameGroup = b; }
386 
387   //! Get whether items can be moved only within their group ( \c true ) or can be moved anywhere in the tree ( \c false ). Used only when selection_drag_mode() is FLU_DRAG_TO_MOVE.
move_only_same_group()388   inline bool move_only_same_group()
389     { return rdata.moveOnlySameGroup; }
390 
391   //! \return the number of selected entries
392   int num_selected();
393 
394   //! Get the color to use for shading odd entries
odd_shaded_entry_color()395   inline Fl_Color odd_shaded_entry_color() const
396     { return rdata.shadedColors[1]; }
397 
398   //! Set whether only a single branch (except the root branch) is allowed open at a time. Default is \c false
only_one_open_branch(bool b)399   inline void only_one_open_branch( bool b )
400     { rdata.singleBranchOpen = b; }
401 
402   //! Get whether only a single branch (except the root branch) is allowed open at a time
only_one_open_branch()403   inline bool only_one_open_branch()
404     { return rdata.singleBranchOpen; }
405 
406   //! Open or close the root node
open(bool b)407   inline void open( bool b )
408     { root.open( b ); }
409 
410   //! Is the root node open or closed?
open()411   inline bool open() const
412     { return root.open(); }
413 
414   //! Set whether you can open/close a branch even if it has no children. Default is \c false
open_without_children(bool b)415   inline void open_without_children( bool b )
416     { rdata.openWOChildren = b; }
417 
418   //! Get whether you can open/close a branch even if it has no children.
open_without_children()419   inline bool open_without_children() const
420     { return rdata.openWOChildren; }
421 
422   //! Set whether selecting a branch also opens it. Default is \c false
open_on_select(bool b)423   inline void open_on_select( bool b )
424     { rdata.openOnSelect = b; }
425 
426   //! Get whether selecting a branch also opens it
open_on_select()427   inline bool open_on_select() const
428     { return rdata.openOnSelect; }
429 
430   //! Print the tree to stdout
431   void print();
432 
433   //! Remove the entry identified by path \b fullpath from the tree
434   /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
435   unsigned long remove( const char *fullpath );
436 
437   //! Remove entry \b name in path \b path from the tree
438   /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
439   unsigned long remove( const char *path, const char *name );
440 
441   //! Remove the entry identified by unique id \b id from the tree
442   /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
443   unsigned long remove( unsigned int id );
444 
445   //! Remove the entry containing the widget \b w from the tree. Note that the widget is automatically destroyed
446   /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
447   unsigned long remove( Fl_Widget *w );
448 
449   //! Remove Node \b n from the tree
450   /*! \return the id of \b n on successful removal, or \c 0 if \b n is not in the tree */
remove(Node * n)451   inline unsigned long remove( Node* n )
452     { if( !n ) return 0; else return remove( n->id() ); }
453 
454   //! Override of Fl_Widget::resize
455   void resize( int X, int Y, int W, int H );
456 
457   //! Convenience routine to set the root label color. See Flu_Tree_Browser::Node::label_color()
root_color(Fl_Color c)458   inline void root_color( Fl_Color c )
459     { get_root()->label_color( c ); }
460 
461   //! Convenience routine to set the root label color. See Flu_Tree_Browser::Node::label_color()
root_color()462   inline Fl_Color root_color()
463     { return get_root()->label_color(); }
464 
465   //! Convenience routine to set the root label font. See Flu_Tree_Browser::Node::label_font()
root_font(Fl_Font f)466   inline void root_font( Fl_Font f )
467     { get_root()->label_font( f ); }
468 
469   //! Convenience routine to set the root label font. See Flu_Tree_Browser::Node::label_font()
root_font()470   inline Fl_Font root_font()
471     { return get_root()->label_font(); }
472 
473   //! Convenience routine to set the root label size. See Flu_Tree_Browser::Node::label_size()
root_size(unsigned char s)474   inline void root_size( unsigned char s )
475     { get_root()->label_size( s ); }
476 
477   //! Convenience routine to set the root label size. See Flu_Tree_Browser::Node::label_size()
root_size()478   inline unsigned char root_size()
479     { return get_root()->label_size(); }
480 
481   //! Select all entries in the tree
select_all()482   inline void select_all()
483     { root.select_all(); }
484 
485   //! Get the color to use when hilighting selected entries
selection_color()486   inline Fl_Color selection_color() const
487     { return rdata.defSelectionColor; }
488 
489   //! Set the color to use when hilighting selected entries. Default is FL_SELECTION_COLOR
selection_color(Fl_Color c)490   inline void selection_color( Fl_Color c )
491     { rdata.defSelectionColor = c; }
492 
493   //! Set how selection is affected when the mouse is dragged. This can be one of FLU_DRAG_IGNORE, FLU_DRAG_TO_SELECT, FLU_DRAG_TO_MOVE. Default is FLU_DRAG_TO_SELECT.
selection_drag_mode(int m)494   inline void selection_drag_mode( int m )
495     { rdata.selectionDragMode = m; }
496 
497   //! Get how selection is affected when the mouse is dragged
selection_drag_mode()498   inline int selection_drag_mode() const
499     { return rdata.selectionDragMode; }
500 
501   //! Set whether the hilighted node is always selected. Default is \c false
selection_follows_hilight(bool b)502   inline void selection_follows_hilight( bool b )
503     { rdata.selectionFollowsHilight = b; if( b && rdata.hilighted ) rdata.hilighted->select(true); }
504 
505   //! Get whether the hilighted node is always selected
selection_follows_hilight()506   inline bool selection_follows_hilight()
507     { return rdata.selectionFollowsHilight; }
508 
509   //! Set how individual entries are selected using the mouse. This can be one of FLU_NO_SELECT, FLU_SINGLE_SELECT, FLU_MULTI_SELECT. Default is FLU_MULTI_SELECT
selection_mode(int m)510   inline void selection_mode( int m )
511     { rdata.selectionMode = m; root.unselect_all(); }
512 
513   //! Get how individual entries are selected using the mouse
selection_mode()514   inline int selection_mode() const
515     { return rdata.selectionMode; }
516 
517   //! If selection_mode() is \c FLU_SINGLE_SELECT, then whichever node is under the mouse will always be selected, with all others unselected. Default is \c false
select_under_mouse(bool b)518   inline void select_under_mouse( bool b )
519     { rdata.selectUnderMouse = b; }
520 
521   //! If selection_mode() is \c FLU_SINGLE_SELECT, then whichever node is under the mouse will always be selected, with all others unselected. Default is \c false
select_under_mouse()522   inline bool select_under_mouse() const
523     { return rdata.selectUnderMouse; }
524 
525   //! Calling this will cause any newly added branches to use the default branch icon
526   void set_default_branch_icons();
527 
528   //! Set which node is hilighted and ready to be selected or unselected. This also scrolls the browser so \b n is visible.
529   void set_hilighted( Node* n );
530 
531   //! Set the title of the root of the tree to \b label. If \b w is not \c NULL then that widget is the entry and its label is visible depending on the value of \b showLabel. Note that the widget is destroyed by the tree/node on clear() or the destructor
532   /*! The root icons, color, font and size are set to the current branch icons and text color, font and size */
533   Node* set_root( const char *label, Fl_Widget *w = 0, bool showLabel = true );
534 
535   //! Set the colors to use for shading every other entry. Default is FL_WHITE, FL_WHITE
shaded_entry_colors(Fl_Color even,Fl_Color odd)536   inline void shaded_entry_colors( Fl_Color even, Fl_Color odd )
537     { rdata.shadedColors[0] = even; rdata.shadedColors[1] = odd; }
538 
539   //! Set whether branch entries are visible. Default is \c true
show_branches(bool b)540   inline void show_branches( bool b )
541     { rdata.showBranches = b; rdata.forceResize = true; }
542 
543   //! Get whether branch entries are visible
show_branches()544   inline bool show_branches() const
545     { return rdata.showBranches; }
546 
547   //! Set whether the connectors between entries are visible. Default is \c true
show_connectors(bool b)548   inline void show_connectors( bool b )
549     { rdata.showConnectors = b; }
550 
551   //! Get whether the connectors between entries are visible
show_connectors()552   inline bool show_connectors() const
553     { return rdata.showConnectors; }
554 
555   //! Set whether the root branch (i.e. the name of the tree) is visible. Default is \c true
show_root(bool b)556   inline void show_root( bool b )
557     { rdata.showRoot = b; rdata.forceResize = true; }
558 
559   //! Get whether the root branch (i.e. the name of the tree) is visible
show_root()560   inline bool show_root() const
561     { return rdata.showRoot; }
562 
563   //! Set whether leaf entries are visible. Default is \c true
show_leaves(bool b)564   inline void show_leaves( bool b )
565     { rdata.showLeaves = b; rdata.forceResize = true; }
566 
567   //! Get whether leaf entries are visible
show_leaves()568   inline bool show_leaves() const
569     { return rdata.showLeaves; }
570 
571   //! Sort the tree according to insertion_mode()
sort()572   inline void sort()
573     { root.sort(); }
574 
575   //! Unselect all entries in the tree
unselect_all()576   inline void unselect_all()
577     { root.unselect_all(); }
578 
use_FLU_WIDGET_CALLBACK(bool b)579   inline static void use_FLU_WIDGET_CALLBACK( bool b )
580     { USE_FLU_WIDGET_CALLBACK = b; }
581 
582   //! Set the vertical gap between tree entries. Default is 0
vertical_gap(int g)583   inline void vertical_gap( int g )
584     { rdata.vGap = g; rdata.forceResize = true; }
585 
586   //! Get the vertical gap between tree entries
vertical_gap()587   inline int vertical_gap() const
588     { return rdata.vGap; }
589 
590   //! Override of Fl_Widget::when. Currently only FL_WHEN_NEVER, FL_WHEN_CHANGED, and FL_WHEN_NOT_CHANGED are supported. Default value is FL_WHEN_CHANGED
591   /*! When the callback occurs, you can use callback_reason() to determine exactly what cause the callback and callback_node()
592     to get the node that was affected. */
593   //inline void when( unsigned int w )
594   //{ rdata.when = w; }
595 
596   //! Override of Fl_Widget::when
597   //inline unsigned int when() const
598   //{ return rdata.when; }
599 
600   //! Set the gap between the widget and the icon that precedes it. Default is 2
widget_gap(int g)601   inline void widget_gap( int g )
602     { rdata.wGap = g; rdata.forceResize = true; }
603 
604   //! Get the gap between the widget and the icon that precedes it
widget_gap()605   inline int widget_gap() const
606     { return rdata.wGap; }
607 
608  protected:
609 
610   class RData;
611 
612   //! Internal class holding an (alphabetically) ordered list of nodes
613   class FLU_EXPORT NodeList
614     {
615     public:
616       NodeList();
617       ~NodeList();
618       void add( Node* n, int position = -1 );
child(int n)619       inline Node* child( int n ) const { return _nodes[n]; }
620       int erase( Node* n );
621       int erase( const char* n );
622       void erase( int n );
623       void clear();
624       int findNum( const char *n );  // find the number of nodes in the list with name n
625       Node* find( const char* n, int which = 1 );  // find the which'th node in the list with name n
size()626       inline int size() const { return _nNodes; };
627       void sort();
628       static bool move( Node* n1, int where, Node* n2 );
629     private:
630       friend class Node;
631       static int compareNodes( const void *arg1, const void* arg2 );
632       static int reverseCompareNodes( const void *arg1, const void* arg2 );
633       bool search( Node *n, int &index );
634       bool search( const char *n, int &index );
635       bool linSearch( Node *n, int &index );
636       bool linSearch( const char *n, int &index );
637       bool binSearch( Node *n, int &index );
638       bool binSearch( const char *n, int &index );
639       Node **_nodes;
640       int _nNodes, _size;
641     };
642 
643   //! Internal class holding a stack of integers
644   class FLU_EXPORT IntStack
645     {
646     public:
647       IntStack();
648       IntStack( const IntStack& s );
649       ~IntStack();
650       void push( int i );
651       int pop();
652       void clear();
653       inline int operator [](int i) { return _list[i]; }
size()654       inline int size() { return _size; }
655       IntStack& operator =( const IntStack& s );
656     private:
657       int *_list;
658       int _size, _bufferSize;
659     };
660 
661   public:
662   enum { MOVE_BEFORE, MOVE_INSIDE, MOVE_AFTER }; // where to move a dragged node?
663  protected:
664 
665   //! Recursive data structure passed down through the node tree
666   class FLU_EXPORT RData {
667   public:
668     // volatile objects (from the perspective of each node during a recursive descent)
669     int x, y, totalW, totalH;
670     bool first, last, dragging, shiftSelect, shiftSelectAll, visibilityChanged, selectionFollowsHilight;
671     Node *hilighted, /**lastHilighted, */ *previous, *grabbed, *dragNode, *animatedNode;
672     int delta, shadedIndex, counter, searchIndex, branchIconW, dragPos, dragWhere;
673     Fl_Color lineColor, bgColor, selectionColor;
674     bool forceResize;  // force the browser to resize on the next draw (which forces a recalculation of the tree layout)
675     unsigned int nextId;  // monotonically increasing id of each entry
676     FluSimpleString path;  // used to construct the full path during a findPath() operation
677     IntStack branchConnectors;
678 
679     // static objects (from the perspective of each node during a recursive descent)
680     int insertionMode;
681     Fl_Image *defaultCollapseIcons[2], *defaultBranchIcons[2];
682     Fl_Image *collapseIcons[2], *branchIcons[2], *leafIcon;
683     int hGap, vGap, wGap;
684     int lineStyle, lineWidth, selectionMode, selectionDragMode;
685     bool showRoot, showConnectors, showLeaves, showBranches, openOnSelect,
686       allowDuplication, animate, animating, singleBranchOpen, moveOnlySameGroup, justOpenedClosed,
687       isMoveValid, doubleClickToOpen, dnd, allBranchesAlwaysOpen, autoBranches, openWOChildren,
688       selectUnderMouse;
689     float collapseTime, fps, animationDelta, animationOffset;
690     Fl_Color defLineColor, defSelectionColor, shadedColors[2];
691     Fl_Color defLeafColor, defBranchColor;
692     Fl_Font defLeafFont, defBranchFont;
693     int defLeafSize, defBranchSize;
694     int browserX, browserY, browserW, browserH;
695     Node *root;
696     Flu_Tree_Browser *tree;
697     unsigned int cbReason;
698     Node *cbNode, *lastOpenBranch;
699   };
700 
701  public:
702 
703 #ifdef USE_FLU_DND
704   //! This class can be subclassed to make an object which can be dropped on a tree to make a new node
705   /*! When the object is dropped, the tree will name the object according to what the function
706     \b name() returns */
707   class FLU_EXPORT DND_Object : public Flu_DND
708     {
709     public:
710 
711       //! Default constructor
712       DND_Object();
713 
714       //! The descendent should call this when the user grabs the object to start dragging it (e.g. on the FL_PUSH event)
grab()715       inline void grab()
716 	{ dnd_grab( this, "DND_Object" ); }
717 
718       //! Descendents MUST implement this function to return the name of the dropped object
719       virtual const char* name() = 0;
720 
721     };
722 #endif
723 
724   //! This class holds a single entry in the tree
725   class FLU_EXPORT Node
726     {
727 
728     protected:
729 
730       enum { ADD, REMOVE, FIND, FIND_NUMBER, GET_SELECTED };  // parameters for modify()
731       enum { DRAW, MEASURE, MEASURE_THIS_OPEN, HANDLE, COUNT_SELECTED };  // parameters for recurse()
732 
733       // flags
734       enum { SELECTED = 0x0001, COLLAPSED = 0x0002, LEAF = 0x0004, SHOW_LABEL = 0x0008,
735 	     ACTIVE = 0x0010, EXPAND_TO_WIDTH = 0x0020, ALWAYS_OPEN = 0x0040,
736 	     SOME_VISIBLE_CHILDREN = 0x0080, MOVABLE = 0x0100, DROPPABLE = 0x0200,
737 	     AUTO_LABEL_COLOR = 0x0400, AUTO_COLOR = 0x0800, AUTO_LABEL = 0x1000,
738 	     SWAP_LABEL_AND_WIDGET = 0x2000, ICON_AT_END = 0x4000 };
739 
740       // flag manipulator functions
CHECK(unsigned short flag)741       inline bool CHECK( unsigned short flag ) const { return flags & flag; }
SET(unsigned short flag)742       inline void SET( unsigned short flag ) { flags |= flag; }
SET(unsigned short flag,bool b)743       inline void SET( unsigned short flag, bool b ) { if(b) SET(flag); else CLEAR(flag); }
CLEAR(unsigned short flag)744       inline void CLEAR( unsigned short flag ) { flags &= ~flag; }
745 
746     public:
747 
748       //! Is this node currently active?
active()749       inline bool active() const
750 	{ return CHECK(ACTIVE); }
751 
752       //! Activate or deactivate this node
753       void active( bool b );
754 
755       //! Activate this node
activate()756       inline void activate()
757 	{ active(true); }
758 
759       //! Add the entry specified by \b fullpath to this node. If \b w is not \c NULL then that widget is the entry and the label (as specified in \b fullPath) is visible depending on the value of \b showLabel. Note that the widget is destroyed by the tree/node on clear() or the destructor
760       /*! \return a pointer to the Node of the added entry or NULL if the add failed */
761       inline Node* add( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true )
762 	{ return( modify( fullpath, ADD, tree->rdata, w, showLabel ) ); }
763 
764       //! Convenience function that is the same as add() except it appends a '/' to \b fullpath if one does not exist
765       Node* add_branch( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true );
766 
767       //! Convenience function that is the same as add() except it removes any '/' at the end of \b fullpath
768       Node* add_leaf( const char* fullpath, Fl_Widget *w = 0, bool showLabel = true );
769 
770       //! Convenience function that is the same as add() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
771       Node* add( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
772 
773       //! Convenience function that is the same as add_branch() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
774       Node* add_branch( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
775 
776       //! Convenience function that is the same as add_leaf() except \b path and \b name are concatenated (with a '/' added if necessary) to create the full path
777       Node* add_leaf( const char* path, const char* name, Fl_Widget *w = 0, bool showLabel = true );
778 
779       //! Set whether a branch node is always open (only for branch nodes). Default is \c false
always_open(bool b)780       inline void always_open( bool b )
781 	{ if( b ) open(true); SET(ALWAYS_OPEN,b); tree->rdata.forceResize = true; }
782 
783       //! Get whether this branch node is always open (only for branches)
always_open()784       inline bool always_open() const
785 	{ return CHECK(ALWAYS_OPEN); }
786 
787       //! Set whether the color of the widget in this node tracks the color of the node itself. This is useful for changing the color of the widget when the node is selected. Default is \c false
auto_color(bool b)788       inline void auto_color( bool b )
789 	{ SET(AUTO_COLOR,b); }
790 
791       //! Get whether the color of the widget in this node tracks the color of the node itself
auto_color()792       inline bool auto_color()
793 	{ return CHECK(AUTO_COLOR); }
794 
795       //! Set whether the label of the widget in this node tracks the label of the node itself. This is useful for when the label of the node changes and you want the widget to reflect the change
auto_label(bool b)796       inline void auto_label( bool b )
797 	{ SET(AUTO_LABEL,b); }
798 
799       //! Set whether the label of the widget in this node tracks the label of the node itself.
auto_label()800       inline bool auto_label()
801 	{ return CHECK(AUTO_LABEL); }
802 
803       //! Set whether the label color of the widget in this node tracks the label color of the node itself. This is useful for changing the label color of the widget when the node is selected. Default is \c false
auto_label_color(bool b)804       inline void auto_label_color( bool b )
805 	{ SET(AUTO_LABEL_COLOR,b); }
806 
807       //! Get whether the label color of the widget in this node tracks the label color of the node itself
auto_label_color()808       inline bool auto_label_color()
809 	{ return CHECK(AUTO_LABEL_COLOR); }
810 
811       //! Set the branch icons to use for this node (only for branch nodes)
812       void branch_icons( Fl_Image *closed, Fl_Image *open );
813 
814       //! Convenience routine to set both branch icons at once
branch_icon(Fl_Image * icon)815       inline void branch_icon( Fl_Image *icon )
816 	{ branch_icons( icon, icon ); }
817 
818       //! \return child \b i of this node (base 0). Bounds checking is performed and NULL is returned if the child cannot be found
819       Node* child( int i ) const;
820 
821       //! \return the number of child nodes beneath this node
children()822       inline int children() const
823 	{ return _children.size(); }
824 
825       //! Clear all child entries from this node (does not change the entry of this node)
826       void clear();
827 
828       //! Close this node (only for branches)
close()829       inline void close()
830 	{ open( false ); }
831 
832       //! Is this node closed? (only for branches)
closed()833       inline bool closed()
834 	{ return !open(); }
835 
836        //! Set the collapse icons to use for this node (only for branch nodes)
837       /*! \note if a NULL icon is passed, the default plus/minus icons are chosen */
838       void collapse_icons( Fl_Image *closed, Fl_Image *open );
839 
840       //! Deactivate this node
deactivate()841       inline void deactivate()
842 	{ active(false); }
843 
844       //! Get the depth of this node in the tree
845       unsigned short depth() const;
846 
847       //! Do the tree browser callback. \b reason should be one of FLU_HILIGHTED, FLU_UNHILIGHTED, FLU_SELECTED, FLU_UNSELECTED, FLU_OPENED, FLU_CLOSED, FLU_DOUBLE_CLICK, FLU_WIDGET_CALLBACK
848       void do_callback( int reason );
849 
850       //! Set whether this node can receive new nodes as a result of dragging-and-dropping (only for branch nodes). Default is \c true
droppable(bool b)851       inline void droppable( bool b )
852 	{ SET(DROPPABLE,b); }
853 
854       //! Get whether this node can receive new nodes as a result of dragging-and-dropping (only for branch nodes).
droppable()855       inline bool droppable()
856 	{ return CHECK(DROPPABLE); }
857 
858       //! Set whether to force the size of the embedded widget to expand to fill the visible width of the browser. Default is \c false
expand_to_width(bool b)859       inline void expand_to_width( bool b )
860 	{ SET(EXPAND_TO_WIDTH,b); tree->rdata.forceResize = true; }
861 
862       //! Get whether to force the size of the embedded widget to expand to fill the visible width of the browser
expand_to_width()863       inline bool expand_to_width() const
864 	{ return CHECK(EXPAND_TO_WIDTH); }
865 
866       //! Find the entry identified by \b fullpath
867       /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
find(const char * fullpath)868       inline Node* find( const char *fullpath )
869 	{ return( modify( fullpath, FIND, tree->rdata ) ); }
870 
871       //! Find the entry identified by unique id \b id
872       /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
873       Node* find( unsigned int id );
874 
875       //! Find the entry containing the widget \b w
876       /*! \return a pointer to the Node of the found entry, or NULL if no matching entry was found */
877       Node* find( Fl_Widget *w );
878 
879       //! Search for Node \b n
880       /*! \return a pointer to \b n if it is found, or NULL if it is not present */
find(Node * n)881       inline Node* find( Node *n )
882 	{ if( !n ) return NULL; else return find( n->id() ); }
883 
884       //! \return the full path of this node
885       /*! \note the returned value is only valid until the next time find_path() is called */
find_path()886       inline const char* find_path()
887 	{ return tree->find_path( this ); }
888 
889       //! \return the first node in this hierarchy (i.e. this node)
890       Node* first();
891 
892       //! \return the first branch encountered in a depth-first traversal (or this node if it is a branch). NULL means there are no branches
893       Node* first_branch();
894 
895       //! \return the first leaf encountered in a depth-first traversal (or this node if it is a leaf). NULL means there are no leaves
896       Node* first_leaf();
897 
898       //! Set whether this node draws the label to the left of the widget (\c false, default) or to the right of the widget (\c true)
swap_label_and_widget(bool b)899       inline void swap_label_and_widget( bool b )
900 	{ SET(SWAP_LABEL_AND_WIDGET,b); }
901 
902       //! Get whether this node draws the label to the left of the widget (\c false, default) or to the right of the widget (\c true)
swap_label_and_widget()903       inline bool swap_label_and_widget()
904 	{ return CHECK(SWAP_LABEL_AND_WIDGET); }
905 
906       //! \return the selected Node that is at \b index among all selected nodes, or \c NULL if no Node is selected
907       /*! For example, \c get_selected(1) will return the first selected node. */
908       Node* get_selected( int index );
909 
910       //! Set whether the icon for this node is drawn after the label and widget (\c true) or before (\c false, default) (only for leaf nodes)
icon_at_end(bool b)911       inline void icon_at_end( bool b )
912 	{ SET(ICON_AT_END,b); }
913 
914       //! Get whether the icon for this node is drawn after the label and widget (\c true) or before (\c false, default) (only for leaf nodes)
icon_at_end()915       inline bool icon_at_end()
916 	{ return CHECK(ICON_AT_END); }
917 
918       //! Get the unique ID of this node
id()919       inline unsigned int id() const
920 	{ return _id; }
921 
922       //! Get the index this node is (as a child) in its parent's list
923       /*! \return -1 if this node has no parent, else its index in its parent's list of children */
924       int index() const;
925 
926       //! Insert a new node at position \b pos
927       Node* insert( const char* fullpath, int pos );
928 
929       //! Insert a new branch at position \b pos
930       Node* insert_branch( const char* fullpath, int pos );
931 
932       //! Insert a new leaf at position \b pos
933       Node* insert_leaf( const char* fullpath, int pos );
934 
935       //! Is node \b n an ancestor of this node?
936       bool is_ancestor( Node* n );
937 
938       //! Is this node a branch node?
939       bool is_branch() const;
940 
941       //! Is node \b n a descendent of this node?
942       bool is_descendent( Node* n );
943 
944       //! Is this node a leaf node?
945       bool is_leaf() const;
946 
947       //! Is this node the root node?
is_root()948       inline bool is_root() const
949 	{ return( _parent == 0 ); }
950 
951       //! Set the label for this node. Note that setting the label may invalidate a sorted tree. Fix by calling Flu_Tree_Browser::sort()
label(const char * l)952       inline void label( const char *l )
953 	{ text = l; tree->redraw(); }
954 
955       //! Get the label for this node
label()956       inline const char* label() const
957 	{ return text.c_str(); }
958 
959       //! Set the label color for this node
label_color(Fl_Color c)960       inline void label_color( Fl_Color c )
961 	{ textColor = c; }
962 
963       //! Get the label color for this node
label_color()964       inline Fl_Color label_color() const
965 	{ return textColor; }
966 
967       //! Set the label font for this node
label_font(Fl_Font f)968       inline void label_font( Fl_Font f )
969 	{ textFont = f; tree->rdata.forceResize = true; }
970 
971       //! Get the label font for this node
label_font()972       inline Fl_Font label_font() const
973 	{ return textFont; }
974 
975       //! Set the label size for this node
label_size(unsigned char s)976       inline void label_size( unsigned char s )
977 	{ textSize = s; tree->rdata.forceResize = true; }
978 
979       //! Get the label size for this node
label_size()980       inline unsigned char label_size() const
981 	{ return textSize; }
982 
983       //! Is the label for this node visible?
label_visible()984       inline bool label_visible() const
985 	{ return CHECK(SHOW_LABEL); }
986 
987       //! Set whether the label for this node is visible
label_visible(bool b)988       inline void label_visible( bool b )
989 	{ SET(SHOW_LABEL,b); tree->rdata.forceResize = true; }
990 
991       //! \return the last node in this hierarchy
992       Node* last();
993 
994       //! \return the last branch encountered in a depth-first traversal (or this node if it is a branch and has no children). NULL means there are no branches
995       Node* last_branch();
996 
997       //! \return the last leaf encountered in a depth-first traversal (or this node if it is a leaf). NULL means there are no leaves
998       Node* last_leaf();
999 
1000       //! Set the leaf icon to use for this node (only for leaf nodes)
1001       void leaf_icon( Fl_Image *icon );
1002 
1003       //! Set whether this node can be moved (either via move() or by dragging with the mouse). Default is \c true
movable(bool b)1004       inline void movable( bool b )
1005 	{ SET(MOVABLE,b); }
1006 
1007       //! Get whether this node can be moved (either via move() or by dragging with the mouse)
movable()1008       inline bool movable()
1009 	{ return CHECK(MOVABLE); }
1010 
1011       //! Move this node to absolute position \b pos within its parent
1012       /*! \return \c true if the move was successful, or \c false if the move is not allowed
1013        */
1014       bool move( int pos );
1015 
1016       //! Move this node to a position before, after, or inside node \b n
1017       /*! \param where can be one of MOVE_BEFORE, MOVE_AFTER, or MOVE_INSIDE
1018 	\return \c true if the move was successful, or \c false if the move is not allowed
1019        */
move(int where,Node * n)1020       inline bool move( int where, Node* n )
1021 	{ return( move( this, where, n ) ); }
1022 
1023       //! Move node \b n1 to a position before, after, or inside node \b n2
1024       /*! \param where can be one of MOVE_BEFORE, MOVE_AFTER, or MOVE_INSIDE
1025 	\return \c true if the move was successful, or \c false if the move is not allowed
1026        */
1027       static bool move( Node* n1, int where, Node* n2 );
1028 
1029       //! \return the next node (after this node) in this hierarchy (depth-first traversal)
1030       Node* next();
1031 
1032       //! \return the next branch (after this node) encountered in a depth-first traversal. NULL means there are no more branches
1033       Node* next_branch();
1034 
1035       //! \return the next leaf (after this node) encountered in a depth-first traversal. NULL means there are no more leaves
1036       Node* next_leaf();
1037 
1038       //! \return the next sibling (after this node) encountered in a depth-first traversal. NULL means this node is the last child w.r.t. its parent
1039       Node* next_sibling();
1040 
1041       //! \return the number of selected entries
1042       int num_selected();
1043 
1044       //! Is this node currently open? (only for branch nodes)
open()1045       inline bool open() const
1046 	{ return( !CHECK(COLLAPSED) || tree->rdata.allBranchesAlwaysOpen ); }
1047 
1048       //! Open or close this node (only for branch nodes)
1049       void open( bool b );
1050 
1051       //! Get the node that is the parent of this node, or NULL if there is no parent
parent()1052       inline Node* parent() const
1053 	{ return _parent; }
1054 
1055       //! \return the previous node (before this node) in this hierarchy (depth-first traversal)
1056       Node* previous();
1057 
1058       //! \return the previous branch (before this node) encountered in a depth-first traversal. NULL means there are no more branches
1059       Node* previous_branch();
1060 
1061       //! \return the previous leaf (before this node) encountered in a depth-first traversal. NULL means there are no more leaves
1062       Node* previous_leaf();
1063 
1064       //! \return the previous sibling (before this node) encountered in a depth-first traversal. NULL means this node is the first child w.r.t. its parent
1065       Node* previous_sibling();
1066 
1067       //! Print this node and its children to stdout
1068       void print( int spaces = 0 );
1069 
1070       //! Remove the entry identified by path \b fullpath from this node
1071       /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
remove(const char * fullpath)1072       inline unsigned long remove( const char *fullpath )
1073 	{ return( (unsigned long)modify( fullpath, REMOVE, tree->rdata ) ); }
1074 
1075       //! Remove the entry identified by unique id \b id from this node
1076       /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
1077       unsigned long remove( unsigned int id );
1078 
1079       //! Remove the node containing the widget \b w from this node. Note that the widget is automatically destroyed
1080       /*! \return the unique id of the removed entry, or \c 0 if no matching entry was found */
1081       unsigned long remove( Fl_Widget *w );
1082 
1083       //! Remove Node \b n
1084       /*! \return the id of \b n on successful removal, or \c 0 if \b n is present */
remove(Node * n)1085       inline unsigned long remove( Node* n )
1086 	{ if( !n ) return 0; else return remove( n->id() ); }
1087 
1088       //! Select this entry and all child entries
1089       void select_all();
1090 
1091       //! Is this node currently selected?
selected()1092       inline bool selected() const
1093 	{ return CHECK(SELECTED); }
1094 
1095       //! Select or unselect this node
1096       void select( bool b );
1097 
1098       //! Select only this node
select_only()1099       inline void select_only()
1100 	{ tree->unselect_all(); select(true); }
1101 
1102       //! Sort this node's children according to Flu_Tree_Browser::insertion_mode()
sort_children()1103       inline void sort_children()
1104 	{ sort(); }
1105 
1106       //! Swap this node and node \b n in their respective trees
1107       /*! \return \c true if the swap was successful, else \c false */
swap(Node * n)1108       inline bool swap( Node* n )
1109 	{ return swap( this, n ); }
1110 
1111       //! Swap nodes \b n1 and \b n2 in their respective trees
1112       /*! \return \c true if the swap was successful, else \c false */
1113       static bool swap( Node* n1, Node* n2 );
1114 
1115       //! Unselect this entry and all child entries (except for Node \b except )
1116       void unselect_all( Node* except = NULL );
1117 
1118       //! Get the user-specific data stored in this node
user_data()1119       inline void* user_data()
1120 	{ return userData; }
1121 
1122       //! Set the user-specific data stored in this node
user_data(void * d)1123       inline void user_data( void *d )
1124 	{ userData = d; }
1125 
1126       //! Get the widget in this node, or NULL if there is no widget. Note that the widget is destroyed by the tree/node on clear() or the destructor
widget()1127       inline Fl_Widget* widget() const
1128 	{ return( _widget ? _widget->w : NULL ); }
1129 
1130       //! Set the widget in this node. Note that the widget is destroyed by the tree/node on clear() or the destructor
1131       void widget( Fl_Widget *w );
1132 
1133     protected:
1134 
1135       friend class Flu_Tree_Browser;
1136       friend class NodeList;
1137 
1138       // Root node constructor
1139       Node( const char *lbl = 0 );
1140 
1141       // Non-root constructor
1142       Node( bool l, const char* n, Node *p, RData &rdata, Fl_Widget *w, bool showLabel );
1143 
1144       ~Node();
1145 
1146       // add/remove/find/get
1147       Node* modify( const char* path, int what, RData &rdata, Fl_Widget *w = 0, bool showLabel = true );
1148 
1149       void initType();
1150 
1151       void sort();
1152 
1153       void determineVisibility( bool parentVisible = true );
1154 
1155       static bool isMoveValid( Node* &n1, int &where, Node* &n2 );
1156 
1157       // handle/draw/measure/count
1158       int recurse( RData &rdata, int type, int event = 0 );
1159 
1160       void draw( RData &rdata, bool measure );
1161 
1162       // recursively finding the full path of the node identified by id
1163       bool findPath( unsigned int id, RData &rdata );
1164 
1165       // recursively finding the full path of the node containing w
1166       bool findPath( Fl_Widget *w, RData &rdata );
1167 
1168       class FLU_EXPORT WidgetInfo
1169 	{
1170 	public:
1171 	  Fl_Widget *w;
1172 	  int defaultW;  // the initial width of the widget
1173 	  void (*CB)(Fl_Widget*,void*);
1174 	  void *CBData;
1175 	};
1176 
1177       unsigned int _id; // the unique id of this node
1178       unsigned short flags;
1179       NodeList _children;
1180       Node *_parent;
1181       Flu_Tree_Browser *tree;
1182       FluSimpleString text;
1183       WidgetInfo *_widget;  // memory overhead deferred to WidgetInfo. present only if widget is
1184       Fl_Group *_group;
1185       void *userData;
1186       int totalChildH; // needed for animation
1187       Fl_Image *cIcon[2], *bIcon[2], *lIcon;
1188       Fl_Color textColor;
1189       Fl_Font textFont;
1190       unsigned char textSize;  // the font size of the entry label text
1191       unsigned short textW, textH;  // how big the entry label actually is (stored within the node for performance reasons)
1192       int currentY; // needed for animation
1193       unsigned short currentH;
1194 
_widgetCB(Fl_Widget *,void * arg)1195       inline static void _widgetCB( Fl_Widget* /*w*/, void* arg )
1196 	{ ((Node*)arg)->widgetCB(); }
1197       void widgetCB();
1198      };
1199 
1200  protected:
1201 
_scrollCB(Fl_Widget *,void * arg)1202   inline static void _scrollCB( Fl_Widget* /*w*/, void* arg )
1203     { ((Flu_Tree_Browser*)arg)->redraw(); }
1204 
_timerRedrawCB(void * arg)1205   inline static void _timerRedrawCB( void *arg )
1206     { ((Flu_Tree_Browser*)arg)->timerRedrawCB(); }
1207   void timerRedrawCB();
1208 
_timerScrollCB(void * arg)1209   inline static void _timerScrollCB( void *arg )
1210     { ((Flu_Tree_Browser*)arg)->timerScrollCB(); }
1211   void timerScrollCB();
1212 
1213   void on_dnd_leave();
1214 
1215   void on_dnd_release();
1216 
1217   bool on_dnd_drag( int X, int Y );
1218 
1219   void on_dnd_drop( const Flu_DND_Event *e );
1220 
1221   /* override of Fl_Double_Window::draw() */
1222   void draw();
1223 
1224   Fl_Group *scrollBox;
1225   Fl_Scrollbar *scrollH, *scrollV;
1226   Fl_Group *_box;
1227   Node root;
1228   RData rdata;
1229   //int lastEvent;
1230   float autoScrollX, autoScrollY;
1231   bool scrolledTimerOn;
1232 
1233 };
1234 
1235 #endif
1236