1 //
2 // "$Id$"
3 //
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include <FL/Fl_Tree.H>
10 #include <FL/Fl_Preferences.H>
11
12 //////////////////////
13 // Fl_Tree.cxx
14 //////////////////////
15 //
16 // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK
17 // Copyright (C) 2009-2010 by Greg Ercolano.
18 //
19 // This library is free software. Distribution and use rights are outlined in
20 // the file "COPYING" which should have been included with this file. If this
21 // file is missing or damaged, see the license at:
22 //
23 // http://www.fltk.org/COPYING.php
24 //
25 // Please report all bugs and problems on the following page:
26 //
27 // http://www.fltk.org/str.php
28 //
29
30 // INTERNAL: scroller callback (hor+vert scroll)
scroll_cb(Fl_Widget *,void * data)31 static void scroll_cb(Fl_Widget*,void *data) {
32 ((Fl_Tree*)data)->redraw();
33 }
34
35 // INTERNAL: Parse elements from 'path' into an array of null terminated strings
36 // Handles escape characters, ignores multiple /'s.
37 // Path="/aa/bb", returns arr[0]="aa", arr[1]="bb", arr[2]=0.
38 // Caller must call free_path(arr).
39 //
parse_path(const char * path)40 static char **parse_path(const char *path) {
41 size_t len = strlen(path);
42 char *cp = new char[(len+1)], *word = cp, *s = cp; // freed below or in free_path()
43 char **ap = new char*[(len+1)], **arr = ap; // overallocates arr[]
44 while (1) {
45 if (*path =='/' || *path == 0) { // handle path sep or eos
46 if (word != s) { *s++ = 0; *arr++= word; word = s; }
47 if ( !*path++) break; else continue; // eos? done, else cont
48 } else if ( *path == '\\' ) { // handle escape
49 if ( *(++path) ) { *s++ = *path++; } else continue;
50 } else { *s++ = *path++; } // handle normal char
51 }
52 *arr = 0;
53 if ( arr == ap ) delete[] cp; // empty arr[]? delete since free_path() can't
54 return ap;
55 }
56
57 // INTERNAL: Free an array 'arr' returned by parse_path()
free_path(char ** arr)58 static void free_path(char **arr) {
59 if ( arr ) {
60 if ( arr[0] ) { delete[] arr[0]; } // deletes cp in parse_path
61 delete[] arr; // deletes ptr array
62 }
63 }
64
65 #if 0 /* unused code -- STR #3169 */
66 // INTERNAL: Recursively descend 'item's tree hierarchy
67 // accumulating total child 'count'
68 //
69 static int find_total_children(Fl_Tree_Item *item, int count=0) {
70 count++;
71 for ( int t=0; t<item->children(); t++ ) {
72 count = find_total_children(item->child(t), count);
73 }
74 return(count);
75 }
76 #endif
77
78 /// Constructor.
Fl_Tree(int X,int Y,int W,int H,const char * L)79 Fl_Tree::Fl_Tree(int X, int Y, int W, int H, const char *L) : Fl_Group(X,Y,W,H,L) {
80 #if FLTK_ABI_VERSION >= 10303
81 _root = new Fl_Tree_Item(this);
82 #else
83 _root = new Fl_Tree_Item(_prefs);
84 #endif
85 _root->parent(0); // we are root of tree
86 _root->label("ROOT");
87 _item_focus = 0;
88 _callback_item = 0;
89 _callback_reason = FL_TREE_REASON_NONE;
90 _scrollbar_size = 0; // 0: uses Fl::scrollbar_size()
91
92 #if FLTK_ABI_VERSION >= 10301
93 // NEW
94 _lastselect = 0;
95 #else /*FLTK_ABI_VERSION*/
96 // OLD: data initialized static inside handle()
97 #endif /*FLTK_ABI_VERSION*/
98
99 box(FL_DOWN_BOX);
100 color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
101 when(FL_WHEN_CHANGED);
102 int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
103 _vscroll = new Fl_Scrollbar(X+W-scrollsize,Y,scrollsize,H);
104 _vscroll->hide();
105 _vscroll->type(FL_VERTICAL);
106 _vscroll->step(1);
107 _vscroll->callback(scroll_cb, (void*)this);
108 #if FLTK_ABI_VERSION >= 10303
109 _hscroll = new Fl_Scrollbar(X,Y+H-scrollsize,W,scrollsize);
110 _hscroll->hide();
111 _hscroll->type(FL_HORIZONTAL);
112 _hscroll->step(1);
113 _hscroll->callback(scroll_cb, (void*)this);
114 _tox = _tix = X + Fl::box_dx(box());
115 _toy = _tiy = Y + Fl::box_dy(box());
116 _tow = _tiw = W - Fl::box_dw(box());
117 _toh = _tih = H - Fl::box_dh(box());
118 _tree_w = -1;
119 _tree_h = -1;
120 #endif
121 end();
122 }
123
124 /// Destructor.
~Fl_Tree()125 Fl_Tree::~Fl_Tree() {
126 if ( _root ) { delete _root; _root = 0; }
127 }
128
129 /// Extend the selection between and including \p 'from' and \p 'to'
130 /// depending on direction \p 'dir', \p 'val', and \p 'visible'.
131 ///
132 /// Efficient: does not walk entire tree; starts with \p 'from' and stops
133 /// at \p 'to' while moving in direction \p 'dir'. Dir must be specified though.
134 #if FLTK_ABI_VERSION >= 10303
135 ///
136 /// If dir cannot be known in advance, such as during SHIFT-click operations,
137 /// the method extend_selection(Fl_Tree_Item*,Fl_Tree_Item*,int,bool)
138 /// should be used.
139 #endif
140 ///
141 /// Handles calling redraw() if anything changed.
142 ///
143 /// \param[in] from Starting item
144 /// \param[in] to Ending item
145 /// \param[in] dir Direction to extend selection (FL_Up or FL_Down)
146 /// \param[in] val 0=deselect, 1=select, 2=toggle
147 /// \param[in] visible true=affect only open(), visible items,<br>
148 /// false=affect open or closed items (default)
149 /// \returns The number of items whose selection states were changed, if any.
150 /// \version 1.3.3
151 ///
extend_selection_dir(Fl_Tree_Item * from,Fl_Tree_Item * to,int dir,int val,bool visible)152 int Fl_Tree::extend_selection_dir(Fl_Tree_Item *from, Fl_Tree_Item *to,
153 int dir, int val, bool visible ) {
154 int changed = 0;
155 for (Fl_Tree_Item *item=from; item; item = next_item(item, dir, visible) ) {
156 switch (val) {
157 case 0:
158 if ( deselect(item, when()) ) ++changed;
159 break;
160 case 1:
161 if ( select(item, when()) ) ++changed;
162 break;
163 case 2:
164 select_toggle(item, when());
165 ++changed; // toggle always involves a change
166 break;
167 }
168 if ( item==to ) break;
169 }
170 return(changed);
171 }
172
173 /// Extend a selection between \p 'from' and \p 'to' depending on \p 'visible'.
174 ///
175 /// Similar to the more efficient
176 /// extend_selection_dir(Fl_Tree_Item*,Fl_Tree_Item*,int dir,int val,bool vis)
177 /// method, but direction (up or down) doesn't need to be known.<br>
178 /// We're less efficient because we search the tree for to/from, then operate
179 /// on items in between. The more efficient method avoids the "search",
180 /// but necessitates a direction to be specified to find \p 'to'.<br>
181 /// Used by SHIFT-click to extend a selection between two items inclusive.<br>
182 /// Handles calling redraw() if anything changed.
183 ///
184 /// \param[in] from Starting item
185 /// \param[in] to Ending item
186 /// \param[in] val Select or deselect items (0=deselect, 1=select, 2=toggle)
187 /// \param[in] visible true=affect only open(), visible items,<br>
188 /// false=affect open or closed items (default)
189 /// \returns The number of items whose selection states were changed, if any.
190 #if FLTK_ABI_VERSION >= 10303
191 /// \version 1.3.3 ABI feature
extend_selection(Fl_Tree_Item * from,Fl_Tree_Item * to,int val,bool visible)192 int Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to,
193 int val, bool visible) {
194 #else
195 /// \note Made public in 1.3.3 ABI
196 // Adding overload if not at least one overload breaks ABI, so avoid
197 // by making a private function until ABI can change..
198 int Fl_Tree::extend_selection__(Fl_Tree_Item *from, Fl_Tree_Item *to,
199 int val, bool visible) {
200 #endif
201 int changed = 0;
202 if ( from == to ) {
203 if ( visible && !from->is_visible() ) return(0); // do nothing
204 switch (val) {
205 case 0:
206 if ( deselect(from, when()) ) ++changed;
207 break;
208 case 1:
209 if ( select(from, when()) ) ++changed;
210 break;
211 case 2:
212 select_toggle(from, when());
213 ++changed; // always changed
214 break;
215 }
216 return(changed);
217 }
218 char on = 0;
219 for ( Fl_Tree_Item *item = first(); item; item = item->next_visible(_prefs) ) {
220 if ( visible && !item->is_visible() ) continue;
221 if ( on || (item == from) || (item == to) ) {
222 switch (val) {
223 case 0:
224 if ( deselect(item, when()) ) ++changed;
225 break;
226 case 1:
227 if ( select(item, when()) ) ++changed;
228 break;
229 case 2:
230 select_toggle(item, when());
231 ++changed; // toggle always involves a change
232 break;
233 }
234 if ( (item == from) || (item == to) ) {
235 on ^= 1;
236 if ( !on ) break; // done
237 }
238 }
239 }
240 return(changed);
241 }
242
243 #if FLTK_ABI_VERSION >= 10303
244 // not needed, above overload handles this
245 #else
246 /// Extend a selection between \p 'from' and \p 'to'.
247 /// Extends selection for items and all children, visible ('open') or not.
248 /// Walks entire tree from top to bottom looking for \p 'from' and \p 'to'.
249 /// \version 1.3.0
250 ///
251 void Fl_Tree::extend_selection(Fl_Tree_Item *from, Fl_Tree_Item *to) {
252 const int val = 1; // 0=clr, 1=set, 2=toggle
253 const bool visible = false; // true=only 'open' items, false='open' or 'closed'
254 extend_selection__(from, to, val, visible); // use private method until we can release it
255 }
256 #endif
257
258 /// Standard FLTK event handler for this widget.
259 /// \todo add Fl_Widget_Tracker (see Fl_Browser_.cxx::handle())
260 int Fl_Tree::handle(int e) {
261 if (e == FL_NO_EVENT) return(0); // XXX: optimize to prevent slow resizes on large trees!
262 int ret = 0;
263 char is_shift = Fl::event_state() & FL_SHIFT ? 1 : 0;
264 char is_ctrl = Fl::event_state() & FL_CTRL ? 1 : 0;
265 char is_command = Fl::event_state() & FL_COMMAND ? 1 : 0; // ctrl on win/lin, 'Command' on mac
266 #if FLTK_ABI_VERSION >= 10301
267 // NEW: data inside Fl_Tree
268 #else /*FLTK_ABI_VERSION*/
269 // OLD:
270 static Fl_Tree_Item *_lastselect = 0; // used to extend selections
271 #endif /*FLTK_ABI_VERSION*/
272 // Developer note: Fl_Browser_::handle() used for reference here..
273 // #include <FL/names.h> // for event debugging
274 // fprintf(stderr, "DEBUG: %s (%d)\n", fl_eventnames[e], e);
275
276 if (e == FL_ENTER || e == FL_LEAVE) return(1);
277 switch (e) {
278 case FL_FOCUS: {
279 // FLTK tests if we want focus.
280 // If a nav key was used to give us focus, and we've got no saved
281 // focus widget, determine which item gets focus depending on nav key.
282 //
283 if ( ! _item_focus ) { // no focus established yet?
284 switch (Fl::event_key()) { // determine if focus was navigated..
285 case FL_Tab: { // received focus via TAB?
286 int updown = is_shift ? FL_Up : FL_Down; // SHIFT-TAB similar to Up, TAB similar to Down
287 set_item_focus(next_visible_item(0, updown));
288 break;
289 }
290 case FL_Left: // received focus via LEFT or UP?
291 case FL_Up: { // XK_ISO_Left_Tab
292 set_item_focus(next_visible_item(0, FL_Up));
293 break;
294 }
295 case FL_Right: // received focus via RIGHT or DOWN?
296 case FL_Down:
297 default: {
298 set_item_focus(next_visible_item(0, FL_Down));
299 break;
300 }
301 }
302 }
303 if ( visible_focus() ) redraw(); // draw focus change
304 return(1);
305 }
306 case FL_UNFOCUS: { // FLTK telling us some other widget took focus.
307 if ( visible_focus() ) redraw(); // draw focus change
308 return(1);
309 }
310 case FL_KEYBOARD: { // keyboard shortcut
311 // Do shortcuts first or scrollbar will get them...
312 if ( (Fl::focus() == this) && // tree has focus?
313 _prefs.selectmode() > FL_TREE_SELECT_NONE ) { // select mode that supports kb events?
314 if ( !_item_focus ) { // no current focus item?
315 set_item_focus(first_visible_item()); // use first vis item
316 if ( Fl::event_key() == FL_Up || // Up or down?
317 Fl::event_key() == FL_Down ) // ..if so, already did 'motion'
318 return(1); // ..so just return.
319 }
320 if ( _item_focus ) {
321 int ekey = Fl::event_key();
322 switch (ekey) {
323 case FL_Enter: // ENTER: toggle open/close
324 case FL_KP_Enter: {
325 open_toggle(_item_focus, when()); // toggle item in focus
326 return(1); // done, we handled key
327 }
328 case ' ': // SPACE: change selection state
329 switch ( _prefs.selectmode() ) {
330 case FL_TREE_SELECT_NONE:
331 break; // ignore, let group have shot at event
332 case FL_TREE_SELECT_SINGLE:
333 case FL_TREE_SELECT_SINGLE_DRAGGABLE:
334 if ( is_ctrl ) { // CTRL-SPACE: (single mode) toggle
335 if ( ! _item_focus->is_selected() ) {
336 select_only(_item_focus, when());
337 } else {
338 deselect_all(0, when());
339 }
340 } else {
341 select_only(_item_focus, when()); // SPACE: (single mode) select only
342 }
343 _lastselect = _item_focus;
344 return(1); // done, we handled key
345 case FL_TREE_SELECT_MULTI:
346 if ( is_ctrl ) {
347 select_toggle(_item_focus, when()); // CTRL-SPACE: (multi mode) toggle selection
348 } else {
349 select(_item_focus, when()); // SPACE: (multi-mode) select
350 }
351 _lastselect = _item_focus;
352 return(1); // done, we handled key
353 }
354 break;
355 case FL_Right: // RIGHT: open children (if any)
356 case FL_Left: { // LEFT: close children (if any)
357 if ( _item_focus ) {
358 if ( ekey == FL_Right && _item_focus->is_close() ) {
359 open(_item_focus); // open closed item
360 ret = 1;
361 } else if ( ekey == FL_Left && _item_focus->is_open() ) {
362 close(_item_focus); // close open item
363 ret = 1;
364 }
365 return(1);
366 }
367 break;
368 }
369 case FL_Up: // UP: next item up, or extend selection up
370 case FL_Down: { // DOWN: next item down, or extend selection down
371 set_item_focus(next_visible_item(_item_focus, ekey)); // next item up|dn
372 if ( _item_focus ) { // item in focus?
373 // Autoscroll
374 int itemtop = _item_focus->y();
375 int itembot = _item_focus->y()+_item_focus->h();
376 if ( itemtop < y() ) { show_item_top(_item_focus); }
377 if ( itembot > y()+h() ) { show_item_bottom(_item_focus); }
378 // Extend selection
379 if ( _prefs.selectmode() == FL_TREE_SELECT_MULTI && // multiselect on?
380 is_shift && // shift key?
381 ! _item_focus->is_selected() ) { // not already selected?
382 select(_item_focus, when()); // extend selection..
383 _lastselect = _item_focus;
384 }
385 return(1);
386 }
387 break;
388 }
389 case 'a':
390 case 'A': {
391 if ( is_command ) { // ^A (win/linux), Meta-A (mac)
392 switch ( _prefs.selectmode() ) {
393 case FL_TREE_SELECT_NONE:
394 case FL_TREE_SELECT_SINGLE:
395 case FL_TREE_SELECT_SINGLE_DRAGGABLE:
396 break;
397 case FL_TREE_SELECT_MULTI:
398 // Do a 'select all'
399 select_all();
400 _lastselect = first_visible_item();
401 take_focus();
402 return(1);
403 }
404 }
405 break;
406 }
407 }
408 }
409 }
410 break;
411 }
412 }
413
414 // Let Fl_Group take a shot at handling the event
415 if (Fl_Group::handle(e)) {
416 return(1); // handled? don't continue below
417 }
418
419 // Handle events the child FLTK widgets didn't need
420
421 // fprintf(stderr, "Fl_Tree::handle(): Event was %s (%d)\n", fl_eventnames[e], e); // DEBUGGING
422 if ( ! _root ) return(ret);
423 static int last_my = 0;
424 switch ( e ) {
425 case FL_PUSH: { // clicked on tree
426 last_my = Fl::event_y(); // save for dragging direction..
427 if (Fl::visible_focus() && handle(FL_FOCUS)) Fl::focus(this);
428 #if FLTK_ABI_VERSION >= 10303
429 Fl_Tree_Item *item = _root->find_clicked(_prefs, 0);
430 #else
431 Fl_Tree_Item *item = _root->find_clicked(_prefs);
432 #endif
433 if ( !item ) { // clicked, but not on an item?
434 _lastselect = 0;
435 switch ( _prefs.selectmode() ) {
436 case FL_TREE_SELECT_NONE:
437 break;
438 case FL_TREE_SELECT_SINGLE:
439 case FL_TREE_SELECT_SINGLE_DRAGGABLE:
440 case FL_TREE_SELECT_MULTI:
441 deselect_all();
442 break;
443 }
444 break;
445 }
446 set_item_focus(item); // becomes new focus widget, calls redraw() if needed
447 ret |= 1; // handled
448 if ( Fl::event_button() == FL_LEFT_MOUSE ) {
449 if ( item->event_on_collapse_icon(_prefs) ) { // collapse icon clicked?
450 open_toggle(item); // toggle open (handles redraw)
451 } else if ( item->event_on_label(_prefs) && // label clicked?
452 (!item->widget() || !Fl::event_inside(item->widget())) ) { // not inside widget
453 switch ( _prefs.selectmode() ) {
454 case FL_TREE_SELECT_NONE:
455 break;
456 case FL_TREE_SELECT_SINGLE:
457 case FL_TREE_SELECT_SINGLE_DRAGGABLE:
458 select_only(item, when()); // select only this item (handles redraw)
459 _lastselect = item;
460 break;
461 case FL_TREE_SELECT_MULTI: {
462 if ( is_shift ) { // SHIFT+PUSH?
463 if ( _lastselect ) {
464 int val = is_ctrl ? 2 : 1;
465 bool visible = true;
466 #if FLTK_ABI_VERSION >= 10303
467 extend_selection(_lastselect, item, val, visible);
468 #else
469 extend_selection__(_lastselect, item, val, visible);
470 #endif
471 } else {
472 select(item); // add to selection
473 }
474 } else if ( is_ctrl ) { // CTRL+PUSH?
475 select_toggle(item, when()); // toggle selection state
476 } else {
477 select_only(item, when());
478 }
479 _lastselect = item;
480 break;
481 }
482 }
483 }
484 }
485 break;
486 }
487 case FL_DRAG: {
488 // Do scrolling first..
489
490 // Detect up/down dragging
491 int my = Fl::event_y();
492 int dir = (my>last_my) ? FL_Down : FL_Up;
493 last_my = my;
494
495 // Handle autoscrolling
496 if ( my < y() ) { // Above top?
497 dir = FL_Up; // ..going up
498 int p = vposition()-(y()-my); // ..position above us
499 if ( p < 0 ) p = 0; // ..don't go above 0
500 vposition(p); // ..scroll to new position
501 } else if ( my > (y()+h()) ) { // Below bottom?
502 dir = FL_Down; // ..going down
503 int p = vposition()+(my-y()-h()); // ..position below us
504 if ( p > (int)_vscroll->maximum() ) // ..don't go below bottom
505 p = (int)_vscroll->maximum();
506 vposition(p); // ..scroll to new position
507 }
508
509 // Now handle the event..
510 // During drag, only interested in left-mouse operations.
511 //
512 if ( Fl::event_button() != FL_LEFT_MOUSE ) break;
513 #if FLTK_ABI_VERSION >= 10303
514 Fl_Tree_Item *item = _root->find_clicked(_prefs, 1); // item we're on, vertically
515 #else
516 Fl_Tree_Item *item = _root->find_clicked(_prefs); // item we're on, vertically
517 #endif
518 if ( !item ) break; // not near item? ignore drag event
519 ret |= 1; // acknowledge event
520 if (_prefs.selectmode() != FL_TREE_SELECT_SINGLE_DRAGGABLE)
521 set_item_focus(item); // becomes new focus item
522 if (item==_lastselect) break; // same item as before? avoid reselect
523
524 // Handle selection behavior
525 switch ( _prefs.selectmode() ) {
526 case FL_TREE_SELECT_NONE:
527 break; // no selection changes
528 case FL_TREE_SELECT_SINGLE: {
529 select_only(item, when()); // select only this item (handles redraw)
530 break;
531 }
532 case FL_TREE_SELECT_SINGLE_DRAGGABLE: {
533 item = _lastselect; // Keep the source intact
534 redraw();
535 break;
536 }
537 case FL_TREE_SELECT_MULTI: {
538 Fl_Tree_Item *from = next_visible_item(_lastselect, dir); // avoid reselecting item
539 Fl_Tree_Item *to = item;
540 int val = is_ctrl ? 2 : 1; // toggle_select() or just select()?
541 bool visible = true;
542 extend_selection_dir(from, to, dir, val, visible);
543 break;
544 }
545 }
546 _lastselect = item; // save current item for later
547 break;
548 }
549 case FL_RELEASE:
550 if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE &&
551 Fl::event_button() == FL_LEFT_MOUSE) {
552 #if FLTK_ABI_VERSION >= 10303
553 Fl_Tree_Item *item = _root->find_clicked(_prefs, 1); // item we're on, vertically
554 #else
555 Fl_Tree_Item *item = _root->find_clicked(_prefs); // item we're on, vertically
556 #endif
557
558 if (item && _lastselect && item != _lastselect &&
559 Fl::event_x() >= item->label_x()) {
560 //printf("Would drag '%s' to '%s'\n", _lastselect->label(), item->label());
561 // Are we dropping above or below the target item?
562 const int h = Fl::event_y() - item->y();
563 const int mid = item->h() / 2;
564 const bool before = h < mid;
565 //printf("Dropping %s it\n", before ? "before" : "after");
566
567 // Do nothing if it would be a no-op
568 if ((before && prev(item) != _lastselect) ||
569 (!before && next(item) != _lastselect)) {
570 Fl_Tree_Item *parent = item->parent();
571
572 if (parent) {
573 int pos = parent->find_child(item);
574 if (!before)
575 pos++;
576
577 // Special case: trying to drop right before a folder
578 if (item->children() && item->is_open() && !before) {
579 parent = item;
580 pos = 0;
581 }
582
583 // If we're moving inside the same parent, use the below/above methods
584 if (_lastselect->parent() == parent) {
585 if (before) {
586 _lastselect->move_above(item);
587 } else {
588 _lastselect->move_below(item);
589 }
590 } else {
591 _lastselect->move_into(parent, pos);
592 }
593
594 redraw();
595 do_callback_for_item(_lastselect, FL_TREE_REASON_DRAGGED);
596 }
597 }
598 }
599 redraw();
600 } // End single-drag check
601 ret |= 1;
602 break;
603 }
604 return(ret);
605 }
606
607 #if FLTK_ABI_VERSION >= 10303
608 // nothing
609 #else
610 // Redraw timeout callback
611 // (Only need this hack for old ABI 10302 and older)
612 //
613 static void redraw_soon(void *data) {
614 ((Fl_Tree*)data)->redraw();
615 Fl::remove_timeout(redraw_soon, data);
616 }
617 #endif
618
619 #if FLTK_ABI_VERSION >= 10303
620 /// Recalculate widget dimensions and scrollbar visibility,
621 /// normally managed automatically.
622 ///
623 /// Low overhead way to update the tree widget's outer/inner dimensions
624 /// and re-determine scrollbar visibility based on these changes without
625 /// recalculating the entire size of the tree data.
626 ///
627 /// Assumes that either the tree's size in _tree_w/_tree_h are correct
628 /// so that scrollbar visibility can be calculated easily, or are both
629 /// zero indicating scrollbar visibility can't be calculated yet.
630 ///
631 /// This method is called when the widget is resize()ed or if the
632 /// scrollbar's sizes are changed (affects tree widget's inner dimensions
633 /// tix/y/w/h), and also used by calc_tree().
634 /// \version 1.3.3 ABI feature
635 ///
636 void Fl_Tree::calc_dimensions() {
637 // Calc tree outer xywh
638 // Area of the tree widget /outside/ scrollbars
639 //
640 _tox = x() + Fl::box_dx(box());
641 _toy = y() + Fl::box_dy(box());
642 _tow = w() - Fl::box_dw(box());
643 _toh = h() - Fl::box_dh(box());
644
645 // Scrollbar visiblity + positions
646 // Calc this ONLY if tree_h and tree_w have been calculated.
647 // Zero values for these indicate calc in progress, but not done yet.
648 //
649 if ( _tree_h >= 0 && _tree_w >= 0 ) {
650 int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
651 int vshow = _tree_h > _toh ? 1 : 0;
652 int hshow = _tree_w > _tow ? 1 : 0;
653 // See if one scroller's appearance affects the other's visibility
654 if ( hshow && !vshow && (_tree_h > (_toh-scrollsize)) ) vshow = 1;
655 if ( vshow && !hshow && (_tree_w > (_tow-scrollsize)) ) hshow = 1;
656 // vertical scrollbar visibility
657 if ( vshow ) {
658 _vscroll->show();
659 _vscroll->resize(_tox+_tow-scrollsize, _toy,
660 scrollsize, h()-Fl::box_dh(box()) - (hshow ? scrollsize : 0));
661 } else {
662 _vscroll->hide();
663 _vscroll->value(0);
664 }
665 // horizontal scrollbar visibility
666 if ( hshow ) {
667 _hscroll->show();
668 _hscroll->resize(_tox, _toy+_toh-scrollsize,
669 _tow - (vshow ? scrollsize : 0), scrollsize);
670 } else {
671 _hscroll->hide();
672 _hscroll->value(0);
673 }
674
675 // Calculate inner dimensions
676 // The area the tree occupies inside the scrollbars and margins
677 //
678 _tix = _tox;
679 _tiy = _toy;
680 _tiw = _tow - (_vscroll->visible() ? _vscroll->w() : 0);
681 _tih = _toh - (_hscroll->visible() ? _hscroll->h() : 0);
682
683 // Scrollbar tab sizes
684 _vscroll->slider_size(float(_tih) / float(_tree_h));
685 _vscroll->range(0.0, _tree_h - _tih);
686
687 _hscroll->slider_size(float(_tiw) / float(_tree_w));
688 _hscroll->range(0.0, _tree_w - _tiw);
689 } else {
690 // Best we can do without knowing tree_h/tree_w
691 _tix = _tox;
692 _tiy = _toy;
693 _tiw = _tow;
694 _tih = _toh;
695 }
696 }
697
698 /// Recalculates the tree's sizes and scrollbar visibility,
699 /// normally managed automatically.
700 ///
701 /// On return:
702 ///
703 /// - _tree_w will be the overall pixel width of the entire viewable tree
704 /// - _tree_h will be the overall pixel height ""
705 /// - scrollbar visibility and pan sizes are updated
706 /// - internal _tix/_tiy/_tiw/_tih dimensions are updated
707 ///
708 /// _tree_w/_tree_h include the tree's margins (e.g. marginleft()),
709 /// whether items are open or closed, label contents and font sizes, etc.
710 ///
711 /// The tree hierarchy's size is managed separately from the widget's
712 /// size as an optimization; this way resize() on the widget doesn't
713 /// involve recalculating the tree's hierarchy needlessly, as widget
714 /// size has no bearing on the tree hierarchy.
715 ///
716 /// The tree hierarchy's size only changes when items are added/removed,
717 /// open/closed, label contents or font sizes changed, margins changed, etc.
718 ///
719 /// This calculation involves walking the *entire* tree from top to bottom,
720 /// potentially a slow calculation if the tree has many items (potentially
721 /// hundreds of thousands), and should therefore be called sparingly.
722 ///
723 /// For this reason, recalc_tree() is used as a way to /schedule/
724 /// calculation when changes affect the tree hierarchy's size.
725 ///
726 /// Apps may want to call this method directly if the app makes changes
727 /// to the tree's geometry, then immediately needs to work with the tree's
728 /// new dimensions before an actual redraw (and recalc) occurs. (This
729 /// use by an app should only rarely be needed)
730 ///
731 void Fl_Tree::calc_tree() {
732 // Set tree width and height to zero, and recalc just _tox/_toy/_tow/_toh for now.
733 _tree_w = _tree_h = -1;
734 calc_dimensions();
735 if ( !_root ) return;
736 // Walk the tree to determine its width and height.
737 // We need this to compute scrollbars..
738 // By the end, 'Y' will be the lowest point on the tree
739 //
740 int X = _tix + _prefs.marginleft() + _hscroll->value();
741 int Y = _tiy + _prefs.margintop() - _vscroll->value();
742 int W = _tiw;
743 // Adjust root's X/W if connectors off
744 if (_prefs.connectorstyle() == FL_TREE_CONNECTOR_NONE) {
745 X -= _prefs.openicon()->w();
746 W += _prefs.openicon()->w();
747 }
748 int xmax = 0, render = 0, ytop = Y;
749 fl_font(_prefs.labelfont(), _prefs.labelsize());
750 _root->draw(X, Y, W, 0, xmax, 1, render); // descend into tree without drawing (render=0)
751 // Save computed tree width and height
752 _tree_w = _prefs.marginleft() + xmax - X; // include margin in tree's width
753 _tree_h = _prefs.margintop() + Y - ytop; // include margin in tree's height
754 // Calc tree dims again; now that tree_w/tree_h are known, scrollbars are calculated.
755 calc_dimensions();
756 }
757 #endif
758
759 void Fl_Tree::resize(int X,int Y,int W, int H) {
760 fix_scrollbar_order();
761 Fl_Group::resize(X,Y,W,H);
762 #if FLTK_ABI_VERSION >= 10303
763 calc_dimensions();
764 #endif
765 init_sizes();
766 }
767
768 #if FLTK_ABI_VERSION >= 10303
769 /// Standard FLTK draw() method, handles drawing the tree widget.
770 void Fl_Tree::draw() {
771 fix_scrollbar_order();
772 // Has tree recalc been scheduled? If so, do it
773 if ( _tree_w == -1 ) calc_tree();
774 else calc_dimensions();
775 // Let group draw box+label but *NOT* children.
776 // We handle drawing children ourselves by calling each item's draw()
777 {
778 // Draw group's bg + label
779 if ( damage() & ~FL_DAMAGE_CHILD) { // redraw entire widget?
780 Fl_Group::draw_box();
781 Fl_Group::draw_label();
782 }
783 if ( ! _root ) return;
784 // These values are changed during drawing
785 // By end, 'Y' will be the lowest point on the tree
786 int X = _tix + _prefs.marginleft() - _hscroll->value();
787 int Y = _tiy + _prefs.margintop() - _vscroll->value();
788 int W = _tiw - X + _tix;
789 // Adjust root's X/W if connectors off
790 if (_prefs.connectorstyle() == FL_TREE_CONNECTOR_NONE) {
791 X -= _prefs.openicon()->w();
792 W += _prefs.openicon()->w();
793 }
794 // Draw entire tree, starting with root
795 fl_push_clip(_tix,_tiy,_tiw,_tih);
796 {
797 int xmax = 0;
798 fl_font(_prefs.labelfont(), _prefs.labelsize());
799 _root->draw(X, Y, W, // descend into tree here to draw it
800 (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus
801 xmax, 1, 1);
802 }
803 fl_pop_clip();
804 }
805 // Draw scrollbars last
806 draw_child(*_vscroll);
807 draw_child(*_hscroll);
808 // That little tile between the scrollbars
809 if ( _vscroll->visible() && _hscroll->visible() ) {
810 fl_color(_vscroll->color());
811 fl_rectf(_hscroll->x()+_hscroll->w(),
812 _vscroll->y()+_vscroll->h(),
813 _vscroll->w(),
814 _hscroll->h());
815 }
816
817 // Draw dragging line
818 if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE &&
819 Fl::pushed() == this) {
820
821 Fl_Tree_Item *item = _root->find_clicked(_prefs, 1); // item we're on, vertically
822 if (item && item != _item_focus) {
823 // Are we dropping above or before the target item?
824 const int h = Fl::event_y() - item->y();
825 const int mid = item->h() / 2;
826 const bool before = h < mid;
827
828 fl_color(FL_BLACK);
829
830 int tgt = item->y() + (before ? 0 : item->h());
831 fl_line(item->x(), tgt, item->x() + item->w(), tgt);
832 }
833 }
834 }
835 #else
836 /// Standard FLTK draw() method, handles drawing the tree widget.
837 void Fl_Tree::draw() {
838 int ytoofar = draw_tree();
839
840 // See if we're scrolled below bottom of tree
841 // This can happen if someone just closed a large item.
842 // If so, change scroller as needed.
843 //
844 if ( _vscroll->visible() && ytoofar > 0 ) {
845 int scrollval = _vscroll->value();
846 int ch = h() - Fl::box_dh(box());
847 int range2 = scrollval - ytoofar;
848 int size2 = ch + range2;
849 if ( range2 < 0 ) {
850 _vscroll->value(0);
851 _vscroll->hide();
852 } else {
853 _vscroll->slider_size(float(ch)/float(size2));
854 _vscroll->range(0.0,range2);
855 _vscroll->value(range2);
856 }
857 Fl::add_timeout(.10, redraw_soon, (void*)this); // use timer to trigger redraw; we can't
858 }
859
860 // Draw dragging line
861 if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE &&
862 Fl::pushed() == this) {
863
864 Fl_Tree_Item *item = _root->find_clicked(_prefs); // item we're on, vertically
865 if (item && item != _item_focus) {
866 // Are we dropping above or before the target item?
867 const int h = Fl::event_y() - item->y();
868 const int mid = item->h() / 2;
869 const bool before = h < mid;
870
871 fl_color(FL_BLACK);
872
873 int tgt = item->y() + (before ? 0 : item->h());
874 fl_line(item->x(), tgt, item->x() + item->w(), tgt);
875 }
876 }
877 }
878
879 // This method is undocumented, and has been removed in ABI 1.3.3
880 int Fl_Tree::draw_tree() {
881 int ret = 0;
882 fix_scrollbar_order();
883 // Let group draw box+label but *NOT* children.
884 // We handle drawing children ourselves by calling each item's draw()
885 //
886 int cx = x() + Fl::box_dx(box());
887 int cy = y() + Fl::box_dy(box());
888 int cw = w() - Fl::box_dw(box());
889 int ch = h() - Fl::box_dh(box());
890 {
891 // Handle group's bg
892 if ( damage() & ~FL_DAMAGE_CHILD) { // redraw entire widget?
893 Fl_Group::draw_box();
894 Fl_Group::draw_label();
895 }
896 if ( ! _root ) return(0);
897 // These values are changed during drawing
898 // By end, 'Y' will be the lowest point on the tree
899 int X = cx + _prefs.marginleft();
900 int Y = cy + _prefs.margintop() - (_vscroll->visible() ? _vscroll->value() : 0);
901 int W = cw - _prefs.marginleft(); // - _prefs.marginright();
902 // Adjust root's X/W if connectors off
903 if (_prefs.connectorstyle() == FL_TREE_CONNECTOR_NONE) {
904 X -= _prefs.openicon()->w();
905 W += _prefs.openicon()->w();
906 }
907 int Ysave = Y;
908 fl_push_clip(cx,cy,cw,ch);
909 {
910 fl_font(_prefs.labelfont(), _prefs.labelsize());
911 _root->draw(X, Y, W, this,
912 (Fl::focus()==this)?_item_focus:0, // show focus item ONLY if Fl_Tree has focus
913 _prefs);
914 }
915 fl_pop_clip();
916
917 // Show vertical scrollbar?
918 {
919 #if FLTK_ABI_VERSION >= 10301
920 // NEW
921 int SY = Y + _prefs.marginbottom();
922 #else /*FLTK_ABI_VERSION*/
923 // OLD
924 int SY = Y;
925 #endif /*FLTK_ABI_VERSION*/
926 int ydiff = (SY+_prefs.margintop())-Ysave; // ydiff=size of tree
927 int ytoofar = (cy+ch) - SY; // ytoofar -- if >0, scrolled beyond bottom
928 if ( ytoofar > 0 ) ydiff += ytoofar;
929 if ( Ysave<cy || ydiff>ch || int(_vscroll->value())>1 ) {
930 _vscroll->visible();
931 int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
932 int sx = x()+w()-Fl::box_dx(box())-scrollsize;
933 int sy = y()+Fl::box_dy(box());
934 int sw = scrollsize;
935 int sh = h()-Fl::box_dh(box());
936 _vscroll->show();
937 _vscroll->resize(sx,sy,sw,sh);
938 _vscroll->slider_size(float(ch)/float(ydiff));
939 _vscroll->range(0.0,ydiff-ch);
940 ret = ytoofar;
941 } else {
942 _vscroll->Fl_Slider::value(0);
943 _vscroll->hide();
944 ret = 0;
945 }
946 }
947 }
948 draw_child(*_vscroll); // draw scroll last
949 return(ret);
950 }
951 #endif
952
953 /// Print the tree as 'ascii art' to stdout.
954 /// Used mainly for debugging.
955 /// \todo should be const
956 /// \version 1.3.0
957 ///
958 void Fl_Tree::show_self() {
959 if ( ! _root ) return;
960 _root->show_self();
961 }
962
963 /// Set the label for the root item to \p 'new_label'.
964 ///
965 /// Makes an internally managed copy of 'new_label'.
966 ///
967 void Fl_Tree::root_label(const char *new_label) {
968 if ( ! _root ) return;
969 _root->label(new_label);
970 }
971
972 /// Returns the root item.
973 Fl_Tree_Item* Fl_Tree::root() {
974 return(_root);
975 }
976
977 /// Sets the root item to \p 'newitem'.
978 ///
979 /// If a root item already exists, clear() is called first to clear it
980 /// before replacing it with newitem.
981 ///
982 #if FLTK_ABI_VERSION >= 10303
983 /// Use this to install a custom item (derived from Fl_Tree_Item) as the root
984 /// of the tree. This allows the derived class to implement custom drawing
985 /// by overriding Fl_Tree_Item::draw_item_content().
986 ///
987 #endif
988 /// \version 1.3.3
989 ///
990 void Fl_Tree::root(Fl_Tree_Item *newitem) {
991 if ( _root ) clear();
992 _root = newitem;
993 }
994
995 /// Adds a new item, given a menu style \p 'path'.
996 /// Any parent nodes that don't already exist are created automatically.
997 /// Adds the item based on the value of sortorder().
998 /// If \p 'item' is NULL, a new item is created.
999 ///
1000 /// To specify items or submenus that contain slashes ('/' or '\')
1001 /// use an escape character to protect them, e.g.
1002 /// \code
1003 /// tree->add("/Holidays/Photos/12\\/25\\/2010"); // Adds item "12/25/2010"
1004 /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp"
1005 /// \endcode
1006 /// \param[in] path The path to the item, e.g. "Flintstone/Fred".
1007 /// \param[in] item The new item to be added.
1008 /// If NULL, a new item is created with
1009 /// a name that is the last element in \p 'path'.
1010 /// \returns The new item added, or 0 on error.
1011 /// \version 1.3.3
1012 ///
1013 Fl_Tree_Item* Fl_Tree::add(const char *path, Fl_Tree_Item *item) {
1014 // Tree has no root? make one
1015 if ( ! _root ) {
1016 #if FLTK_ABI_VERSION >= 10303
1017 _root = new Fl_Tree_Item(this);
1018 #else
1019 _root = new Fl_Tree_Item(_prefs);
1020 #endif
1021 _root->parent(0);
1022 _root->label("ROOT");
1023 }
1024 // Find parent item via path
1025 char **arr = parse_path(path);
1026 item = _root->add(_prefs, arr, item);
1027 free_path(arr);
1028 return(item);
1029 }
1030
1031 #if FLTK_ABI_VERSION >= 10303
1032 // do nothing here: add(path,item) where item defaults to 0 takes its place
1033 #else
1034 /// Adds a new item given a menu style \p 'path'.
1035 /// Same as calling add(path, NULL);
1036 /// \param[in] path The path to the item to be created, e.g. "Flintstone/Fred".
1037 /// \returns The new item added, or 0 on error.
1038 /// \see add(const char*,Fl_Tree_Item*)
1039 /// \version 1.3.0 release
1040 ///
1041 Fl_Tree_Item* Fl_Tree::add(const char *path) {
1042 return add(path, 0);
1043 }
1044 #endif
1045
1046 /// Add a new child item labeled \p 'name' to the specified \p 'parent_item'.
1047 ///
1048 /// \param[in] parent_item The parent item the new child item will be added to.
1049 /// Must not be NULL.
1050 /// \param[in] name The label for the new item
1051 /// \returns The new item added.
1052 /// \version 1.3.0 release
1053 ///
1054 Fl_Tree_Item* Fl_Tree::add(Fl_Tree_Item *parent_item, const char *name) {
1055 return(parent_item->add(_prefs, name));
1056 }
1057
1058 /// Inserts a new item \p 'name' above the specified Fl_Tree_Item \p 'above'.
1059 /// Example:
1060 /// \code
1061 /// tree->add("Aaa/000"); // "000" is index 0 in Aaa's children
1062 /// tree->add("Aaa/111"); // "111" is index 1 in Aaa's children
1063 /// tree->add("Aaa/222"); // "222" is index 2 in Aaa's children
1064 /// ..
1065 /// // How to use insert_above() to insert a new item above Aaa/222
1066 /// Fl_Tree_Item *item = tree->find_item("Aaa/222"); // get item Aaa/222
1067 /// if (item) tree->insert_above(item, "New item"); // insert new item above it
1068 /// \endcode
1069 ///
1070 /// \param[in] above -- the item above which to insert the new item. Must not be NULL.
1071 /// \param[in] name -- the name of the new item
1072 /// \returns The new item added, or 0 if 'above' could not be found.
1073 /// \see insert()
1074 ///
1075 Fl_Tree_Item* Fl_Tree::insert_above(Fl_Tree_Item *above, const char *name) {
1076 return(above->insert_above(_prefs, name));
1077 }
1078
1079 /// Insert a new item \p 'name' into \p 'item's children at position \p 'pos'.
1080 ///
1081 /// If \p pos is out of range the new item is
1082 /// - prepended if \p pos \< 0 or
1083 /// - appended if \p pos \> item->children().
1084 ///
1085 /// Note: \p pos == children() is not considered out of range: the item is
1086 /// appended to the child list.
1087 /// Example:
1088 /// \code
1089 /// tree->add("Aaa/000"); // "000" is index 0 in Aaa's children
1090 /// tree->add("Aaa/111"); // "111" is index 1 in Aaa's children
1091 /// tree->add("Aaa/222"); // "222" is index 2 in Aaa's children
1092 /// ..
1093 /// // How to use insert() to insert a new item between Aaa/111 + Aaa/222
1094 /// Fl_Tree_Item *item = tree->find_item("Aaa"); // get parent item Aaa
1095 /// if (item) tree->insert(item, "New item", 2); // insert as a child of Aaa at index #2
1096 /// \endcode
1097 ///
1098 /// \param[in] item The existing item to insert new child into. Must not be NULL.
1099 /// \param[in] name The label for the new item
1100 /// \param[in] pos The position of the new item in the child list
1101 /// \returns The new item added.
1102 /// \see insert_above()
1103 ///
1104 Fl_Tree_Item* Fl_Tree::insert(Fl_Tree_Item *item, const char *name, int pos) {
1105 return(item->insert(_prefs, name, pos));
1106 }
1107
1108 /// Remove the specified \p 'item' from the tree.
1109 /// \p item may not be NULL.
1110 /// If it has children, all those are removed too.
1111 /// If item being removed has focus, no item will have focus.
1112 /// \returns 0 if done, -1 if 'item' not found.
1113 ///
1114 int Fl_Tree::remove(Fl_Tree_Item *item) {
1115 // Item being removed is focus item? zero focus
1116 if ( item == _item_focus ) _item_focus = 0;
1117 #if FLTK_ABI_VERSION >= 10301
1118 if ( item == _lastselect ) _lastselect = 0;
1119 #endif /*FLTK_ABI_VERSION*/
1120 if ( item == _root ) {
1121 clear();
1122 } else {
1123 Fl_Tree_Item *parent = item->parent(); // find item's parent
1124 if ( ! parent ) return(-1);
1125 parent->remove_child(item); // remove child + children
1126 }
1127 return(0);
1128 }
1129
1130 /// Clear the entire tree's children, including the root.
1131 /// The tree will be left completely empty.
1132 ///
1133 void Fl_Tree::clear() {
1134 if ( ! _root ) return;
1135 _root->clear_children();
1136 delete _root; _root = 0;
1137 _item_focus = 0;
1138 #if FLTK_ABI_VERSION >= 10301
1139 _lastselect = 0;
1140 #endif /*FLTK_ABI_VERSION*/
1141 }
1142
1143 /// Clear all the children for \p 'item'.
1144 /// Item may not be NULL.
1145 ///
1146 void Fl_Tree::clear_children(Fl_Tree_Item *item) {
1147 if ( item->has_children() ) {
1148 item->clear_children();
1149 redraw(); // redraw only if there were children to clear
1150 }
1151 }
1152
1153 /// Find the item, given a menu style path, e.g. "/Parent/Child/item".
1154 /// There is both a const and non-const version of this method.
1155 /// Const version allows pure const methods to use this method
1156 /// to do lookups without causing compiler errors.
1157 ///
1158 /// To specify items or submenus that contain slashes ('/' or '\')
1159 /// use an escape character to protect them, e.g.
1160 ///
1161 /// \code
1162 /// tree->add("/Holidays/Photos/12\\/25\\/2010"); // Adds item "12/25/2010"
1163 /// tree->add("/Pathnames/c:\\\\Program Files\\\\MyApp"); // Adds item "c:\Program Files\MyApp"
1164 /// \endcode
1165 ///
1166 /// \param[in] path -- the tree item's pathname to be found (e.g. "Flintstones/Fred")
1167 /// \returns The item, or NULL if not found.
1168 /// \see item_pathname()
1169 ///
1170 const Fl_Tree_Item *Fl_Tree::find_item(const char *path) const {
1171 if ( ! _root ) return(NULL);
1172 char **arr = parse_path(path);
1173 const Fl_Tree_Item *item = _root->find_item(arr);
1174 free_path(arr);
1175 return(item);
1176 }
1177
1178 /// Non-const version of Fl_Tree::find_item(const char *path) const
1179 Fl_Tree_Item *Fl_Tree::find_item(const char *path) {
1180 // "Effective C++, 3rd Ed", p.23. Sola fide, Amen.
1181 return(const_cast<Fl_Tree_Item*>(
1182 static_cast<const Fl_Tree&>(*this).find_item(path)));
1183 }
1184
1185 // Handle safe 'reverse string concatenation'.
1186 // In the following we build the pathname from right-to-left,
1187 // since we start at the child and work our way up to the root.
1188 //
1189 #define SAFE_RCAT(c) { \
1190 slen += 1; if ( slen >= pathnamelen ) { pathname[0] = '\0'; return(-2); } \
1191 *s-- = c; \
1192 }
1193
1194 /// Return \p 'pathname' of size \p 'pathnamelen' for the specified \p 'item'.
1195 ///
1196 /// If \p 'item' is NULL, root() is used.<br>
1197 /// The tree's root will be included in the pathname if showroot() is on.<br>
1198 /// Menu items or submenus that contain slashes ('/' or '\') in their names
1199 /// will be escaped with a backslash. This is symmetrical with the add()
1200 /// function which uses the same escape pattern to set names.
1201 ///
1202 /// \param[out] pathname The string to use to return the pathname
1203 /// \param[in] pathnamelen The maximum length of the string (including NULL). Must not be zero.
1204 /// \param[in] item The item whose pathname is to be returned.
1205 /// \returns
1206 /// - 0 : OK (\p pathname returns the item's pathname)
1207 /// - -1 : item not found (pathname="")
1208 /// - -2 : pathname not large enough (pathname="")
1209 /// \see find_item()
1210 ///
1211 int Fl_Tree::item_pathname(char *pathname, int pathnamelen, const Fl_Tree_Item *item) const {
1212 pathname[0] = '\0';
1213 item = item ? item : _root;
1214 if ( !item ) return(-1);
1215 // Build pathname starting at end
1216 char *s = (pathname+pathnamelen-1);
1217 int slen = 0; // length of string compiled so far (including NULL)
1218 SAFE_RCAT('\0');
1219 while ( item ) {
1220 if ( item->is_root() && showroot() == 0 ) break; // don't include root in path if showroot() off
1221 // Find name of current item
1222 const char *name = item->label() ? item->label() : "???"; // name for this item
1223 int len = (int) strlen(name);
1224 // Add name to end of pathname[]
1225 for ( --len; len>=0; len-- ) {
1226 SAFE_RCAT(name[len]); // rcat name of item
1227 if ( name[len] == '/' || name[len] == '\\' ) {
1228 SAFE_RCAT('\\'); // escape front or back slashes within name
1229 }
1230 }
1231 SAFE_RCAT('/'); // rcat leading slash
1232 item = item->parent(); // move up tree (NULL==root)
1233 }
1234 if ( *(++s) == '/' ) { ++s; --slen; } // leave off leading slash from pathname
1235 if ( s != pathname ) memmove(pathname, s, slen); // Shift down right-aligned string
1236 return(0);
1237 }
1238
1239 #if FLTK_ABI_VERSION >= 10303
1240 /// Find the item that was last clicked on.
1241 /// You should use callback_item() instead, which is fast,
1242 /// and is meant to be used within a callback to determine the item clicked.
1243 ///
1244 /// This method walks the entire tree looking for the first item that is
1245 /// under the mouse. (The value of the \p 'yonly' flag affects whether
1246 /// both x and y events are checked, or just y)
1247 ///
1248 /// Use this method /only/ if you've subclassed Fl_Tree, and are receiving
1249 /// events before Fl_Tree has been able to process and update callback_item().
1250 ///
1251 /// \param[in] yonly -- 0: check both event's X and Y values.
1252 /// -- 1: only check event's Y value, don't care about X.
1253 /// \returns The item clicked, or NULL if no item was under the current event.
1254 /// \version 1.3.0
1255 /// \version 1.3.3 ABI feature: added yonly parameter
1256 ///
1257 const Fl_Tree_Item* Fl_Tree::find_clicked(int yonly) const {
1258 if ( ! _root ) return(NULL);
1259 return(_root->find_clicked(_prefs, yonly));
1260 }
1261
1262 /// Non-const version of Fl_Tree::find_clicked(int yonly) const.
1263 Fl_Tree_Item *Fl_Tree::find_clicked(int yonly) {
1264 // "Effective C++, 3rd Ed", p.23. Sola fide, Amen.
1265 return(const_cast<Fl_Tree_Item*>(
1266 static_cast<const Fl_Tree&>(*this).find_clicked(yonly)));
1267 }
1268 #else
1269 /// Find the item that was last clicked on.
1270 /// You should use callback_item() instead, which is fast,
1271 /// and is meant to be used within a callback to determine the item clicked.
1272 ///
1273 /// This method walks the entire tree looking for the first item that is
1274 /// under the mouse, i.e. at Fl::event_x() / Fl::event_y().
1275 ///
1276 /// Use this method /only/ if you've subclassed Fl_Tree, and are receiving
1277 /// events before Fl_Tree has been able to process and update callback_item().
1278 ///
1279 /// \returns The item clicked, or NULL if no item was under the current event.
1280 /// \version 1.3.0
1281 ///
1282 const Fl_Tree_Item* Fl_Tree::find_clicked() const {
1283 if ( ! _root ) return(NULL);
1284 return(_root->find_clicked(_prefs));
1285 }
1286
1287 /// Non-const version of Fl_Tree::find_clicked() const.
1288 /// \version 1.3.0
1289 Fl_Tree_Item *Fl_Tree::find_clicked() {
1290 // "Effective C++, 3rd Ed", p.23. Sola fide, Amen.
1291 return(const_cast<Fl_Tree_Item*>(
1292 static_cast<const Fl_Tree&>(*this).find_clicked()));
1293 }
1294 #endif
1295
1296 /// Set the item that was last clicked.
1297 /// Should only be used by subclasses needing to change this value.
1298 /// Normally Fl_Tree manages this value.
1299 ///
1300 /// \deprecated in 1.3.3 ABI -- use callback_item() instead.
1301 ///
1302 void Fl_Tree::item_clicked(Fl_Tree_Item* item) {
1303 _callback_item = item;
1304 }
1305
1306 /// Return the item that was last clicked.
1307 ///
1308 /// Valid only from within the callback().
1309 ///
1310 /// \returns The item clicked, or 0 if none.
1311 /// 0 may also be used to indicate several items were clicked/changed.
1312 /// \deprecated in 1.3.3 ABI -- use callback_item() instead.
1313 ///
1314 Fl_Tree_Item* Fl_Tree::item_clicked() {
1315 return(_callback_item);
1316 }
1317
1318 /// Returns next open(), visible item above (\p dir==FL_Up)
1319 /// or below (\p dir==FL_Down) the specified \p 'item', or 0 if no more items.
1320 ///
1321 /// If \p 'item' is 0, returns last() if \p 'dir' is FL_Up,
1322 /// or first() if \p dir is FL_Down.
1323 ///
1324 /// \code
1325 /// // Walk down the tree (forwards)
1326 /// for ( Fl_Tree_Item *i=tree->first_visible_item(); i; i=tree->next_visible_item(i, FL_Down) )
1327 /// printf("Item: %s\n", i->label());
1328 ///
1329 /// // Walk up the tree (backwards)
1330 /// for ( Fl_Tree_Item *i=tree->last_visible_item(); i; i=tree->next_visible_item(i, FL_Up) )
1331 /// printf("Item: %s\n", i->label());
1332 /// \endcode
1333 /// \param[in] item The item above/below which we'll find the next visible item
1334 /// \param[in] dir The direction to search. Can be FL_Up or FL_Down.
1335 /// \returns The item found, or 0 if there's no visible items above/below the specified \p item.
1336 /// \version 1.3.3
1337 ///
1338 Fl_Tree_Item *Fl_Tree::next_visible_item(Fl_Tree_Item *item, int dir) {
1339 return next_item(item, dir, true);
1340 }
1341
1342 /// Returns the first item in the tree, or 0 if none.
1343 ///
1344 /// Use this to walk the tree in the forward direction, e.g.
1345 /// \code
1346 /// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->next(item) )
1347 /// printf("Item: %s\n", item->label());
1348 /// \endcode
1349 ///
1350 /// \returns First item in tree, or 0 if none (tree empty).
1351 /// \see first(), next(), last(), prev()
1352 ///
1353 Fl_Tree_Item* Fl_Tree::first() {
1354 return(_root); // first item always root
1355 }
1356
1357 /// Returns the first open(), visible item in the tree, or 0 if none.
1358 /// \deprecated in 1.3.3 ABI -- use first_visible_item() instead.
1359 ///
1360 Fl_Tree_Item* Fl_Tree::first_visible() {
1361 return(first_visible_item());
1362 }
1363
1364 /// Returns the first open(), visible item in the tree, or 0 if none.
1365 /// \returns First visible item in tree, or 0 if none.
1366 /// \see first_visible_item(), last_visible_item(), next_visible_item()
1367 /// \version 1.3.3
1368 ///
1369 Fl_Tree_Item* Fl_Tree::first_visible_item() {
1370 Fl_Tree_Item *i = showroot() ? first() : next(first());
1371 while ( i ) {
1372 if ( i->visible() ) return(i);
1373 i = next(i);
1374 }
1375 return(0);
1376 }
1377
1378 /// Return the next item after \p 'item', or 0 if no more items.
1379 ///
1380 /// Use this code to walk the entire tree:
1381 /// \code
1382 /// for ( Fl_Tree_Item *i = tree->first(); i; i = tree->next(i) )
1383 /// printf("Item: %s\n", i->label());
1384 /// \endcode
1385 ///
1386 /// \param[in] item The item to use to find the next item. If NULL, returns 0.
1387 /// \returns Next item in tree, or 0 if at last item.
1388 ///
1389 /// \see first(), next(), last(), prev()
1390 ///
1391 Fl_Tree_Item *Fl_Tree::next(Fl_Tree_Item *item) {
1392 if ( ! item ) return(0);
1393 return(item->next());
1394 }
1395
1396 /// Return the previous item before \p 'item', or 0 if no more items.
1397 ///
1398 /// This can be used to walk the tree in reverse, e.g.
1399 /// \code
1400 /// for ( Fl_Tree_Item *item = tree->first(); item; item = tree->prev(item) )
1401 /// printf("Item: %s\n", item->label());
1402 /// \endcode
1403 ///
1404 /// \param[in] item The item to use to find the previous item. If NULL, returns 0.
1405 /// \returns Previous item in tree, or 0 if at first item.
1406 ///
1407 /// \see first(), next(), last(), prev()
1408 ///
1409 Fl_Tree_Item *Fl_Tree::prev(Fl_Tree_Item *item) {
1410 if ( ! item ) return(0);
1411 return(item->prev());
1412 }
1413
1414 /// Returns the last item in the tree.
1415 ///
1416 /// This can be used to walk the tree in reverse, e.g.
1417 ///
1418 /// \code
1419 /// for ( Fl_Tree_Item *item = tree->last(); item; item = tree->prev() )
1420 /// printf("Item: %s\n", item->label());
1421 /// \endcode
1422 ///
1423 /// \returns Last item in the tree, or 0 if none (tree empty).
1424 /// \see first(), next(), last(), prev()
1425 ///
1426 Fl_Tree_Item* Fl_Tree::last() {
1427 if ( ! _root ) return(0);
1428 Fl_Tree_Item *item = _root;
1429 while ( item->has_children() ) {
1430 item = item->child(item->children()-1);
1431 }
1432 return(item);
1433 }
1434
1435 /// Returns the last open(), visible item in the tree.
1436 /// \deprecated in 1.3.3 -- use last_visible_item() instead.
1437 ///
1438 Fl_Tree_Item* Fl_Tree::last_visible() {
1439 return(last_visible_item());
1440 }
1441
1442 /// Returns the last open(), visible item in the tree.
1443 /// \returns Last visible item in the tree, or 0 if none.
1444 /// \see first_visible_item(), last_visible_item(), next_visible_item()
1445 /// \version 1.3.3
1446 ///
1447 Fl_Tree_Item* Fl_Tree::last_visible_item() {
1448 Fl_Tree_Item *item = last();
1449 while ( item ) {
1450 if ( item->visible() ) {
1451 if ( item == _root && !showroot() ) {
1452 return(0);
1453 } else {
1454 return(item);
1455 }
1456 }
1457 item = prev(item);
1458 }
1459 return(item);
1460 }
1461
1462 /// Returns the first selected item in the tree.
1463 ///
1464 /// Use this to walk the tree from top to bottom
1465 /// looking for all the selected items, e.g.
1466 ///
1467 /// \code
1468 /// // Walk tree forward, from top to bottom
1469 /// for ( Fl_Tree_Item *i=tree->first_selected_item(); i; i=tree->next_selected_item(i) )
1470 /// printf("Selected item: %s\n", i->label());
1471 /// \endcode
1472 ///
1473 /// \returns The first selected item, or 0 if none.
1474 /// \see first_selected_item(), last_selected_item(), next_selected_item()
1475 ///
1476 Fl_Tree_Item *Fl_Tree::first_selected_item() {
1477 return(next_selected_item(0));
1478 }
1479
1480 #if FLTK_ABI_VERSION >= 10303
1481 // nothing
1482 #else
1483 /// Returns the next selected item after \p 'item'.
1484 /// If \p item is 0, search starts at the first item (root).
1485 ///
1486 /// This is a convenience method; equivalent to next_selected_item(item, FL_Down);
1487 ///
1488 /// Use this to walk the tree forward (downward) looking for all the selected items, e.g.
1489 /// \code
1490 /// for ( Fl_Tree_Item *i = tree->first_selected_item(); i; i = tree->next_selected_item(i) )
1491 /// printf("Selected item: %s\n", i->label());
1492 /// \endcode
1493 ///
1494 /// \param[in] item The item to use to find the next selected item. If NULL, first() is used.
1495 /// \returns The next selected item, or 0 if there are no more selected items.
1496 /// \see first_selected_item(), last_selected_item(), next_selected_item()
1497 ///
1498 Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item) {
1499 return(next_selected_item(item, FL_Down));
1500 }
1501 #endif
1502
1503 /// Returns the last selected item in the tree.
1504 ///
1505 /// Use this to walk the tree in reverse from bottom to top
1506 /// looking for all the selected items, e.g.
1507 ///
1508 /// \code
1509 /// // Walk tree in reverse, from bottom to top
1510 /// for ( Fl_Tree_Item *i=tree->last_selected_item(); i; i=tree->next_selected_item(i, FL_Up) )
1511 /// printf("Selected item: %s\n", i->label());
1512 /// \endcode
1513 ///
1514 /// \returns The last selected item, or 0 if none.
1515 /// \see first_selected_item(), last_selected_item(), next_selected_item()
1516 /// \version 1.3.3
1517 ///
1518 Fl_Tree_Item *Fl_Tree::last_selected_item() {
1519 return(next_selected_item(0, FL_Up));
1520 }
1521
1522 /// Returns next item after \p 'item' in direction \p 'dir'
1523 /// depending on \p 'visible'.
1524 ///
1525 /// Next item will be above (if dir==FL_Up) or below (if dir==FL_Down).
1526 /// If \p 'visible' is true, only items whose parents are open() will be returned.
1527 /// If \p 'visible' is false, even items whose parents are close()ed will be returned.
1528 ///
1529 /// If \p item is 0, the return value will be the result of this truth table:
1530 /// <PRE>
1531 /// visible=true visible=false
1532 /// ------------------- -------------
1533 /// dir=FL_Up: last_visible_item() last()
1534 /// dir=FL_Down: first_visible_item() first()
1535 /// </PRE>
1536 ///
1537 /// \par Example use:
1538 /// \code
1539 /// // Walk down the tree showing open(), visible items
1540 /// for ( Fl_Tree_Item *i=tree->first_visible_item(); i; i=tree->next_item(i, FL_Down, true) )
1541 /// printf("Item: %s\n", i->label());
1542 ///
1543 /// // Walk up the tree showing open(), visible items
1544 /// for ( Fl_Tree_Item *i=tree->last_visible_item(); i; i=tree->next_item(i, FL_Up, true) )
1545 /// printf("Item: %s\n", i->label());
1546 ///
1547 /// // Walk down the tree showing all items (open or closed)
1548 /// for ( Fl_Tree_Item *i=tree->first(); i; i=tree->next_item(i, FL_Down, false) )
1549 /// printf("Item: %s\n", i->label());
1550 ///
1551 /// // Walk up the tree showing all items (open or closed)
1552 /// for ( Fl_Tree_Item *i=tree->last(); i; i=tree->next_item(i, FL_Up, false) )
1553 /// printf("Item: %s\n", i->label());
1554 /// \endcode
1555 ///
1556 /// \param[in] item The item to use to find the next item. If NULL, returns 0.
1557 /// \param[in] dir Can be FL_Up or FL_Down (default=FL_Down or 'next')
1558 /// \param[in] visible true=return only open(), visible items,<br>
1559 /// false=return open or closed items (default)
1560 /// \returns Next item in tree in the direction and visibility specified,
1561 /// or 0 if no more items of specified visibility in that direction.
1562 /// \see first(), last(), next(),<BR>
1563 /// first_visible_item(), last_visible_item(), next_visible_item(),<BR>
1564 /// first_selected_item(), last_selected_item(), next_selected_item()
1565 /// \version 1.3.3
1566 ///
1567 Fl_Tree_Item *Fl_Tree::next_item(Fl_Tree_Item *item, int dir, bool visible) {
1568 if ( ! item ) { // no start item?
1569 if ( visible ) {
1570 item = ( dir == FL_Up ) ? last_visible_item() : // wrap to bottom
1571 first_visible_item(); // wrap to top
1572 } else {
1573 item = ( dir == FL_Up ) ? last() : // wrap to bottom
1574 first(); // wrap to top
1575 }
1576 if ( ! item ) return(0);
1577 if ( item->visible_r() ) return(item); // return first/last visible item
1578 }
1579 switch (dir) {
1580 case FL_Up:
1581 if ( visible ) return(item->prev_visible(_prefs));
1582 else return(item->prev());
1583 case FL_Down:
1584 if ( visible ) return(item->next_visible(_prefs));
1585 else return(item->next());
1586 }
1587 return(0); // unknown dir
1588 }
1589
1590 /// Returns the next selected item above or below \p 'item', depending on \p 'dir'.
1591 /// If \p 'item' is 0, search starts at either first() or last(), depending on \p 'dir':
1592 /// first() if \p 'dir' is FL_Down (default), last() if \p 'dir' is FL_Up.
1593 ///
1594 /// Use this to walk the tree looking for all the selected items, e.g.
1595 /// \code
1596 /// // Walk down the tree (forwards)
1597 /// for ( Fl_Tree_Item *i=tree->first_selected_item(); i; i=tree->next_selected_item(i, FL_Down) )
1598 /// printf("Item: %s\n", i->label());
1599 ///
1600 /// // Walk up the tree (backwards)
1601 /// for ( Fl_Tree_Item *i=tree->last_selected_item(); i; i=tree->next_selected_item(i, FL_Up) )
1602 /// printf("Item: %s\n", i->label());
1603 /// \endcode
1604 ///
1605 /// \param[in] item The item above or below which we'll find the next selected item.
1606 /// If NULL, first() is used if FL_Down, last() if FL_Up.
1607 /// (default=NULL)
1608 /// \param[in] dir The direction to go.
1609 /// FL_Up for moving up the tree,
1610 /// FL_Down for down the tree (default)
1611 /// \returns The next selected item, or 0 if there are no more selected items.
1612 /// \see first_selected_item(), last_selected_item(), next_selected_item()
1613 /// \version 1.3.3
1614 ///
1615 Fl_Tree_Item *Fl_Tree::next_selected_item(Fl_Tree_Item *item, int dir) {
1616 switch (dir) {
1617 case FL_Down:
1618 if ( ! item ) {
1619 if ( ! (item = first()) ) return(0);
1620 if ( item->is_selected() ) return(item);
1621 }
1622 while ( (item = item->next()) )
1623 if ( item->is_selected() )
1624 return(item);
1625 return(0);
1626 case FL_Up:
1627 if ( ! item ) {
1628 if ( ! (item = last()) ) return(0);
1629 if ( item->is_selected() ) return(item);
1630 }
1631 while ( (item = item->prev()) )
1632 if ( item->is_selected() )
1633 return(item);
1634 return(0);
1635 }
1636 return(0);
1637 }
1638
1639 #if FLTK_ABI_VERSION >= 10303 /* reason for this: Fl_Tree_Item_Array::manage_item_destroy() */
1640 /// Returns the currently selected items as an array of \p 'ret_items'.
1641 ///
1642 /// Example:
1643 /// \code
1644 /// // Get selected items as an array
1645 /// Fl_Tree_Item_Array items;
1646 /// tree->get_selected_items(items);
1647 /// // Manipulate the returned array
1648 /// for ( int t=0; t<items.total(); t++ ) {
1649 /// Fl_Tree_Item &item = items[t];
1650 /// ..do stuff with each selected item..
1651 /// }
1652 /// \endcode
1653 ///
1654 /// \param[out] ret_items The returned array of selected items.
1655 /// \returns The number of items in the returned array.
1656 /// \see first_selected_item(), next_selected_item()
1657 /// \version 1.3.3 ABI feature
1658 ///
1659 int Fl_Tree::get_selected_items(Fl_Tree_Item_Array &ret_items) {
1660 ret_items.clear();
1661 for ( Fl_Tree_Item *i=first_selected_item(); i; i=next_selected_item(i) ) {
1662 ret_items.add(i);
1663 }
1664 return ret_items.total();
1665 }
1666 #endif
1667
1668 /// Open the specified \p 'item'.
1669 ///
1670 /// This causes the item's children (if any) to be shown.<br>
1671 /// Invokes the callback depending on the value of optional
1672 /// parameter \p 'docallback'.<br>
1673 /// Handles calling redraw() if anything changed.
1674 ///
1675 /// The callback can use callback_item() and callback_reason() respectively to determine
1676 /// the item changed and the reason the callback was called.
1677 ///
1678 /// \param[in] item -- the item to be opened. Must not be NULL.
1679 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1680 /// - 0 - callback() is not invoked
1681 /// - 1 - callback() is invoked if item changed (default),
1682 /// callback_reason() will be FL_TREE_REASON_OPENED
1683 /// \returns
1684 /// - 1 -- item was opened
1685 /// - 0 -- item was already open, no change
1686 ///
1687 /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason()
1688 ///
1689 int Fl_Tree::open(Fl_Tree_Item *item, int docallback) {
1690 if ( item->is_open() ) return(0);
1691 item->open(); // handles recalc_tree()
1692 redraw();
1693 if ( docallback ) {
1694 do_callback_for_item(item, FL_TREE_REASON_OPENED);
1695 }
1696 return(1);
1697 }
1698
1699 /// Opens the item specified by \p 'path'.
1700 ///
1701 /// This causes the item's children (if any) to be shown.<br>
1702 /// Invokes the callback depending on the value of optional
1703 /// parameter \p 'docallback'.<br>
1704 /// Handles calling redraw() if anything changed.
1705 ///
1706 /// Items or submenus that themselves contain slashes ('/' or '\')
1707 /// should be escaped, e.g. open("Holidays/12\\/25\\/2010").
1708 ///
1709 /// The callback can use callback_item() and callback_reason() respectively to determine
1710 /// the item changed and the reason the callback was called.
1711 ///
1712 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
1713 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1714 /// - 0 - callback() is not invoked
1715 /// - 1 - callback() is invoked if item changed (default),
1716 /// callback_reason() will be FL_TREE_REASON_OPENED
1717 /// \returns
1718 /// - 1 -- OK: item opened
1719 /// - 0 -- OK: item was already open, no change
1720 /// - -1 -- ERROR: item was not found
1721 /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason()
1722 ///
1723 int Fl_Tree::open(const char *path, int docallback) {
1724 Fl_Tree_Item *item = find_item(path);
1725 if ( ! item ) return(-1);
1726 return(open(item, docallback)); // handles recalc_tree()
1727 }
1728
1729 /// Toggle the open state of \p 'item'.
1730 ///
1731 /// Invokes the callback depending on the value of optional
1732 /// parameter \p 'docallback'.<br>
1733 /// Handles calling redraw() if anything changed.
1734 ///
1735 /// The callback can use callback_item() and callback_reason() respectively to determine
1736 /// the item changed and the reason the callback was called.
1737 ///
1738 /// \param[in] item -- the item whose open state is to be toggled. Must not be NULL.
1739 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1740 /// - 0 - callback() is not invoked
1741 /// - 1 - callback() is invoked (default), callback_reason() will be either
1742 /// FL_TREE_REASON_OPENED or FL_TREE_REASON_CLOSED
1743 ///
1744 /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason()
1745 ///
1746 void Fl_Tree::open_toggle(Fl_Tree_Item *item, int docallback) {
1747 if ( item->is_open() ) {
1748 close(item, docallback); // handles recalc_tree()
1749 } else {
1750 open(item, docallback); // handles recalc_tree()
1751 }
1752 }
1753
1754 /// Closes the specified \p 'item'.
1755 ///
1756 /// Invokes the callback depending on the value of optional
1757 /// parameter \p 'docallback'.<br>
1758 /// Handles calling redraw() if anything changed.
1759 ///
1760 /// The callback can use callback_item() and callback_reason() respectively to determine
1761 /// the item changed and the reason the callback was called.
1762 ///
1763 /// \param[in] item -- the item to be closed. Must not be NULL.
1764 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1765 /// - 0 - callback() is not invoked
1766 /// - 1 - callback() is invoked if item changed (default),
1767 /// callback_reason() will be FL_TREE_REASON_CLOSED
1768 /// \returns
1769 /// - 1 -- item was closed
1770 /// - 0 -- item was already closed, no change
1771 /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason()
1772 ///
1773 int Fl_Tree::close(Fl_Tree_Item *item, int docallback) {
1774 if ( item->is_close() ) return(0);
1775 item->close(); // handles recalc_tree()
1776 redraw();
1777 if ( docallback ) {
1778 do_callback_for_item(item, FL_TREE_REASON_CLOSED);
1779 }
1780 return(1);
1781 }
1782
1783 /// Closes the item specified by \p 'path'.
1784 ///
1785 /// Invokes the callback depending on the value of optional
1786 /// parameter \p 'docallback'.<br>
1787 /// Handles calling redraw() if anything changed.
1788 ///
1789 /// Items or submenus that themselves contain slashes ('/' or '\')
1790 /// should be escaped, e.g. close("Holidays/12\\/25\\/2010").
1791 ///
1792 /// The callback can use callback_item() and callback_reason() respectively to determine
1793 /// the item changed and the reason the callback was called.
1794 ///
1795 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
1796 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1797 /// - 0 - callback() is not invoked
1798 /// - 1 - callback() is invoked if item changed (default),
1799 /// callback_reason() will be FL_TREE_REASON_CLOSED
1800 /// \returns
1801 /// - 1 -- OK: item closed
1802 /// - 0 -- OK: item was already closed, no change
1803 /// - -1 -- ERROR: item was not found
1804 /// \see open(), close(), is_open(), is_close(), callback_item(), callback_reason()
1805 ///
1806 int Fl_Tree::close(const char *path, int docallback) {
1807 Fl_Tree_Item *item = find_item(path);
1808 if ( ! item ) return(-1);
1809 return(close(item, docallback)); // handles recalc_tree()
1810 }
1811
1812 /// See if \p 'item' is open.
1813 ///
1814 /// Items that are 'open' are themselves not necessarily visible;
1815 /// one of the item's parents might be closed.
1816 ///
1817 /// \param[in] item -- the item to be tested. Must not be NULL.
1818 /// \returns
1819 /// - 1 : item is open
1820 /// - 0 : item is closed
1821 ///
1822 int Fl_Tree::is_open(Fl_Tree_Item *item) const {
1823 return(item->is_open()?1:0);
1824 }
1825
1826 /// See if item specified by \p 'path' is open.
1827 ///
1828 /// Items or submenus that themselves contain slashes ('/' or '\')
1829 /// should be escaped, e.g. is_open("Holidays/12\\/25\\/2010").
1830 ///
1831 /// Items that are 'open' are themselves not necessarily visible;
1832 /// one of the item's parents might be closed.
1833 ///
1834 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
1835 /// \returns
1836 /// - 1 - OK: item is open
1837 /// - 0 - OK: item is closed
1838 /// - -1 - ERROR: item was not found
1839 /// \see Fl_Tree_Item::visible_r()
1840 ///
1841 int Fl_Tree::is_open(const char *path) const {
1842 const Fl_Tree_Item *item = find_item(path);
1843 if ( ! item ) return(-1);
1844 return(item->is_open()?1:0);
1845 }
1846
1847 /// See if the specified \p 'item' is closed.
1848 ///
1849 /// \param[in] item -- the item to be tested. Must not be NULL.
1850 /// \returns
1851 /// - 1 : item is closed
1852 /// - 0 : item is open
1853 ///
1854 int Fl_Tree::is_close(Fl_Tree_Item *item) const {
1855 return(item->is_close());
1856 }
1857
1858 /// See if item specified by \p 'path' is closed.
1859 ///
1860 /// Items or submenus that themselves contain slashes ('/' or '\')
1861 /// should be escaped, e.g. is_close("Holidays/12\\/25\\/2010").
1862 ///
1863 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
1864 /// \returns
1865 /// - 1 - OK: item is closed
1866 /// - 0 - OK: item is open
1867 /// - -1 - ERROR: item was not found
1868 ///
1869 int Fl_Tree::is_close(const char *path) const {
1870 const Fl_Tree_Item *item = find_item(path);
1871 if ( ! item ) return(-1);
1872 return(item->is_close()?1:0);
1873 }
1874
1875 /// Select the specified \p 'item'. Use 'deselect()' to deselect it.
1876 ///
1877 /// Invokes the callback depending on the value of optional parameter \p docallback.<br>
1878 /// Handles calling redraw() if anything changed.
1879 ///
1880 /// The callback can use callback_item() and callback_reason() respectively to determine
1881 /// the item changed and the reason the callback was called.
1882 ///
1883 /// \param[in] item -- the item to be selected. Must not be NULL.
1884 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1885 /// - 0 - the callback() is not invoked
1886 /// - 1 - the callback() is invoked if item changed state,
1887 /// callback_reason() will be FL_TREE_REASON_SELECTED
1888 /// \returns
1889 /// - 1 - item's state was changed
1890 /// - 0 - item was already selected, no change was made
1891 ///
1892 int Fl_Tree::select(Fl_Tree_Item *item, int docallback) {
1893 int alreadySelected = item->is_selected();
1894 if ( !alreadySelected ) {
1895 item->select();
1896 set_changed();
1897 if ( docallback ) {
1898 do_callback_for_item(item, FL_TREE_REASON_SELECTED);
1899 }
1900 redraw();
1901 return(1);
1902 }
1903 #if FLTK_ABI_VERSION >= 10301
1904 // NEW
1905 if ( alreadySelected ) {
1906 if ( (item_reselect_mode() == FL_TREE_SELECTABLE_ALWAYS) && docallback ) {
1907 do_callback_for_item(item, FL_TREE_REASON_RESELECTED);
1908 }
1909 }
1910 #endif /*FLTK_ABI_VERSION*/
1911 return(0);
1912 }
1913
1914 /// Select the item specified by \p 'path'.
1915 ///
1916 /// Invokes the callback depending on the value of optional
1917 /// parameter \p 'docallback'.<br>
1918 /// Handles calling redraw() if anything changed.
1919 ///
1920 /// Items or submenus that themselves contain slashes ('/' or '\')
1921 /// should be escaped, e.g. select("Holidays/12\\/25\\/2010").
1922 ///
1923 /// The callback can use callback_item() and callback_reason() respectively to determine
1924 /// the item changed and the reason the callback was called.
1925 ///
1926 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
1927 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1928 /// - 0 - the callback() is not invoked
1929 /// - 1 - the callback() is invoked if item changed state (default),
1930 /// callback_reason() will be FL_TREE_REASON_SELECTED
1931 /// \returns
1932 /// - 1 : OK: item's state was changed
1933 /// - 0 : OK: item was already selected, no change was made
1934 /// - -1 : ERROR: item was not found
1935 ///
1936 int Fl_Tree::select(const char *path, int docallback) {
1937 Fl_Tree_Item *item = find_item(path);
1938 if ( ! item ) return(-1);
1939 return(select(item, docallback));
1940 }
1941
1942 /// Toggle the select state of the specified \p 'item'.
1943 ///
1944 /// Invokes the callback depending on the value of optional
1945 /// parameter \p 'docallback'.<br>
1946 /// Handles calling redraw() if anything changed.
1947 ///
1948 /// The callback can use callback_item() and callback_reason() respectively to determine
1949 /// the item changed and the reason the callback was called.
1950 ///
1951 /// \param[in] item -- the item to be selected. Must not be NULL.
1952 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1953 /// - 0 - the callback() is not invoked
1954 /// - 1 - the callback() is invoked (default), callback_reason() will be
1955 /// either FL_TREE_REASON_SELECTED or FL_TREE_REASON_DESELECTED
1956 ///
1957 void Fl_Tree::select_toggle(Fl_Tree_Item *item, int docallback) {
1958 item->select_toggle();
1959 set_changed();
1960 if ( docallback ) {
1961 do_callback_for_item(item, item->is_selected() ? FL_TREE_REASON_SELECTED
1962 : FL_TREE_REASON_DESELECTED);
1963 }
1964 redraw();
1965 }
1966
1967 /// Deselect the specified \p item.
1968 ///
1969 /// Invokes the callback depending on the value of optional
1970 /// parameter \p 'docallback'.<br>
1971 /// Handles calling redraw() if anything changed.
1972 ///
1973 /// The callback can use callback_item() and callback_reason() respectively to determine
1974 /// the item changed and the reason the callback was called.
1975 ///
1976 /// \param[in] item -- the item to be deselected. Must not be NULL.
1977 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
1978 /// - 0 - the callback() is not invoked
1979 /// - 1 - the callback() is invoked if item changed state (default),
1980 /// callback_reason() will be FL_TREE_REASON_DESELECTED
1981 /// \returns
1982 /// - 0 - item was already deselected, no change was made
1983 /// - 1 - item's state was changed
1984 ///
1985 int Fl_Tree::deselect(Fl_Tree_Item *item, int docallback) {
1986 if ( item->is_selected() ) {
1987 item->deselect();
1988 set_changed();
1989 if ( docallback ) {
1990 do_callback_for_item(item, FL_TREE_REASON_DESELECTED);
1991 }
1992 redraw();
1993 return(1);
1994 }
1995 return(0);
1996 }
1997
1998 /// Deselect an item specified by \p 'path'.
1999 ///
2000 /// Invokes the callback depending on the value of optional
2001 /// parameter \p 'docallback'.<br>
2002 /// Handles calling redraw() if anything changed.
2003 ///
2004 /// Items or submenus that themselves contain slashes ('/' or '\')
2005 /// should be escaped, e.g. deselect("Holidays/12\\/25\\/2010").
2006 ///
2007 /// The callback can use callback_item() and callback_reason() respectively to determine
2008 /// the item changed and the reason the callback was called.
2009 ///
2010 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
2011 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
2012 /// - 0 - the callback() is not invoked
2013 /// - 1 - the callback() is invoked if item changed state (default),
2014 /// callback_reason() will be FL_TREE_REASON_DESELECTED
2015 /// \returns
2016 /// - 1 - OK: item's state was changed
2017 /// - 0 - OK: item was already deselected, no change was made
2018 /// - -1 - ERROR: item was not found
2019 ///
2020 int Fl_Tree::deselect(const char *path, int docallback) {
2021 Fl_Tree_Item *item = find_item(path);
2022 if ( ! item ) return(-1);
2023 return(deselect(item, docallback));
2024 }
2025
2026 /// Deselect \p 'item' and all its children.
2027 ///
2028 /// If item is NULL, first() is used.<br>
2029 /// Invokes the callback depending on the value of optional
2030 /// parameter \p 'docallback'.<br>
2031 /// Handles calling redraw() if anything changed.
2032 ///
2033 /// The callback can use callback_item() and callback_reason() respectively to determine
2034 /// the item changed and the reason the callback was called.
2035 ///
2036 /// \param[in] item The item that will be deselected (along with all its children).
2037 /// If NULL, first() is used.
2038 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
2039 /// - 0 - the callback() is not invoked
2040 /// - 1 - the callback() is invoked for each item that changed state (default),
2041 /// callback_reason() will be FL_TREE_REASON_DESELECTED
2042 /// \returns Count of how many items were actually changed to the deselected state.
2043 ///
2044 int Fl_Tree::deselect_all(Fl_Tree_Item *item, int docallback) {
2045 item = item ? item : first(); // NULL? use first()
2046 if ( ! item ) return(0);
2047 int count = 0;
2048 // Deselect item
2049 if ( item->is_selected() )
2050 if ( deselect(item, docallback) )
2051 ++count;
2052 // Deselect its children
2053 for ( int t=0; t<item->children(); t++ ) {
2054 count += deselect_all(item->child(t), docallback); // recurse
2055 }
2056 return(count);
2057 }
2058
2059 /// Select only the specified item, deselecting all others that might be selected.
2060 ///
2061 /// If \p 'selitem' is 0, first() is used.<br>
2062 /// Invokes the callback depending on the value of optional
2063 /// parameter \p 'docallback'.<br>
2064 /// Handles calling redraw() if anything changed.
2065 ///
2066 /// The callback can use callback_item() and callback_reason() respectively to determine
2067 /// the item changed and the reason the callback was called.
2068 ///
2069 /// \param[in] selitem The item to be selected. If NULL, first() is used.
2070 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
2071 /// - 0 - the callback() is not invoked
2072 /// - 1 - the callback() is invoked for each item that changed state (default),
2073 /// callback_reason() will be either FL_TREE_REASON_SELECTED or
2074 /// FL_TREE_REASON_DESELECTED
2075 /// \returns The number of items whose selection states were changed, if any.
2076 ///
2077 int Fl_Tree::select_only(Fl_Tree_Item *selitem, int docallback) {
2078 selitem = selitem ? selitem : first(); // NULL? use first()
2079 if ( ! selitem ) return(0);
2080 int changed = 0;
2081 // Deselect everything first.
2082 // Prevents callbacks from seeing more than one item selected.
2083 //
2084 for ( Fl_Tree_Item *item = first(); item; item = item->next() ) {
2085 if ( item == selitem ) continue; // don't do anything to selitem yet..
2086 if ( item->is_selected() ) {
2087 deselect(item, docallback);
2088 ++changed;
2089 }
2090 }
2091 #if FLTK_ABI_VERSION >= 10301
2092 // Should we 'reselect' item if already selected?
2093 if ( selitem->is_selected() && (item_reselect_mode()==FL_TREE_SELECTABLE_ALWAYS) ) {
2094 // Selection unchanged, so no ++changed
2095 select(selitem, docallback); // do callback with reason=reselect
2096 } else if ( !selitem->is_selected() ) {
2097 // Item was not already selected, select and indicate changed
2098 select(selitem, docallback);
2099 ++changed;
2100 }
2101 #else
2102 if ( !selitem->is_selected() ) {
2103 // All items deselected, now select the one we want
2104 select(selitem, docallback);
2105 ++changed;
2106 }
2107 #endif
2108 return(changed);
2109 }
2110
2111 /// Select \p 'item' and all its children.
2112 ///
2113 /// If item is NULL, first() is used.<br>
2114 /// Invokes the callback depending on the value of optional
2115 /// parameter \p 'docallback'.<br>
2116 /// Handles calling redraw() if anything changed.
2117 ///
2118 /// The callback can use callback_item() and callback_reason() respectively to determine
2119 /// the item changed and the reason the callback was called.
2120 ///
2121 /// \param[in] item The item that will be selected (along with all its children).
2122 /// If NULL, first() is used.
2123 /// \param[in] docallback -- A flag that determines if the callback() is invoked or not:
2124 /// - 0 - the callback() is not invoked
2125 /// - 1 - the callback() is invoked for each item that changed state (default),
2126 /// callback_reason() will be FL_TREE_REASON_SELECTED
2127 /// \returns Count of how many items were actually changed to the selected state.
2128 ///
2129 int Fl_Tree::select_all(Fl_Tree_Item *item, int docallback) {
2130 item = item ? item : first(); // NULL? use first()
2131 if ( ! item ) return(0);
2132 int count = 0;
2133 // Select item
2134 if ( !item->is_selected() )
2135 if ( select(item, docallback) )
2136 ++count;
2137 // Select its children
2138 for ( int t=0; t<item->children(); t++ ) {
2139 count += select_all(item->child(t), docallback); // recurse
2140 }
2141 return(count);
2142 }
2143
2144 /// Get the item that currently has keyboard focus.
2145 Fl_Tree_Item* Fl_Tree::get_item_focus() const {
2146 return(_item_focus);
2147 }
2148
2149 /// Set the item that currently should have keyboard focus.
2150 ///
2151 /// Handles calling redraw() to update the focus box (if it is visible).
2152 ///
2153 /// \param[in] item The item that should take focus. If NULL, none will have focus.
2154 ///
2155 void Fl_Tree::set_item_focus(Fl_Tree_Item *item) {
2156 if ( _item_focus != item ) { // changed?
2157 _item_focus = item; // update
2158 if ( visible_focus() ) redraw(); // redraw to update focus box
2159 }
2160 }
2161
2162 /// See if the specified \p 'item' is selected.
2163 ///
2164 /// \param[in] item -- the item to be tested. Must not be NULL.
2165 ///
2166 /// \return
2167 /// - 1 : item selected
2168 /// - 0 : item deselected
2169 ///
2170 int Fl_Tree::is_selected(Fl_Tree_Item *item) const {
2171 return(item->is_selected()?1:0);
2172 }
2173
2174 /// See if item specified by \p 'path' is selected.
2175 ///
2176 /// Items or submenus that themselves contain slashes ('/' or '\')
2177 /// should be escaped, e.g. is_selected("Holidays/12\\/25\\/2010").
2178 ///
2179 /// \param[in] path -- the tree item's pathname (e.g. "Flintstones/Fred")
2180 /// \returns
2181 /// - 1 : item selected
2182 /// - 0 : item deselected
2183 /// - -1 : item was not found
2184 ///
2185 int Fl_Tree::is_selected(const char *path) {
2186 Fl_Tree_Item *item = find_item(path);
2187 if ( ! item ) return(-1);
2188 return(is_selected(item));
2189 }
2190
2191 /// Get the default label fontsize used for creating new items.
2192 Fl_Fontsize Fl_Tree::item_labelsize() const {
2193 return(_prefs.labelsize());
2194 }
2195
2196 /// Set the default label font size used for creating new items.
2197 /// To change the font size on a per-item basis, use Fl_Tree_Item::labelsize(Fl_Fontsize)
2198 ///
2199 void Fl_Tree::item_labelsize(Fl_Fontsize val) {
2200 _prefs.labelsize(val);
2201 }
2202
2203 /// Get the default font face used for creating new items.
2204 Fl_Font Fl_Tree::item_labelfont() const {
2205 return(_prefs.labelfont());
2206 }
2207
2208 /// Set the default font face used for creating new items.
2209 /// To change the font face on a per-item basis, use Fl_Tree_Item::labelfont(Fl_Font)
2210 ///
2211 void Fl_Tree::item_labelfont(Fl_Font val) {
2212 _prefs.labelfont(val);
2213 }
2214
2215 /// Get the default label foreground color used for creating new items.
2216 Fl_Color Fl_Tree::item_labelfgcolor(void) const {
2217 return(_prefs.labelfgcolor());
2218 }
2219
2220 /// Set the default label foreground color used for creating new items.
2221 /// To change the foreground color on a per-item basis, use Fl_Tree_Item::labelfgcolor(Fl_Color)
2222 ///
2223 void Fl_Tree::item_labelfgcolor(Fl_Color val) {
2224 _prefs.labelfgcolor(val);
2225 }
2226
2227 /// Get the default label background color used for creating new items.
2228 /// If the color is 0xffffffff, it is 'transparent'.
2229 Fl_Color Fl_Tree::item_labelbgcolor(void) const {
2230 return(_prefs.labelbgcolor());
2231 }
2232
2233 /// Set the default label background color used for creating new items.
2234 /// A special case is made for color 0xffffffff (default) which is treated as 'transparent'.
2235 /// To change the background color on a per-item basis, use Fl_Tree_Item::labelbgcolor(Fl_Color)
2236 ///
2237 void Fl_Tree::item_labelbgcolor(Fl_Color val) {
2238 _prefs.labelbgcolor(val);
2239 }
2240
2241 /// Get the connector color used for tree connection lines.
2242 Fl_Color Fl_Tree::connectorcolor() const {
2243 return(_prefs.connectorcolor());
2244 }
2245
2246 /// Set the connector color used for tree connection lines.
2247 void Fl_Tree::connectorcolor(Fl_Color val) {
2248 _prefs.connectorcolor(val);
2249 }
2250
2251 /// Get the amount of white space (in pixels) that should appear
2252 /// between the widget's left border and the tree's contents.
2253 ///
2254 int Fl_Tree::marginleft() const {
2255 return(_prefs.marginleft());
2256 }
2257
2258 /// Set the amount of white space (in pixels) that should appear
2259 /// between the widget's left border and the left side of the tree's contents.
2260 ///
2261 void Fl_Tree::marginleft(int val) {
2262 _prefs.marginleft(val);
2263 redraw();
2264 recalc_tree();
2265 }
2266
2267 /// Get the amount of white space (in pixels) that should appear
2268 /// between the widget's top border and the top of the tree's contents.
2269 ///
2270 int Fl_Tree::margintop() const {
2271 return(_prefs.margintop());
2272 }
2273
2274 /// Sets the amount of white space (in pixels) that should appear
2275 /// between the widget's top border and the top of the tree's contents.
2276 ///
2277 void Fl_Tree::margintop(int val) {
2278 _prefs.margintop(val);
2279 redraw();
2280 recalc_tree();
2281 }
2282
2283 #if FLTK_ABI_VERSION >= 10301
2284 /// Get the amount of white space (in pixels) that should appear
2285 /// below the last visible item when the vertical scroller is scrolled to the bottom.
2286 ///
2287 int Fl_Tree::marginbottom() const {
2288 return(_prefs.marginbottom());
2289 }
2290
2291 /// Sets the amount of white space (in pixels) that should appear
2292 /// below the last visible item when the vertical scroller is scrolled to the bottom.
2293 ///
2294 void Fl_Tree::marginbottom(int val) {
2295 _prefs.marginbottom(val);
2296 redraw();
2297 recalc_tree();
2298 }
2299 #endif /*FLTK_ABI_VERSION*/
2300
2301 /// Get the amount of white space (in pixels) that should appear
2302 /// between items in the tree.
2303 ///
2304 int Fl_Tree::linespacing() const {
2305 return(_prefs.linespacing());
2306 }
2307
2308 /// Sets the amount of white space (in pixels) that should appear
2309 /// between items in the tree.
2310 ///
2311 void Fl_Tree::linespacing(int val) {
2312 _prefs.linespacing(val);
2313 redraw();
2314 recalc_tree();
2315 }
2316
2317 /// Get the amount of white space (in pixels) that should appear
2318 /// below an open child tree's contents.
2319 ///
2320 int Fl_Tree::openchild_marginbottom() const {
2321 return(_prefs.openchild_marginbottom());
2322 }
2323
2324 /// Set the amount of white space (in pixels) that should appear
2325 /// below an open child tree's contents.
2326 ///
2327 void Fl_Tree::openchild_marginbottom(int val) {
2328 _prefs.openchild_marginbottom(val);
2329 redraw();
2330 recalc_tree();
2331 }
2332
2333 /// Get the amount of white space (in pixels) that should appear
2334 /// to the left of the usericon.
2335 int Fl_Tree::usericonmarginleft() const {
2336 return(_prefs.usericonmarginleft());
2337 }
2338
2339 /// Set the amount of white space (in pixels) that should appear
2340 /// to the left of the usericon.
2341 void Fl_Tree::usericonmarginleft(int val) {
2342 _prefs.usericonmarginleft(val);
2343 redraw();
2344 recalc_tree();
2345 }
2346
2347 /// Get the amount of white space (in pixels) that should appear
2348 /// to the left of the label text.
2349 int Fl_Tree::labelmarginleft() const {
2350 return(_prefs.labelmarginleft());
2351 }
2352
2353 /// Set the amount of white space (in pixels) that should appear
2354 /// to the left of the label text.
2355 void Fl_Tree::labelmarginleft(int val) {
2356 _prefs.labelmarginleft(val);
2357 redraw();
2358 recalc_tree();
2359 }
2360
2361 #if FLTK_ABI_VERSION >= 10301
2362 /// Get the amount of white space (in pixels) that should appear
2363 /// to the left of the child fltk widget (if any).
2364 int Fl_Tree::widgetmarginleft() const {
2365 return(_prefs.widgetmarginleft());
2366 }
2367
2368 /// Set the amount of white space (in pixels) that should appear
2369 /// to the left of the child fltk widget (if any).
2370 void Fl_Tree::widgetmarginleft(int val) {
2371 _prefs.widgetmarginleft(val);
2372 redraw();
2373 recalc_tree();
2374 }
2375 #endif /*FLTK_ABI_VERSION*/
2376
2377 /// Gets the width of the horizontal connection lines (in pixels)
2378 /// that appear to the left of each tree item's label.
2379 ///
2380 int Fl_Tree::connectorwidth() const {
2381 return(_prefs.connectorwidth());
2382 }
2383
2384 /// Sets the width of the horizontal connection lines (in pixels)
2385 /// that appear to the left of each tree item's label.
2386 ///
2387 void Fl_Tree::connectorwidth(int val) {
2388 _prefs.connectorwidth(val);
2389 redraw();
2390 recalc_tree();
2391 }
2392
2393 /// Returns the Fl_Image being used as the default user icon for all
2394 /// newly created items.
2395 /// Returns zero if no icon has been set, which is the default.
2396 ///
2397 Fl_Image* Fl_Tree::usericon() const {
2398 return(_prefs.usericon());
2399 }
2400
2401 /// Sets the Fl_Image to be used as the default user icon for all
2402 /// newly created items.
2403 ///
2404 /// If you want to specify user icons on a per-item basis,
2405 /// use Fl_Tree_Item::usericon() instead.
2406 ///
2407 /// \param[in] val -- The new image to be used, or
2408 /// zero to disable user icons.
2409 ///
2410 void Fl_Tree::usericon(Fl_Image *val) {
2411 _prefs.usericon(val);
2412 redraw();
2413 recalc_tree();
2414 }
2415
2416 /// Returns the icon to be used as the 'open' icon.
2417 /// If none was set, the internal default is returned,
2418 /// a simple '[+]' icon.
2419 ///
2420 Fl_Image* Fl_Tree::openicon() const {
2421 return(_prefs.openicon());
2422 }
2423
2424 /// Sets the icon to be used as the 'open' icon.
2425 /// This overrides the built in default '[+]' icon.
2426 ///
2427 /// \param[in] val -- The new image, or zero to use the default [+] icon.
2428 ///
2429 void Fl_Tree::openicon(Fl_Image *val) {
2430 _prefs.openicon(val);
2431 redraw();
2432 recalc_tree();
2433 }
2434
2435 /// Returns the icon to be used as the 'close' icon.
2436 /// If none was set, the internal default is returned,
2437 /// a simple '[-]' icon.
2438 ///
2439 Fl_Image* Fl_Tree::closeicon() const {
2440 return(_prefs.closeicon());
2441 }
2442
2443 /// Sets the icon to be used as the 'close' icon.
2444 /// This overrides the built in default '[-]' icon.
2445 ///
2446 /// \param[in] val -- The new image, or zero to use the default [-] icon.
2447 ///
2448 void Fl_Tree::closeicon(Fl_Image *val) {
2449 _prefs.closeicon(val);
2450 redraw();
2451 recalc_tree();
2452 }
2453
2454 /// Returns 1 if the collapse icon is enabled, 0 if not.
2455 /// \see showcollapse(int)
2456 int Fl_Tree::showcollapse() const {
2457 return(_prefs.showcollapse());
2458 }
2459
2460 /// Set if we should show the collapse icon or not.
2461 /// If collapse icons are disabled, the user will not be able
2462 /// to interactively collapse items in the tree, unless the application
2463 /// provides some other means via open() and close().
2464 ///
2465 /// \param[in] val 1: shows collapse icons (default),\n
2466 /// 0: hides collapse icons.
2467 ///
2468 void Fl_Tree::showcollapse(int val) {
2469 _prefs.showcollapse(val);
2470 redraw();
2471 recalc_tree();
2472 }
2473
2474 /// Returns 1 if the root item is to be shown, or 0 if not.
2475 int Fl_Tree::showroot() const {
2476 return(_prefs.showroot());
2477 }
2478
2479 /// Set if the root item should be shown or not.
2480 /// \param[in] val 1 -- show the root item (default)\n
2481 /// 0 -- hide the root item.
2482 ///
2483 void Fl_Tree::showroot(int val) {
2484 _prefs.showroot(val);
2485 redraw();
2486 recalc_tree();
2487 }
2488
2489 /// Returns the line drawing style for inter-connecting items.
2490 Fl_Tree_Connector Fl_Tree::connectorstyle() const {
2491 return(_prefs.connectorstyle());
2492 }
2493
2494 /// Sets the line drawing style for inter-connecting items.
2495 /// See ::Fl_Tree_Connector for possible values.
2496 ///
2497 void Fl_Tree::connectorstyle(Fl_Tree_Connector val) {
2498 _prefs.connectorstyle(val);
2499 redraw();
2500 }
2501
2502 /// Set the default sort order used when items are added to the tree.
2503 /// See ::Fl_Tree_Sort for possible values.
2504 ///
2505 Fl_Tree_Sort Fl_Tree::sortorder() const {
2506 return(_prefs.sortorder());
2507 }
2508
2509 /// Gets the sort order used to add items to the tree.
2510 void Fl_Tree::sortorder(Fl_Tree_Sort val) {
2511 _prefs.sortorder(val);
2512 // no redraw().. only affects new add()itions
2513 }
2514
2515 /// Sets the style of box used to draw selected items.
2516 /// This is an fltk ::Fl_Boxtype.
2517 /// The default is influenced by FLTK's current Fl::scheme()
2518 ///
2519 Fl_Boxtype Fl_Tree::selectbox() const {
2520 return(_prefs.selectbox());
2521 }
2522
2523 /// Gets the style of box used to draw selected items.
2524 /// This is an fltk ::Fl_Boxtype.
2525 /// The default is influenced by FLTK's current Fl::scheme()
2526 ///
2527 void Fl_Tree::selectbox(Fl_Boxtype val) {
2528 _prefs.selectbox(val);
2529 redraw();
2530 }
2531
2532 /// Gets the tree's current selection mode.
2533 /// See ::Fl_Tree_Select for possible values.
2534 ///
2535 Fl_Tree_Select Fl_Tree::selectmode() const {
2536 return(_prefs.selectmode());
2537 }
2538
2539 /// Sets the tree's selection mode.
2540 /// See ::Fl_Tree_Select for possible values.
2541 ///
2542 void Fl_Tree::selectmode(Fl_Tree_Select val) {
2543 _prefs.selectmode(val);
2544 }
2545
2546 #if FLTK_ABI_VERSION >= 10301
2547 /// Returns the current item re/selection mode.
2548 /// \version 1.3.1 ABI feature
2549 ///
2550 Fl_Tree_Item_Reselect_Mode Fl_Tree::item_reselect_mode() const {
2551 return(_prefs.item_reselect_mode());
2552 }
2553
2554 /// Sets the item re/selection mode.
2555 /// See ::Fl_Tree_Item_Reselect_Mode for possible values.
2556 /// \version 1.3.1 ABI feature
2557 ///
2558 void Fl_Tree::item_reselect_mode(Fl_Tree_Item_Reselect_Mode mode) {
2559 _prefs.item_reselect_mode(mode);
2560 }
2561
2562 /// Get the 'item draw mode' used for the tree.
2563 /// \version 1.3.1 ABI feature
2564 ///
2565 Fl_Tree_Item_Draw_Mode Fl_Tree::item_draw_mode() const {
2566 return(_prefs.item_draw_mode());
2567 }
2568
2569 /// Set the 'item draw mode' used for the tree to \p 'mode'.
2570 ///
2571 /// This affects how items in the tree are drawn,
2572 /// such as when a widget() is defined.
2573 /// See ::Fl_Tree_Item_Draw_Mode for possible values.
2574 /// \version 1.3.1 ABI feature
2575 ///
2576 void Fl_Tree::item_draw_mode(Fl_Tree_Item_Draw_Mode mode) {
2577 _prefs.item_draw_mode(mode);
2578 }
2579
2580 /// Set the 'item draw mode' used for the tree to integer \p 'mode'.
2581 ///
2582 /// This affects how items in the tree are drawn,
2583 /// such as when a widget() is defined.
2584 /// See ::Fl_Tree_Item_Draw_Mode for possible values.
2585 /// \version 1.3.1 ABI feature
2586 ///
2587 void Fl_Tree::item_draw_mode(int mode) {
2588 _prefs.item_draw_mode(Fl_Tree_Item_Draw_Mode(mode));
2589 }
2590 #endif
2591
2592 /// See if \p 'item' is currently displayed on-screen (visible within the widget).
2593 ///
2594 /// This can be used to detect if the item is scrolled off-screen.
2595 /// Checks to see if the item's vertical position is within the top and bottom
2596 /// edges of the display window. This does NOT take into account the hide() / show()
2597 /// or open() / close() status of the item.
2598 ///
2599 /// \param[in] item The item to be checked. If NULL, first() is used.
2600 /// \returns 1 if displayed, 0 if scrolled off screen or no items are in tree.
2601 ///
2602 int Fl_Tree::displayed(Fl_Tree_Item *item) {
2603 item = item ? item : first();
2604 if (!item) return(0);
2605 return( (item->y() >= y()) && (item->y() <= (y()+h()-item->h())) ? 1 : 0);
2606 }
2607
2608 /// Adjust the vertical scrollbar so that \p 'item' is visible
2609 /// \p 'yoff' pixels from the top of the Fl_Tree widget's display.
2610 ///
2611 /// For instance, yoff=0 will position the item at the top.
2612 ///
2613 /// If yoff is larger than the vertical scrollbar's limit,
2614 /// the value will be clipped. So if yoff=100, but scrollbar's max
2615 /// is 50, then 50 will be used.
2616 ///
2617 /// \param[in] item The item to be shown. If NULL, first() is used.
2618 /// \param[in] yoff The pixel offset from the top for the displayed position.
2619 ///
2620 /// \see show_item_top(), show_item_middle(), show_item_bottom()
2621 ///
2622 void Fl_Tree::show_item(Fl_Tree_Item *item, int yoff) {
2623 item = item ? item : first();
2624 if (!item) return;
2625 int newval = item->y() - y() - yoff + (int)_vscroll->value();
2626 if ( newval < _vscroll->minimum() ) newval = (int)_vscroll->minimum();
2627 if ( newval > _vscroll->maximum() ) newval = (int)_vscroll->maximum();
2628 _vscroll->value(newval);
2629 redraw();
2630 }
2631
2632 /// Adjust the vertical scrollbar to show \p 'item' at the top
2633 /// of the display IF it is currently off-screen (for instance show_item_top()).
2634 /// If it is already on-screen, no change is made.
2635 ///
2636 /// \param[in] item The item to be shown. If NULL, first() is used.
2637 ///
2638 /// \see show_item_top(), show_item_middle(), show_item_bottom()
2639 ///
2640 void Fl_Tree::show_item(Fl_Tree_Item *item) {
2641 item = item ? item : first();
2642 if (!item) return;
2643 if ( displayed(item) ) return;
2644 show_item_top(item);
2645 }
2646
2647 /// Adjust the vertical scrollbar so that \p 'item' is at the top of the display.
2648 ///
2649 /// \param[in] item The item to be shown. If NULL, first() is used.
2650 ///
2651 void Fl_Tree::show_item_top(Fl_Tree_Item *item) {
2652 item = item ? item : first();
2653 if (item) show_item(item, 0);
2654 }
2655
2656 /// Adjust the vertical scrollbar so that \p 'item' is in the middle of the display.
2657 ///
2658 /// \param[in] item The item to be shown. If NULL, first() is used.
2659 ///
2660 void Fl_Tree::show_item_middle(Fl_Tree_Item *item) {
2661 item = item ? item : first();
2662 #if FLTK_ABI_VERSION >= 10303
2663 if (item) show_item(item, (_tih/2)-(item->h()/2));
2664 #else
2665 if (item) show_item(item, (h()/2)-(item->h()/2));
2666 #endif
2667 }
2668
2669 /// Adjust the vertical scrollbar so that \p 'item' is at the bottom of the display.
2670 ///
2671 /// \param[in] item The item to be shown. If NULL, first() is used.
2672 ///
2673 void Fl_Tree::show_item_bottom(Fl_Tree_Item *item) {
2674 item = item ? item : first();
2675 #if FLTK_ABI_VERSION >= 10303
2676 if (item) show_item(item, _tih-item->h());
2677 #else
2678 if (item) show_item(item, h()-item->h());
2679 #endif
2680 }
2681
2682 /// Displays \p 'item', scrolling the tree as necessary.
2683 /// \param[in] item The item to be displayed. If NULL, first() is used.
2684 ///
2685 void Fl_Tree::display(Fl_Tree_Item *item) {
2686 item = item ? item : first();
2687 if (item) show_item_middle(item);
2688 }
2689
2690 /// Returns the vertical scroll position as a pixel offset.
2691 /// The position returned is how many pixels of the tree are scrolled off the top edge
2692 /// of the screen.
2693 /// \see vposition(int), hposition(), hposition(int)
2694 ///
2695 int Fl_Tree::vposition() const {
2696 return((int)_vscroll->value());
2697 }
2698
2699 /// Sets the vertical scroll offset to position \p 'pos'.
2700 /// The position is how many pixels of the tree are scrolled off the top edge
2701 /// of the screen.
2702 /// \param[in] pos The vertical position (in pixels) to scroll the tree to.
2703 /// \see vposition(), hposition(), hposition(int)
2704 ///
2705 void Fl_Tree::vposition(int pos) {
2706 if (pos < 0) pos = 0;
2707 if (pos > _vscroll->maximum()) pos = (int)_vscroll->maximum();
2708 if (pos == _vscroll->value()) return;
2709 _vscroll->value(pos);
2710 redraw();
2711 }
2712
2713 /// Returns the horizontal scroll position as a pixel offset.
2714 /// The position returned is how many pixels of the tree are scrolled off the left edge
2715 /// of the screen.
2716 /// \see hposition(int), vposition(), vposition(int)
2717 /// \note Must be using FLTK ABI 1.3.3 or higher for this to be effective.
2718 ///
2719 int Fl_Tree::hposition() const {
2720 #if FLTK_ABI_VERSION >= 10303
2721 return((int)_hscroll->value());
2722 #else
2723 return(0);
2724 #endif
2725 }
2726
2727 /// Sets the horizontal scroll offset to position \p 'pos'.
2728 /// The position is how many pixels of the tree are scrolled off the left edge
2729 /// of the screen.
2730 /// \param[in] pos The vertical position (in pixels) to scroll the tree to.
2731 /// \see hposition(), vposition(), vposition(int)
2732 /// \note Must be using FLTK ABI 1.3.3 or higher for this to be effective.
2733 ///
2734 void Fl_Tree::hposition(int pos) {
2735 #if FLTK_ABI_VERSION >= 10303
2736 if (pos < 0) pos = 0;
2737 if (pos > _hscroll->maximum()) pos = (int)_hscroll->maximum();
2738 if (pos == _hscroll->value()) return;
2739 _hscroll->value(pos);
2740 redraw();
2741 #endif
2742 }
2743
2744 /// See if widget \p 'w' is one of the Fl_Tree widget's scrollbars.
2745 /// Use this to skip over the scrollbars when walking the child() array. Example:
2746 /// \code
2747 /// for ( int i=0; i<tree->children(); i++ ) { // walk children
2748 /// Fl_Widget *w = tree->child(i);
2749 /// if ( tree->is_scrollbar(w) ) continue; // skip scrollbars
2750 /// ..do work here..
2751 /// }
2752 /// \endcode
2753 /// \param[in] w Widget to test
2754 /// \returns 1 if \p w is a scrollbar, 0 if not.
2755 /// \todo should be const
2756 ///
2757 int Fl_Tree::is_scrollbar(Fl_Widget *w) {
2758 #if FLTK_ABI_VERSION >= 10303
2759 return( (w==_vscroll || w==_hscroll) ? 1 : 0 );
2760 #else
2761 return( (w==_vscroll) ? 1 : 0 );
2762 #endif
2763 }
2764
2765 /// Gets the default size of scrollbars' troughs for this widget
2766 /// in pixels.
2767 ///
2768 /// If this value is zero (default), this widget will use the global
2769 /// Fl::scrollbar_size() value as the scrollbar's width.
2770 ///
2771 /// \returns Scrollbar size in pixels, or 0 if the global Fl::scrollbar_size() is being used.
2772 /// \see Fl::scrollbar_size(int)
2773 ///
2774 int Fl_Tree::scrollbar_size() const {
2775 return(_scrollbar_size);
2776 }
2777
2778 /// Sets the pixel size of the scrollbars' troughs to \p 'size'
2779 /// for this widget, in pixels.
2780 ///
2781 /// Normally you should not need this method, and should use the global
2782 /// Fl::scrollbar_size(int) instead to manage the size of ALL
2783 /// your widgets' scrollbars. This ensures your application
2784 /// has a consistent UI, and is the default behavior. Normally
2785 /// this is what you want.
2786 ///
2787 /// Only use this method if you really need to override just THIS
2788 /// instance of the widget's scrollbar size. (This need should be rare.)
2789 ///
2790 /// Setting \p size to the special value of 0 causes the widget to
2791 /// track the global Fl::scrollbar_size(), which is the default.
2792 ///
2793 /// \param[in] size Sets the scrollbar size in pixels.\n
2794 /// If 0 (default), scrollbar size tracks the global Fl::scrollbar_size()
2795 /// \see Fl::scrollbar_size()
2796 ///
2797 void Fl_Tree::scrollbar_size(int size) {
2798 _scrollbar_size = size;
2799 int scrollsize = _scrollbar_size ? _scrollbar_size : Fl::scrollbar_size();
2800 if ( _vscroll->w() != scrollsize ) {
2801 _vscroll->resize(x()+w()-scrollsize, h(), scrollsize, _vscroll->h());
2802 }
2803 #if FLTK_ABI_VERSION >= 10303
2804 if ( _hscroll->h() != scrollsize ) {
2805 _hscroll->resize(x(), y()+h()-scrollsize, _hscroll->w(), scrollsize);
2806 }
2807 // Changing scrollbar size affects _tiw/_tih + may affect scrollbar visibility
2808 calc_dimensions();
2809 #endif
2810 }
2811
2812 /// See if the vertical scrollbar is currently visible.
2813 /// \returns 1 if scrollbar visible, 0 if not.
2814 ///
2815 int Fl_Tree::is_vscroll_visible() const {
2816 return(_vscroll->visible() ? 1 : 0);
2817 }
2818
2819 /// See if the horizontal scrollbar is currently visible.
2820 /// \returns 1 if scrollbar visible, 0 if not.
2821 /// \note Must be using FLTK ABI 1.3.3 or higher for this to be effective.
2822 ///
2823 int Fl_Tree::is_hscroll_visible() const {
2824 #if FLTK_ABI_VERSION >= 10303
2825 return(_hscroll->visible() ? 1 : 0);
2826 #else
2827 return 0;
2828 #endif
2829 }
2830
2831 /// Do the callback for the specified \p 'item' using \p 'reason',
2832 /// setting the callback_item() and callback_reason().
2833 ///
2834 void Fl_Tree::do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason) {
2835 callback_reason(reason);
2836 callback_item(item);
2837 do_callback((Fl_Widget*)this, user_data());
2838 }
2839
2840 /// Sets the item that was changed for this callback.
2841 /// Used internally to pass the item that invoked the callback.
2842 ///
2843 void Fl_Tree::callback_item(Fl_Tree_Item* item) {
2844 _callback_item = item;
2845 }
2846
2847 /// Gets the item that caused the callback.
2848 /// The callback() can use this value to see which item changed.
2849 ///
2850 Fl_Tree_Item* Fl_Tree::callback_item() {
2851 return(_callback_item);
2852 }
2853
2854 /// Sets the reason for this callback.
2855 /// Used internally to pass the reason the callback was invoked.
2856 ///
2857 void Fl_Tree::callback_reason(Fl_Tree_Reason reason) {
2858 _callback_reason = reason;
2859 }
2860
2861 /// Gets the reason for this callback.
2862 ///
2863 /// The callback() can use this value to see why it was called. Example:
2864 /// \code
2865 /// void MyTreeCallback(Fl_Widget *w, void *userdata) {
2866 /// Fl_Tree *tree = (Fl_Tree*)w;
2867 /// Fl_Tree_Item *item = tree->callback_item(); // the item changed (can be NULL if more than one item was changed!)
2868 /// switch ( tree->callback_reason() ) { // reason callback was invoked
2869 /// case FL_TREE_REASON_OPENED: ..item was opened..
2870 /// case FL_TREE_REASON_CLOSED: ..item was closed..
2871 /// case FL_TREE_REASON_SELECTED: ..item was selected..
2872 /// case FL_TREE_REASON_RESELECTED: ..item was reselected (double-clicked, etc)..
2873 /// case FL_TREE_REASON_DESELECTED: ..item was deselected..
2874 /// }
2875 /// }
2876 /// \endcode
2877 ///
2878 /// \see item_reselect_mode() -- enables FL_TREE_REASON_RESELECTED events
2879 ///
2880 Fl_Tree_Reason Fl_Tree::callback_reason() const {
2881 return(_callback_reason);
2882 }
2883
2884 /**
2885 * Read a preferences database into the tree widget.
2886 * A preferences database is a hierarchical collection of data which can be
2887 * directly loaded into the tree view for inspection.
2888 * \param[in] prefs the Fl_Preferences database
2889 */
2890 void Fl_Tree::load(Fl_Preferences &prefs) {
2891 int i, j, n, pn = (int) strlen(prefs.path());
2892 char *p;
2893 const char *path = prefs.path();
2894 if (strcmp(path, ".")==0)
2895 path += 1; // root path is empty
2896 else
2897 path += 2; // child path starts with "./"
2898 n = prefs.groups();
2899 for (i=0; i<n; i++) {
2900 Fl_Preferences prefsChild(prefs, i);
2901 add(prefsChild.path()+2); // children always start with "./"
2902 load(prefsChild);
2903 }
2904 n = prefs.entries();
2905 for (i=0; i<n; i++) {
2906 // We must remove all fwd slashes in the key and value strings. Replace with backslash.
2907 char *key = strdup(prefs.entry(i));
2908 int kn = (int) strlen(key);
2909 for (j=0; j<kn; j++) {
2910 if (key[j]=='/') key[j]='\\';
2911 }
2912 char *val; prefs.get(key, val, "");
2913 int vn = (int) strlen(val);
2914 for (j=0; j<vn; j++) {
2915 if (val[j]=='/') val[j]='\\';
2916 }
2917 if (vn<40) {
2918 size_t sze = pn + strlen(key) + vn;
2919 p = (char*)malloc(sze+5);
2920 sprintf(p, "%s/%s = %s", path, key, val);
2921 } else {
2922 size_t sze = pn + strlen(key) + 40;
2923 p = (char*)malloc(sze+5);
2924 sprintf(p, "%s/%s = %.40s...", path, key, val);
2925 }
2926 add(p[0]=='/'?p+1:p);
2927 free(p);
2928 free(val);
2929 free(key);
2930 }
2931 }
2932
2933 /// Ensure the scrollbars are the last children
2934 void Fl_Tree::fix_scrollbar_order() {
2935 Fl_Widget** a = (Fl_Widget**)array();
2936 if (a[children()-1] != _vscroll) {
2937 int i,j;
2938 #if FLTK_ABI_VERSION >= 10303
2939 for (i = j = 0; j < children(); j++) {
2940 if (a[j] != _vscroll && a[j] != _hscroll ) a[i++] = a[j];
2941 }
2942 a[i++] = _hscroll;
2943 a[i++] = _vscroll;
2944 #else
2945 for (i = j = 0; j < children(); j++) {
2946 if (a[j] != _vscroll) a[i++] = a[j];
2947 }
2948 a[i++] = _vscroll;
2949 #endif
2950 }
2951 }
2952
2953 /// Schedule tree to recalc the entire tree size.
2954 /// \note Must be using FLTK ABI 1.3.3 or higher for this to be effective.
2955 ///
2956 void Fl_Tree::recalc_tree() {
2957 #if FLTK_ABI_VERSION >= 10303
2958 _tree_w = _tree_h = -1;
2959 #endif
2960 }
2961
2962 //
2963 // End of "$Id$".
2964 //
2965