2 // "$Id: Fl_Tree.cxx 8632 2011-05-04 02:59:50Z greg.ercolano $"
9 #include <FL/Fl_Tree.H>
10 #include <FL/Fl_Preferences.H>
12 //////////////////////
14 //////////////////////
16 // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK
17 // Copyright (C) 2009-2010 by Greg Ercolano.
19 // This library is free software; you can redistribute it and/or
20 // modify it under the terms of the GNU Library General Public
21 // License as published by the Free Software Foundation; either
22 // version 2 of the License, or (at your option) any later version.
24 // This library is distributed in the hope that it will be useful,
25 // but WITHOUT ANY WARRANTY; without even the implied warranty of
26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 // Library General Public License for more details.
29 // You should have received a copy of the GNU Library General Public
30 // License along with this library; if not, write to the Free Software
31 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
35 // INTERNAL: scroller callback
36 static void scroll_cb(Fl_Widget
*,void *data
) {
37 ((Fl_Tree
*)data
)->redraw();
40 // INTERNAL: Parse elements from path into an array of null terminated strings
41 // Handles escape characters.
43 // Return: arr[0]="aa", arr[1]="bb", arr[2]=0
44 // Caller must call free_path(arr).
46 static char **parse_path(const char *path
) {
47 while ( *path
== '/' ) path
++; // skip leading '/'
48 // First pass: identify, null terminate, and count separators
49 int seps
= 1; // separator count (1: first item)
50 int arrsize
= 1; // array size (1: first item)
51 char *save
= strdup(path
); // make copy we can modify
52 char *sin
= save
, *sout
= save
;
54 if ( *sin
== '\\' ) { // handle escape character
57 } else if ( *sin
== '/' ) { // handle submenu
62 } else { // all other chars
67 arrsize
++; // (room for terminating NULL)
68 // Second pass: create array, save nonblank elements
69 char **arr
= (char**)malloc(sizeof(char*) * arrsize
);
72 while ( seps
-- > 0 ) {
73 if ( *sin
) { arr
[t
++] = sin
; } // skips empty fields, e.g. '//'
74 sin
+= (strlen(sin
) + 1);
80 // INTERNAL: Free the array returned by parse_path()
81 static void free_path(char **arr
) {
83 if ( arr
[0] ) { free((void*)arr
[0]); }
88 // INTERNAL: Recursively descend tree hierarchy, accumulating total child count
89 static int find_total_children(Fl_Tree_Item
*item
, int count
=0) {
91 for ( int t
=0; t
<item
->children(); t
++ ) {
92 count
= find_total_children(item
->child(t
), count
);
98 Fl_Tree::Fl_Tree(int X
, int Y
, int W
, int H
, const char *L
) : Fl_Group(X
,Y
,W
,H
,L
) {
99 _root
= new Fl_Tree_Item(_prefs
);
100 _root
->parent(0); // we are root of tree
101 _root
->label("ROOT");
104 _callback_reason
= FL_TREE_REASON_NONE
;
105 _scrollbar_size
= 0; // 0: uses Fl::scrollbar_size()
107 color(FL_BACKGROUND2_COLOR
, FL_SELECTION_COLOR
);
108 when(FL_WHEN_CHANGED
);
109 _vscroll
= new Fl_Scrollbar(0,0,0,0); // will be resized by draw()
111 _vscroll
->type(FL_VERTICAL
);
113 _vscroll
->callback(scroll_cb
, (void*)this);
118 Fl_Tree::~Fl_Tree() {
119 if ( _root
) { delete _root
; _root
= 0; }
122 /// Adds a new item, given a 'menu style' path, eg: "/Parent/Child/item".
123 /// Any parent nodes that don't already exist are created automatically.
124 /// Adds the item based on the value of sortorder().
126 /// To specify items or submenus that contain slashes ('/' or '\')
127 /// use an escape character to protect them, e.g.
130 /// tree->add("/Holidays/Photos/12\\/25\\2010"); // Adds item "12/25/2010"
131 /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp"
134 /// \returns the child item created, or 0 on error.
136 Fl_Tree_Item
* Fl_Tree::add(const char *path
) {
137 if ( ! _root
) { // Create root if none
138 _root
= new Fl_Tree_Item(_prefs
);
140 _root
->label("ROOT");
142 char **arr
= parse_path(path
);
143 Fl_Tree_Item
*item
= _root
->add(_prefs
, arr
);
148 /// Inserts a new item above the specified Fl_Tree_Item, with the label set to 'name'.
149 /// \param[in] above -- the item above which to insert the new item. Must not be NULL.
150 /// \param[in] name -- the name of the new item
151 /// \returns the item that was added, or 0 if 'above' could not be found.
153 Fl_Tree_Item
* Fl_Tree::insert_above(Fl_Tree_Item
*above
, const char *name
) {
154 return(above
->insert_above(_prefs
, name
));
157 /// Insert a new item into a tree-item's children at a specified position.
159 /// \param[in] item The existing item to insert new child into. Must not be NULL.
160 /// \param[in] name The label for the new item
161 /// \param[in] pos The position of the new item in the child list
162 /// \returns the item that was added.
164 Fl_Tree_Item
* Fl_Tree::insert(Fl_Tree_Item
*item
, const char *name
, int pos
) {
165 return(item
->insert(_prefs
, name
, pos
));
168 /// Add a new child to a tree-item.
170 /// \param[in] item The existing item to add new child to. Must not be NULL.
171 /// \param[in] name The label for the new item
172 /// \returns the item that was added.
174 Fl_Tree_Item
* Fl_Tree::add(Fl_Tree_Item
*item
, const char *name
) {
175 return(item
->add(_prefs
, name
));
178 /// Find the item, given a menu style path, eg: "/Parent/Child/item".
179 /// There is both a const and non-const version of this method.
180 /// Const version allows pure const methods to use this method
181 /// to do lookups without causing compiler errors.
183 /// To specify items or submenus that contain slashes ('/' or '\')
184 /// use an escape character to protect them, e.g.
187 /// tree->add("/Holidays/Photos/12\\/25\\2010"); // Adds item "12/25/2010"
188 /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp"
191 /// \param[in] path -- the tree item's pathname to be found (e.g. "Flintstones/Fred")
192 /// \returns the item, or NULL if not found.
194 /// \see item_pathname()
196 Fl_Tree_Item
*Fl_Tree::find_item(const char *path
) {
197 if ( ! _root
) return(NULL
);
198 char **arr
= parse_path(path
);
199 Fl_Tree_Item
*item
= _root
->find_item(arr
);
204 /// A const version of Fl_Tree::find_item(const char *path)
205 const Fl_Tree_Item
*Fl_Tree::find_item(const char *path
) const {
206 if ( ! _root
) return(NULL
);
207 char **arr
= parse_path(path
);
208 const Fl_Tree_Item
*item
= _root
->find_item(arr
);
213 // Handle safe 'reverse string concatenation'.
214 // In the following we build the pathname from right-to-left,
215 // since we start at the child and work our way up to the root.
217 #define SAFE_RCAT(c) { \
218 slen += 1; if ( slen >= pathnamelen ) { pathname[0] = '\0'; return(-2); } \
222 /// Find the pathname for the specified \p item.
223 /// If \p item is NULL, root() is used.
224 /// The tree's root will be included in the pathname of showroot() is on.
225 /// Menu items or submenus that contain slashes ('/' or '\') in their names
226 /// will be escaped with a backslash. This is symmetrical with the add()
227 /// function which uses the same escape pattern to set names.
228 /// \param[in] pathname The string to use to return the pathname
229 /// \param[in] pathnamelen The maximum length of the string (including NULL). Must not be zero.
230 /// \param[in] item The item whose pathname is to be returned.
232 /// - 0 : OK (\p pathname returns the item's pathname)
233 /// - -1 : item not found (pathname="")
234 /// - -2 : pathname not large enough (pathname="")
237 int Fl_Tree::item_pathname(char *pathname
, int pathnamelen
, const Fl_Tree_Item
*item
) const {
239 item
= item
? item
: _root
;
240 if ( !item
) return(-1);
241 // Build pathname starting at end
242 char *s
= (pathname
+pathnamelen
-1);
243 int slen
= 0; // length of string compiled so far (including NULL)
246 if ( item
->is_root() && showroot() == 0 ) break; // don't include root in path if showroot() off
247 // Find name of current item
248 const char *name
= item
->label() ? item
->label() : "???"; // name for this item
249 int len
= strlen(name
);
250 // Add name to end of pathname[]
251 for ( --len
; len
>=0; len
-- ) {
252 SAFE_RCAT(name
[len
]); // rcat name of item
253 if ( name
[len
] == '/' || name
[len
] == '\\' ) {
254 SAFE_RCAT('\\'); // escape front or back slashes within name
257 SAFE_RCAT('/'); // rcat leading slash
258 item
= item
->parent(); // move up tree (NULL==root)
260 if ( *(++s
) == '/' ) ++s
; // leave off leading slash from pathname
261 if ( s
!= pathname
) memmove(pathname
, s
, slen
); // Shift down right-aligned string
265 /// Standard FLTK draw() method, handles draws the tree widget.
266 void Fl_Tree::draw() {
267 // Let group draw box+label but *NOT* children.
268 // We handle drawing children ourselves by calling each item's draw()
271 Fl_Group::draw_box();
272 Fl_Group::draw_label();
274 if ( ! _root
) return;
275 int cx
= x() + Fl::box_dx(box());
276 int cy
= y() + Fl::box_dy(box());
277 int cw
= w() - Fl::box_dw(box());
278 int ch
= h() - Fl::box_dh(box());
279 // These values are changed during drawing
280 // 'Y' will be the lowest point on the tree
281 int X
= cx
+ _prefs
.marginleft();
282 int Y
= cy
+ _prefs
.margintop() - (_vscroll
->visible() ? _vscroll
->value() : 0);
283 int W
= cw
- _prefs
.marginleft(); // - _prefs.marginright();
285 fl_push_clip(cx
,cy
,cw
,ch
);
287 fl_font(_prefs
.labelfont(), _prefs
.labelsize());
288 _root
->draw(X
, Y
, W
, this,
289 (Fl::focus()==this)?_item_focus
:0, // show focus item ONLY if Fl_Tree has focus
294 // Show vertical scrollbar?
295 int ydiff
= (Y
+_prefs
.margintop())-Ysave
; // ydiff=size of tree
296 int ytoofar
= (cy
+ch
) - Y
; // ytoofar -- scrolled beyond bottom (e.g. stow)
298 //printf("ydiff=%d ch=%d Ysave=%d ytoofar=%d value=%d\n",
299 //int(ydiff),int(ch),int(Ysave),int(ytoofar), int(_vscroll->value()));
301 if ( ytoofar
> 0 ) ydiff
+= ytoofar
;
302 if ( Ysave
<cy
|| ydiff
> ch
|| int(_vscroll
->value()) > 1 ) {
305 int scrollsize
= _scrollbar_size
? _scrollbar_size
: Fl::scrollbar_size();
306 int sx
= x()+w()-Fl::box_dx(box())-scrollsize
;
307 int sy
= y()+Fl::box_dy(box());
309 int sh
= h()-Fl::box_dh(box());
311 _vscroll
->range(0.0,ydiff
-ch
);
312 _vscroll
->resize(sx
,sy
,sw
,sh
);
313 _vscroll
->slider_size(float(ch
)/float(ydiff
));
315 _vscroll
->Fl_Slider::value(0);
318 fl_push_clip(cx
,cy
,cw
,ch
);
319 Fl_Group::draw_children(); // draws any FLTK children set via Fl_Tree::widget()
323 /// Returns next visible item above (dir==Fl_Up) or below (dir==Fl_Down) the specified \p item.
324 /// If \p item is 0, returns first() if \p dir is Fl_Up, or last() if \p dir is FL_Down.
326 /// \param[in] item The item above/below which we'll find the next visible item
327 /// \param[in] dir The direction to search. Can be FL_Up or FL_Down.
328 /// \returns The item found, or 0 if there's no visible items above/below the specified \p item.
330 Fl_Tree_Item
*Fl_Tree::next_visible_item(Fl_Tree_Item
*item
, int dir
) {
331 if ( ! item
) { // no start item?
332 item
= ( dir
== FL_Up
) ? last() : first(); // start at top or bottom
333 if ( ! item
) return(0);
334 if ( item
->visible_r() ) return(item
); // return first/last visible item
337 case FL_Up
: return(item
->prev_displayed(_prefs
));
338 case FL_Down
: return(item
->next_displayed(_prefs
));
339 default: return(item
->next_displayed(_prefs
));
343 /// Set the item that currently should have keyboard focus.
344 /// Handles calling redraw() to update the focus box (if it is visible).
346 /// \param[in] item The item that should take focus. If NULL, none will have focus.
348 void Fl_Tree::set_item_focus(Fl_Tree_Item
*item
) {
349 if ( _item_focus
!= item
) { // changed?
350 _item_focus
= item
; // update
351 if ( visible_focus() ) redraw(); // redraw to update focus box
355 /// Find the item that was clicked.
356 /// You should use callback_item() instead, which is fast,
357 /// and is meant to be used within a callback to determine the item clicked.
359 /// This method walks the entire tree looking for the first item that is
360 /// under the mouse (ie. at Fl::event_x()/Fl:event_y().
362 /// Use this method /only/ if you've subclassed Fl_Tree, and are receiving
363 /// events before Fl_Tree has been able to process and update callback_item().
365 /// \returns the item clicked, or 0 if no item was under the current event.
367 const Fl_Tree_Item
* Fl_Tree::find_clicked() const {
368 if ( ! _root
) return(NULL
);
369 return(_root
->find_clicked(_prefs
));
372 /// Set the item that was last clicked.
373 /// Should only be used by subclasses needing to change this value.
374 /// Normally Fl_Tree manages this value.
376 /// Deprecated: use callback_item() instead.
378 void Fl_Tree::item_clicked(Fl_Tree_Item
* val
) {
379 _callback_item
= val
;
382 /// Returns the first item in the tree.
384 /// Use this to walk the tree in the forward direction, eg:
386 /// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) {
387 /// printf("Item: %s\n", item->label());
391 /// \returns first item in tree, or 0 if none (tree empty).
392 /// \see first(),next(),last(),prev()
394 Fl_Tree_Item
* Fl_Tree::first() {
395 return(_root
); // first item always root
398 /// Return the next item after \p item, or 0 if no more items.
400 /// Use this code to walk the entire tree:
402 /// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) ) {
403 /// printf("Item: %s\n", item->label());
407 /// \param[in] item The item to use to find the next item. If NULL, returns 0.
408 /// \returns Next item in tree, or 0 if at last item.
410 /// \see first(),next(),last(),prev()
412 Fl_Tree_Item
*Fl_Tree::next(Fl_Tree_Item
*item
) {
413 if ( ! item
) return(0);
414 return(item
->next());
417 /// Return the previous item before \p item, or 0 if no more items.
419 /// This can be used to walk the tree in reverse, eg:
422 /// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->prev(item) ) {
423 /// printf("Item: %s\n", item->label());
427 /// \param[in] item The item to use to find the previous item. If NULL, returns 0.
428 /// \returns Previous item in tree, or 0 if at first item.
430 /// \see first(),next(),last(),prev()
432 Fl_Tree_Item
*Fl_Tree::prev(Fl_Tree_Item
*item
) {
433 if ( ! item
) return(0);
434 return(item
->prev());
437 /// Returns the last item in the tree.
439 /// This can be used to walk the tree in reverse, eg:
442 /// for ( Fl_Tree_Item *item = tree->last(); item; item = tree->prev() ) {
443 /// printf("Item: %s\n", item->label());
447 /// \returns last item in the tree, or 0 if none (tree empty).
449 /// \see first(),next(),last(),prev()
451 Fl_Tree_Item
* Fl_Tree::last() {
452 if ( ! _root
) return(0);
453 Fl_Tree_Item
*item
= _root
;
454 while ( item
->has_children() ) {
455 item
= item
->child(item
->children()-1);
460 /// Returns the first selected item in the tree.
462 /// Use this to walk the tree looking for all the selected items, eg:
465 /// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) {
466 /// printf("Item: %s\n", item->label());
470 /// \returns The next selected item, or 0 if there are no more selected items.
472 Fl_Tree_Item
*Fl_Tree::first_selected_item() {
473 return(next_selected_item(0));
476 /// Returns the next selected item after \p item.
477 /// If \p item is 0, search starts at the first item (root).
479 /// Use this to walk the tree looking for all the selected items, eg:
481 /// for ( Fl_Tree_Item *item = tree->first_selected_item(); item; item = tree->next_selected_item(item) ) {
482 /// printf("Item: %s\n", item->label());
486 /// \param[in] item The item to use to find the next selected item. If NULL, first() is used.
487 /// \returns The next selected item, or 0 if there are no more selected items.
489 Fl_Tree_Item
*Fl_Tree::next_selected_item(Fl_Tree_Item
*item
) {
491 if ( ! (item
= first()) ) return(0);
492 if ( item
->is_selected() ) return(item
);
494 while ( (item
= item
->next()) )
495 if ( item
->is_selected() )
500 /// Standard FLTK event handler for this widget.
501 int Fl_Tree::handle(int e
) {
503 // Developer note: Fl_Browser_::handle() used for reference here..
504 // #include <FL/names.h> // for event debugging
505 // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e);
506 if (e
== FL_ENTER
|| e
== FL_LEAVE
) return(1);
509 // FLTK tests if we want focus.
510 // If a nav key was used to give us focus, and we've got no saved
511 // focus widget, determine which item gets focus depending on nav key.
513 if ( ! _item_focus
) { // no focus established yet?
514 switch (Fl::event_key()) { // determine if focus was navigated..
515 case FL_Tab
: { // received focus via TAB?
516 if ( Fl::event_state(FL_SHIFT
) ) { // SHIFT-TAB similar to FL_Up
517 set_item_focus(next_visible_item(0, FL_Up
));
518 } else { // TAB similar to FL_Down
519 set_item_focus(next_visible_item(0, FL_Down
));
523 case FL_Left
: // received focus via LEFT or UP?
524 case FL_Up
: { // XK_ISO_Left_Tab
525 set_item_focus(next_visible_item(0, FL_Up
));
528 case FL_Right
: // received focus via RIGHT or DOWN?
531 set_item_focus(next_visible_item(0, FL_Down
));
536 if ( visible_focus() ) redraw(); // draw focus change
539 case FL_UNFOCUS
: { // FLTK telling us some other widget took focus.
540 if ( visible_focus() ) redraw(); // draw focus change
543 case FL_KEYBOARD
: { // keyboard shortcut
544 // Do shortcuts first or scrollbar will get them...
545 if (_prefs
.selectmode() > FL_TREE_SELECT_NONE
) {
546 if ( !_item_focus
) {
547 set_item_focus(first());
550 int ekey
= Fl::event_key();
552 case FL_Enter
: // ENTER: selects current item only
554 if ( when() & ~FL_WHEN_ENTER_KEY
) {
555 select_only(_item_focus
);
556 show_item(_item_focus
); // STR #2426
560 case ' ': // toggle selection state
561 switch ( _prefs
.selectmode() ) {
562 case FL_TREE_SELECT_NONE
:
564 case FL_TREE_SELECT_SINGLE
:
565 if ( ! _item_focus
->is_selected() ) // not selected?
566 select_only(_item_focus
); // select only this
568 deselect_all(); // select nothing
570 case FL_TREE_SELECT_MULTI
:
571 select_toggle(_item_focus
);
575 case FL_Right
: // open children (if any)
576 case FL_Left
: { // close children (if any)
578 if ( ekey
== FL_Right
&& _item_focus
->is_close() ) {
583 } else if ( ekey
== FL_Left
&& _item_focus
->is_open() ) {
593 case FL_Up
: // next item up
594 case FL_Down
: { // next item down
595 set_item_focus(next_visible_item(_item_focus
, ekey
)); // next item up|dn
596 if ( _item_focus
) { // item in focus?
598 int itemtop
= _item_focus
->y();
599 int itembot
= _item_focus
->y()+_item_focus
->h();
600 if ( itemtop
< y() ) { show_item_top(_item_focus
); }
601 if ( itembot
> y()+h() ) { show_item_bottom(_item_focus
); }
603 if ( _prefs
.selectmode() == FL_TREE_SELECT_MULTI
&& // multiselect on?
604 (Fl::event_state() & FL_SHIFT
) && // shift key?
605 ! _item_focus
->is_selected() ) { // not already selected?
606 select(_item_focus
); // extend selection..
619 // Let Fl_Group take a shot at handling the event
620 if (Fl_Group::handle(e
)) {
621 return(1); // handled? don't continue below
624 // Handle events the child FLTK widgets didn't need
626 static Fl_Tree_Item
*lastselect
= 0;
627 // fprintf(stderr, "ERCODEBUG: Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING
628 if ( ! _root
) return(ret
);
630 case FL_PUSH
: { // clicked on a tree item?
631 if (Fl::visible_focus() && handle(FL_FOCUS
)) {
635 Fl_Tree_Item
*o
= _root
->find_clicked(_prefs
);
637 set_item_focus(o
); // becomes new focus widget
640 if ( Fl::event_button() == FL_LEFT_MOUSE
) {
641 if ( o
->event_on_collapse_icon(_prefs
) ) { // collapse icon clicked?
643 } else if ( o
->event_on_label(_prefs
) && // label clicked?
644 (!o
->widget() || !Fl::event_inside(o
->widget())) && // not inside widget
645 (!_vscroll
->visible() || !Fl::event_inside(_vscroll
)) ) { // not on scroller
646 switch ( _prefs
.selectmode() ) {
647 case FL_TREE_SELECT_NONE
:
649 case FL_TREE_SELECT_SINGLE
:
652 case FL_TREE_SELECT_MULTI
: {
653 if ( Fl::event_state() & FL_SHIFT
) { // SHIFT+PUSH?
654 select(o
); // add to selection
655 } else if ( Fl::event_state() & FL_CTRL
) { // CTRL+PUSH?
656 select_toggle(o
); // toggle selection state
657 lastselect
= o
; // save toggled item (prevent oscillation)
669 // do the scrolling first:
670 int my
= Fl::event_y();
671 if ( my
< y() ) { // above top?
672 int p
= vposition()-(y()-my
);
675 } else if ( my
> (y()+h()) ) { // below bottom?
676 int p
= vposition()+(my
-y()-h());
677 if ( p
> (int)_vscroll
->maximum() ) p
= (int)_vscroll
->maximum();
680 if ( Fl::event_button() != FL_LEFT_MOUSE
) break;
681 Fl_Tree_Item
*o
= _root
->find_clicked(_prefs
);
683 set_item_focus(o
); // becomes new focus widget
686 // Item's label clicked?
687 if ( o
->event_on_label(_prefs
) &&
688 (!o
->widget() || !Fl::event_inside(o
->widget())) &&
689 (!_vscroll
->visible() || !Fl::event_inside(_vscroll
)) ) {
690 // Handle selection behavior
691 switch ( _prefs
.selectmode() ) {
692 case FL_TREE_SELECT_NONE
: break; // no selection changes
693 case FL_TREE_SELECT_SINGLE
:
696 case FL_TREE_SELECT_MULTI
:
697 if ( Fl::event_state() & FL_CTRL
&& // CTRL-DRAG: toggle?
698 lastselect
!= o
) { // not already toggled from last microdrag?
699 select_toggle(o
); // toggle selection
700 lastselect
= o
; // save we toggled it (prevents oscillation)
702 select(o
); // select this
713 /// Deselect \p item and all its children.
714 /// If item is NULL, first() is used.
715 /// Handles calling redraw() if anything was changed.
716 /// Invokes the callback depending on the value of optional parameter \p docallback.
718 /// The callback can use callback_item() and callback_reason() respectively to determine
719 /// the item changed and the reason the callback was called.
721 /// \param[in] item The item that will be deselected (along with all its children).
722 /// If NULL, first() is used.
723 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
724 /// - 0 - the callback() is not invoked
725 /// - 1 - the callback() is invoked for each item that changed state,
726 /// callback_reason() will be FL_TREE_REASON_DESELECTED
728 /// \returns count of how many items were actually changed to the deselected state.
730 int Fl_Tree::deselect_all(Fl_Tree_Item
*item
, int docallback
) {
731 item
= item
? item
: first(); // NULL? use first()
732 if ( ! item
) return(0);
735 if ( item
->is_selected() )
736 if ( deselect(item
, docallback
) )
738 // Deselect its children
739 for ( int t
=0; t
<item
->children(); t
++ ) {
740 count
+= deselect_all(item
->child(t
), docallback
); // recurse
745 /// Select \p item and all its children.
746 /// If item is NULL, first() is used.
747 /// Handles calling redraw() if anything was changed.
748 /// Invokes the callback depending on the value of optional parameter \p docallback.
750 /// The callback can use callback_item() and callback_reason() respectively to determine
751 /// the item changed and the reason the callback was called.
753 /// \param[in] item The item that will be selected (along with all its children).
754 /// If NULL, first() is used.
755 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
756 /// - 0 - the callback() is not invoked
757 /// - 1 - the callback() is invoked for each item that changed state,
758 /// callback_reason() will be FL_TREE_REASON_SELECTED
759 /// \returns count of how many items were actually changed to the selected state.
761 int Fl_Tree::select_all(Fl_Tree_Item
*item
, int docallback
) {
762 item
= item
? item
: first(); // NULL? use first()
763 if ( ! item
) return(0);
766 if ( !item
->is_selected() )
767 if ( select(item
, docallback
) )
769 // Select its children
770 for ( int t
=0; t
<item
->children(); t
++ ) {
771 count
+= select_all(item
->child(t
), docallback
); // recurse
776 /// Select only the specified \p item, deselecting all others that might be selected.
777 /// If item is 0, first() is used.
778 /// Handles calling redraw() if anything was changed.
779 /// Invokes the callback depending on the value of optional parameter \p docallback.
781 /// The callback can use callback_item() and callback_reason() respectively to determine
782 /// the item changed and the reason the callback was called.
784 /// \param[in] selitem The item to be selected. If NULL, first() is used.
785 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
786 /// - 0 - the callback() is not invoked
787 /// - 1 - the callback() is invoked for each item that changed state,
788 /// callback_reason() will be either FL_TREE_REASON_SELECTED or
789 /// FL_TREE_REASON_DESELECTED
790 /// \returns the number of items whose selection states were changed, if any.
792 int Fl_Tree::select_only(Fl_Tree_Item
*selitem
, int docallback
) {
793 selitem
= selitem
? selitem
: first(); // NULL? use first()
794 if ( ! selitem
) return(0);
796 for ( Fl_Tree_Item
*item
= first(); item
; item
= item
->next() ) {
797 if ( item
== selitem
) {
798 if ( item
->is_selected() ) continue; // don't count if already selected
799 select(item
, docallback
);
802 if ( item
->is_selected() ) {
803 deselect(item
, docallback
);
811 /// Adjust the vertical scroll bar so that \p item is visible
812 /// \p yoff pixels from the top of the Fl_Tree widget's display.
814 /// For instance, yoff=0 will position the item at the top.
816 /// If yoff is larger than the vertical scrollbar's limit,
817 /// the value will be clipped. So if yoff=100, but scrollbar's max
818 /// is 50, then 50 will be used.
820 /// \param[in] item The item to be shown. If NULL, first() is used.
821 /// \param[in] yoff The pixel offset from the top for the displayed position.
823 /// \see show_item_top(), show_item_middle(), show_item_bottom()
825 void Fl_Tree::show_item(Fl_Tree_Item
*item
, int yoff
) {
826 item
= item
? item
: first();
828 int newval
= item
->y() - y() - yoff
+ (int)_vscroll
->value();
829 if ( newval
< _vscroll
->minimum() ) newval
= (int)_vscroll
->minimum();
830 if ( newval
> _vscroll
->maximum() ) newval
= (int)_vscroll
->maximum();
831 _vscroll
->value(newval
);
835 /// See if \p item is currently displayed on-screen (visible within the widget).
836 /// This can be used to detect if the item is scrolled off-screen.
837 /// Checks to see if the item's vertical position is within the top and bottom
838 /// edges of the display window. This does NOT take into account the hide()/show()
839 /// or open()/close() status of the item.
841 /// \param[in] item The item to be checked. If NULL, first() is used.
842 /// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree.
844 int Fl_Tree::displayed(Fl_Tree_Item
*item
) {
845 item
= item
? item
: first();
846 if (!item
) return(0);
847 return( (item
->y() >= y()) && (item
->y() <= (y()+h()-item
->h())) ? 1 : 0);
850 /// Adjust the vertical scroll bar to show \p item at the top
851 /// of the display IF it is currently off-screen (e.g. show_item_top()).
852 /// If it is already on-screen, no change is made.
854 /// \param[in] item The item to be shown. If NULL, first() is used.
856 /// \see show_item_top(), show_item_middle(), show_item_bottom()
858 void Fl_Tree::show_item(Fl_Tree_Item
*item
) {
859 item
= item
? item
: first();
861 if ( displayed(item
) ) return;
865 /// Adjust the vertical scrollbar so that \p item is at the top of the display.
867 /// \param[in] item The item to be shown. If NULL, first() is used.
869 void Fl_Tree::show_item_top(Fl_Tree_Item
*item
) {
870 item
= item
? item
: first();
871 if (item
) show_item(item
, 0);
874 /// Adjust the vertical scrollbar so that \p item is in the middle of the display.
876 /// \param[in] item The item to be shown. If NULL, first() is used.
878 void Fl_Tree::show_item_middle(Fl_Tree_Item
*item
) {
879 item
= item
? item
: first();
880 if (item
) show_item(item
, (h()/2)-(item
->h()/2));
883 /// Adjust the vertical scrollbar so that \p item is at the bottom of the display.
885 /// \param[in] item The item to be shown. If NULL, first() is used.
887 void Fl_Tree::show_item_bottom(Fl_Tree_Item
*item
) {
888 item
= item
? item
: first();
889 if (item
) show_item(item
, h()-item
->h());
892 /// Returns the vertical scroll position as a pixel offset.
893 /// The position returned is how many pixels of the tree are scrolled off the top edge
894 /// of the screen. Example: A position of '3' indicates the top 3 pixels of
895 /// the tree are scrolled off the top edge of the screen.
896 /// \see vposition(), hposition()
898 int Fl_Tree::vposition() const {
899 return((int)_vscroll
->value());
902 /// Sets the vertical scroll offset to position \p pos.
903 /// The position is how many pixels of the tree are scrolled off the top edge
904 /// of the screen. Example: A position of '3' scrolls the top three pixels of
905 /// the tree off the top edge of the screen.
906 /// \param[in] pos The vertical position (in pixels) to scroll the browser to.
908 void Fl_Tree::vposition(int pos
) {
909 if (pos
< 0) pos
= 0;
910 if (pos
> _vscroll
->maximum()) pos
= (int)_vscroll
->maximum();
911 if (pos
== _vscroll
->value()) return;
912 _vscroll
->value(pos
);
916 /// Displays \p item, scrolling the tree as necessary.
917 /// \param[in] item The item to be displayed. If NULL, first() is used.
919 void Fl_Tree::display(Fl_Tree_Item
*item
) {
920 item
= item
? item
: first();
921 if (item
) show_item_middle(item
);
925 * Read a preferences database into the tree widget.
926 * A preferences database is a hierarchical collection of data which can be
927 * directly loaded into the tree view for inspection.
928 * \param[in] prefs the Fl_Preferences database
930 void Fl_Tree::load(Fl_Preferences
&prefs
)
932 int i
, j
, n
, pn
= strlen(prefs
.path());
934 const char *path
= prefs
.path();
935 if (strcmp(path
, ".")==0)
936 path
+= 1; // root path is empty
938 path
+= 2; // child path starts with "./"
940 for (i
=0; i
<n
; i
++) {
941 Fl_Preferences
prefsChild(prefs
, i
);
942 add(prefsChild
.path()+2); // children always start with "./"
946 for (i
=0; i
<n
; i
++) {
947 // We must remove all fwd slashes in the key and value strings. Replace with backslash.
948 char *key
= strdup(prefs
.entry(i
));
949 int kn
= strlen(key
);
950 for (j
=0; j
<kn
; j
++) {
951 if (key
[j
]=='/') key
[j
]='\\';
953 char *val
; prefs
.get(key
, val
, "");
954 int vn
= strlen(val
);
955 for (j
=0; j
<vn
; j
++) {
956 if (val
[j
]=='/') val
[j
]='\\';
959 int sze
= pn
+ strlen(key
) + vn
;
960 p
= (char*)malloc(sze
+5);
961 sprintf(p
, "%s/%s = %s", path
, key
, val
);
963 int sze
= pn
+ strlen(key
) + 40;
964 p
= (char*)malloc(sze
+5);
965 sprintf(p
, "%s/%s = %.40s...", path
, key
, val
);
967 add(p
[0]=='/'?p
+1:p
);
975 // End of "$Id: Fl_Tree.cxx 8632 2011-05-04 02:59:50Z greg.ercolano $".