2 // "$Id: Fl_Tree_Item.cxx 8589 2011-04-14 13:15:13Z manolo $"
8 #include <FL/Fl_Widget.H>
9 #include <FL/Fl_Tree_Item.H>
10 #include <FL/Fl_Tree_Prefs.H>
12 //////////////////////
14 //////////////////////
16 // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK
17 // Copyright (C) 2009-2010 by Greg Ercolano.
19 // This library is free software; you can redistribute it and/or
20 // modify it under the terms of the GNU Library General Public
21 // License as published by the Free Software Foundation; either
22 // version 2 of the License, or (at your option) any later version.
24 // This library is distributed in the hope that it will be useful,
25 // but WITHOUT ANY WARRANTY; without even the implied warranty of
26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 // Library General Public License for more details.
29 // You should have received a copy of the GNU Library General Public
30 // License along with this library; if not, write to the Free Software
31 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
35 // Was the last event inside the specified xywh?
36 static int event_inside(const int xywh
[4]) {
37 return(Fl::event_inside(xywh
[0],xywh
[1],xywh
[2],xywh
[3]));
41 /// Makes a new instance of Fl_Tree_Item using defaults from 'prefs'.
43 Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs
&prefs
) {
45 _labelfont
= prefs
.labelfont();
46 _labelsize
= prefs
.labelsize();
47 _labelfgcolor
= prefs
.labelfgcolor();
48 _labelbgcolor
= prefs
.labelbgcolor();
58 _collapse_xywh
[0] = 0;
59 _collapse_xywh
[1] = 0;
60 _collapse_xywh
[2] = 0;
61 _collapse_xywh
[3] = 0;
72 Fl_Tree_Item::~Fl_Tree_Item() {
77 _widget
= 0; // Fl_Group will handle destruction
78 _usericon
= 0; // user handled allocation
79 //_children.clear(); // array's destructor handles itself
83 Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Item
*o
) {
84 _label
= o
->label() ? strdup(o
->label()) : 0;
85 _labelfont
= o
->labelfont();
86 _labelsize
= o
->labelsize();
87 _labelfgcolor
= o
->labelfgcolor();
88 _labelbgcolor
= o
->labelbgcolor();
89 _widget
= o
->widget();
91 _visible
= o
->_visible
;
93 _selected
= o
->_selected
;
94 _xywh
[0] = o
->_xywh
[0];
95 _xywh
[1] = o
->_xywh
[1];
96 _xywh
[2] = o
->_xywh
[2];
97 _xywh
[3] = o
->_xywh
[3];
98 _collapse_xywh
[0] = o
->_collapse_xywh
[0];
99 _collapse_xywh
[1] = o
->_collapse_xywh
[1];
100 _collapse_xywh
[2] = o
->_collapse_xywh
[2];
101 _collapse_xywh
[3] = o
->_collapse_xywh
[3];
102 _label_xywh
[0] = o
->_label_xywh
[0];
103 _label_xywh
[1] = o
->_label_xywh
[1];
104 _label_xywh
[2] = o
->_label_xywh
[2];
105 _label_xywh
[3] = o
->_label_xywh
[3];
106 _usericon
= o
->usericon();
107 _userdata
= o
->user_data();
108 _parent
= o
->_parent
;
111 /// Print the tree as 'ascii art' to stdout.
112 /// Used mainly for debugging.
114 void Fl_Tree_Item::show_self(const char *indent
) const {
116 printf("%s-%s (%d children, this=%p, parent=%p depth=%d)\n",
117 indent
,label(),children(),(void*)this, (void*)_parent
, depth());
120 char *i2
= (char*)malloc(strlen(indent
) + 2);
123 for ( int t
=0; t
<children(); t
++ ) {
124 child(t
)->show_self(i2
);
130 /// Set the label. Makes a copy of the name.
131 void Fl_Tree_Item::label(const char *name
) {
132 if ( _label
) { free((void*)_label
); _label
= 0; }
133 _label
= name
? strdup(name
) : 0;
136 /// Return the label.
137 const char *Fl_Tree_Item::label() const {
141 /// Return child item for the specified 'index'.
142 const Fl_Tree_Item
*Fl_Tree_Item::child(int index
) const {
143 return(_children
[index
]);
146 /// Clear all the children for this item.
147 void Fl_Tree_Item::clear_children() {
151 /// Return the index of the immediate child of this item that has the label 'name'.
153 /// \returns index of found item, or -1 if not found.
155 int Fl_Tree_Item::find_child(const char *name
) {
157 for ( int t
=0; t
<children(); t
++ ) {
158 if ( child(t
)->label() ) {
159 if ( strcmp(child(t
)->label(), name
) == 0 ) {
168 /// Find child item by descending array of names. Does not include self in search.
169 /// Only Fl_Tree should need this method.
171 /// \returns item, or 0 if not found
173 const Fl_Tree_Item
*Fl_Tree_Item::find_child_item(char **arr
) const {
174 for ( int t
=0; t
<children(); t
++ ) {
175 if ( child(t
)->label() ) {
176 if ( strcmp(child(t
)->label(), *arr
) == 0 ) { // match?
177 if ( *(arr
+1) ) { // more in arr? descend
178 return(_children
[t
]->find_item(arr
+1));
179 } else { // end of arr? done
180 return(_children
[t
]);
188 /// Find child item by descending array of names. Does not include self in search.
189 /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead.
191 /// \returns item, or 0 if not found
193 Fl_Tree_Item
*Fl_Tree_Item::find_child_item(char **arr
) {
194 for ( int t
=0; t
<children(); t
++ ) {
195 if ( child(t
)->label() ) {
196 if ( strcmp(child(t
)->label(), *arr
) == 0 ) { // match?
197 if ( *(arr
+1) ) { // more in arr? descend
198 return(_children
[t
]->find_item(arr
+1));
199 } else { // end of arr? done
200 return(_children
[t
]);
208 /// Find item by descending array of \p names. Includes self in search.
209 /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead.
211 /// \returns item, or 0 if not found
213 const Fl_Tree_Item
*Fl_Tree_Item::find_item(char **names
) const {
214 if ( label() && strcmp(label(), *names
) == 0 ) { // match self?
215 if ( *(names
+1) == 0 ) { // end of names,
216 return(this); // found ourself.
219 if ( children() ) { // check children..
220 return(find_child_item(names
));
225 /// Find item by descending array of \p names. Includes self in search.
226 /// Only Fl_Tree should need this method.
228 /// \returns item, or 0 if not found
230 Fl_Tree_Item
*Fl_Tree_Item::find_item(char **names
) {
231 if ( label() && strcmp(label(), *names
) == 0 ) { // match self?
232 if ( *(names
+1) == 0 ) { // end of names,
233 return(this); // found ourself.
236 if ( children() ) { // check children..
237 return(find_child_item(names
));
242 /// Find the index number for the specified 'item'
243 /// in the current item's list of children.
245 /// \returns the index, or -1 if not found.
247 int Fl_Tree_Item::find_child(Fl_Tree_Item
*item
) {
248 for ( int t
=0; t
<children(); t
++ ) {
249 if ( item
== child(t
) ) {
256 /// Add a new child to this item with the name 'new_label', with defaults from 'prefs'.
257 /// An internally managed copy is made of the label string.
258 /// Adds the item based on the value of prefs.sortorder().
260 Fl_Tree_Item
*Fl_Tree_Item::add(const Fl_Tree_Prefs
&prefs
, const char *new_label
) {
261 Fl_Tree_Item
*item
= new Fl_Tree_Item(prefs
);
262 item
->label(new_label
);
263 item
->_parent
= this;
264 switch ( prefs
.sortorder() ) {
265 case FL_TREE_SORT_NONE
: {
269 case FL_TREE_SORT_ASCENDING
: {
270 for ( int t
=0; t
<_children
.total(); t
++ ) {
271 Fl_Tree_Item
*c
= _children
[t
];
272 if ( c
->label() && strcmp(c
->label(), new_label
) > 0 ) {
273 _children
.insert(t
, item
);
280 case FL_TREE_SORT_DESCENDING
: {
281 for ( int t
=0; t
<_children
.total(); t
++ ) {
282 Fl_Tree_Item
*c
= _children
[t
];
283 if ( c
->label() && strcmp(c
->label(), new_label
) < 0 ) {
284 _children
.insert(t
, item
);
295 /// Descend into the path specified by \p arr, and add a new child there.
296 /// Should be used only by Fl_Tree's internals.
297 /// Adds the item based on the value of prefs.sortorder().
298 /// \returns the item added.
300 Fl_Tree_Item
*Fl_Tree_Item::add(const Fl_Tree_Prefs
&prefs
, char **arr
) {
301 int t
= find_child(*arr
);
302 Fl_Tree_Item
*item
= 0;
304 item
= (Fl_Tree_Item
*)add(prefs
, *arr
);
306 item
= (Fl_Tree_Item
*)child(t
);
308 if ( *(arr
+1) ) { // descend?
309 return(item
->add(prefs
, arr
+1));
311 return(item
); // end? done
315 /// Insert a new item into current item's children at a specified position.
316 /// \returns the new item inserted.
318 Fl_Tree_Item
*Fl_Tree_Item::insert(const Fl_Tree_Prefs
&prefs
, const char *new_label
, int pos
) {
319 Fl_Tree_Item
*item
= new Fl_Tree_Item(prefs
);
320 item
->label(new_label
);
321 item
->_parent
= this;
322 _children
.insert(pos
, item
);
326 /// Insert a new item above this item.
327 /// \returns the new item inserted, or 0 if an error occurred.
329 Fl_Tree_Item
*Fl_Tree_Item::insert_above(const Fl_Tree_Prefs
&prefs
, const char *new_label
) {
330 Fl_Tree_Item
*p
= _parent
;
331 if ( ! p
) return(0);
332 // Walk our parent's children to find ourself
333 for ( int t
=0; t
<p
->children(); t
++ ) {
334 Fl_Tree_Item
*c
= p
->child(t
);
336 return(p
->insert(prefs
, new_label
, t
));
342 /// Remove child by item.
343 /// \returns 0 if removed, -1 if item not an immediate child.
345 int Fl_Tree_Item::remove_child(Fl_Tree_Item
*item
) {
346 for ( int t
=0; t
<children(); t
++ ) {
347 if ( child(t
) == item
) {
348 item
->clear_children();
356 /// Remove immediate child (and its children) by its label 'name'.
357 /// \returns 0 if removed, -1 if not found.
359 int Fl_Tree_Item::remove_child(const char *name
) {
360 for ( int t
=0; t
<children(); t
++ ) {
361 if ( child(t
)->label() ) {
362 if ( strcmp(child(t
)->label(), name
) == 0 ) {
371 /// Swap two of our children, given two child index values.
372 /// Use this eg. for sorting.
374 /// This method is FAST, and does not involve lookups.
376 /// No range checking is done on either index value.
380 /// - -1 : failed: 'a' or 'b' is not our immediate child
382 void Fl_Tree_Item::swap_children(int ax
, int bx
) {
383 _children
.swap(ax
, bx
);
386 /// Swap two of our children, given item pointers.
387 /// Use this eg. for sorting.
389 /// This method is SLOW because it involves linear lookups.
390 /// For speed, use swap_children(int,int) instead.
394 /// - -1 : failed: 'a' or 'b' is not our immediate child
396 int Fl_Tree_Item::swap_children(Fl_Tree_Item
*a
, Fl_Tree_Item
*b
) {
397 int ax
= -1, bx
= -1;
398 for ( int t
=0; t
<children(); t
++ ) { // find index for a and b
399 if ( _children
[t
] == a
) { ax
= t
; if ( bx
!= -1 ) break; else continue; }
400 if ( _children
[t
] == b
) { bx
= t
; if ( ax
!= -1 ) break; else continue; }
402 if ( ax
== -1 || bx
== -1 ) return(-1); // not found? fail
403 swap_children(ax
,bx
);
407 /// Internal: Horizontal connector line based on preference settings.
408 void Fl_Tree_Item::draw_horizontal_connector(int x1
, int x2
, int y
, const Fl_Tree_Prefs
&prefs
) {
409 fl_color(prefs
.connectorcolor());
410 switch ( prefs
.connectorstyle() ) {
411 case FL_TREE_CONNECTOR_SOLID
:
412 y
|= 1; // force alignment w/dot pattern
415 case FL_TREE_CONNECTOR_DOTTED
:
417 y
|= 1; // force alignment w/dot pattern
418 for ( int xx
=x1
; xx
<=x2
; xx
++ ) {
419 if ( !(xx
& 1) ) fl_point(xx
, y
);
423 case FL_TREE_CONNECTOR_NONE
:
428 /// Internal: Vertical connector line based on preference settings.
429 void Fl_Tree_Item::draw_vertical_connector(int x
, int y1
, int y2
, const Fl_Tree_Prefs
&prefs
) {
430 fl_color(prefs
.connectorcolor());
431 switch ( prefs
.connectorstyle() ) {
432 case FL_TREE_CONNECTOR_SOLID
:
433 y1
|= 1; // force alignment w/dot pattern
434 y2
|= 1; // force alignment w/dot pattern
437 case FL_TREE_CONNECTOR_DOTTED
:
439 y1
|= 1; // force alignment w/dot pattern
440 y2
|= 1; // force alignment w/dot pattern
441 for ( int yy
=y1
; yy
<=y2
; yy
++ ) {
442 if ( yy
& 1 ) fl_point(x
, yy
);
446 case FL_TREE_CONNECTOR_NONE
:
451 /// Find the item that the last event was over.
453 /// Returns the item if it is visible, and mouse is over it.
454 /// Works even if widget deactivated.
455 /// Use event_on_collapse_icon() to determine if collapse button was pressed.
457 /// \returns const visible item under the event if found, or 0 if none.
459 const Fl_Tree_Item
*Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs
&prefs
) const {
460 if ( ! _visible
) return(0);
461 if ( is_root() && !prefs
.showroot() ) {
462 // skip event check if we're root but root not being shown
464 // See if event is over us
465 if ( event_inside(_xywh
) ) { // event within this item?
466 return(this); // found
469 if ( is_open() ) { // open? check children of this item
470 for ( int t
=0; t
<children(); t
++ ) {
471 const Fl_Tree_Item
*item
;
472 if ( ( item
= _children
[t
]->find_clicked(prefs
) ) != NULL
) { // check child and its descendents
473 return(item
); // found?
480 /// Non-const version of the above.
481 /// Find the item that the last event was over.
483 /// Returns the item if it is visible, and mouse is over it.
484 /// Works even if widget deactivated.
485 /// Use event_on_collapse_icon() to determine if collapse button was pressed.
487 /// \returns the visible item under the event if found, or 0 if none.
489 Fl_Tree_Item
*Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs
&prefs
) {
490 if ( ! _visible
) return(0);
491 if ( is_root() && !prefs
.showroot() ) {
492 // skip event check if we're root but root not being shown
494 // See if event is over us
495 if ( event_inside(_xywh
) ) { // event within this item?
496 return(this); // found
499 if ( is_open() ) { // open? check children of this item
500 for ( int t
=0; t
<children(); t
++ ) {
502 if ( ( item
= _children
[t
]->find_clicked(prefs
) ) != NULL
) { // check child and its descendents
503 return(item
); // found?
510 static void draw_item_focus(Fl_Boxtype B
, Fl_Color C
, int X
, int Y
, int W
, int H
) {
511 if (!Fl::visible_focus()) return;
515 case FL_THIN_DOWN_BOX
:
516 case FL_THIN_DOWN_FRAME
:
522 fl_color(fl_contrast(FL_BLACK
, C
));
524 #if defined(USE_X11) || defined(__APPLE_QUARTZ__)
525 fl_line_style(FL_DOT
);
526 fl_rect(X
+ Fl::box_dx(B
), Y
+ Fl::box_dy(B
),
527 W
- Fl::box_dw(B
) - 1, H
- Fl::box_dh(B
) - 1);
528 fl_line_style(FL_SOLID
);
530 // Some platforms don't implement dotted line style, so draw
531 // every other pixel around the focus area...
533 // Also, QuickDraw (MacOS) does not support line styles specifically,
534 // and the hack we use in fl_line_style() will not draw horizontal lines
535 // on odd-numbered rows...
540 W
-= Fl::box_dw(B
) + 2;
541 H
-= Fl::box_dh(B
) + 2;
543 for (xx
= 0, i
= 1; xx
< W
; xx
++, i
++) if (i
& 1) fl_point(X
+ xx
, Y
);
544 for (yy
= 0; yy
< H
; yy
++, i
++) if (i
& 1) fl_point(X
+ W
, Y
+ yy
);
545 for (xx
= W
; xx
> 0; xx
--, i
++) if (i
& 1) fl_point(X
+ xx
, Y
+ H
);
546 for (yy
= H
; yy
> 0; yy
--, i
++) if (i
& 1) fl_point(X
, Y
+ yy
);
550 /// Draw this item and its children.
551 void Fl_Tree_Item::draw(int X
, int &Y
, int W
, Fl_Widget
*tree
,
552 Fl_Tree_Item
*itemfocus
,
553 const Fl_Tree_Prefs
&prefs
, int lastchild
) {
554 if ( ! _visible
) return;
555 fl_font(_labelfont
, _labelsize
);
557 if(usericon() && H
< usericon()->h()) H
= usericon()->h();
558 H
+= prefs
.linespacing() + fl_descent();
559 // adjust horizontally if we draw no connecting lines
560 if ( is_root() && prefs
.connectorstyle() == FL_TREE_CONNECTOR_NONE
) {
561 X
-= prefs
.openicon()->w();
562 W
+= prefs
.openicon()->w();
565 Fl_Color fg
= _selected
? fl_contrast(_labelfgcolor
, tree
->selection_color())
566 : _active
? _labelfgcolor
567 : fl_inactive(_labelfgcolor
);
568 Fl_Color bg
= _selected
? _active
? tree
->selection_color()
569 : fl_inactive(tree
->selection_color())
571 // Update the xywh of this item
577 int textw
=0, texth
=0;
578 fl_measure(_label
, textw
, texth
, 0);
579 int textycenter
= Y
+(H
/2);
580 int &icon_w
= _collapse_xywh
[2] = prefs
.openicon()->w();
581 int &icon_x
= _collapse_xywh
[0] = X
+ (icon_w
+ prefs
.connectorwidth())/2 - 3;
582 int &icon_y
= _collapse_xywh
[1] = textycenter
- (prefs
.openicon()->h()/2);
583 _collapse_xywh
[3] = prefs
.openicon()->h();
584 // Horizontal connector values
585 int hstartx
= X
+icon_w
/2-1;
586 int hendx
= hstartx
+ prefs
.connectorwidth();
587 int hcenterx
= X
+ icon_w
+ ((hendx
- (X
+ icon_w
)) / 2);
589 // See if we should draw this item
590 // If this item is root, and showroot() is disabled, don't draw.
592 char drawthis
= ( is_root() && prefs
.showroot() == 0 ) ? 0 : 1;
595 if ( prefs
.connectorstyle() != FL_TREE_CONNECTOR_NONE
) {
596 // Horiz connector between center of icon and text
597 // if this is root, the connector should not dangle in thin air on the left
599 draw_horizontal_connector(hcenterx
, hendx
, textycenter
, prefs
);
601 draw_horizontal_connector(hstartx
, hendx
, textycenter
, prefs
);
602 if ( has_children() && is_open() ) {
603 // Small vertical line down to children
604 draw_vertical_connector(hcenterx
, textycenter
, Y
+H
, prefs
);
606 // Connectors for last child
609 draw_vertical_connector(hstartx
, Y
, textycenter
, prefs
);
611 draw_vertical_connector(hstartx
, Y
, Y
+H
, prefs
);
615 // Draw collapse icon
616 if ( has_children() && prefs
.showcollapse() ) {
619 prefs
.closeicon()->draw(icon_x
,icon_y
);
621 prefs
.openicon()->draw(icon_x
,icon_y
);
624 // Background for this item
625 int cw1
= icon_w
+prefs
.connectorwidth()/2, cw2
= prefs
.connectorwidth();
626 int cwidth
= cw1
>cw2
? cw1
: cw2
;
627 int &bx
= _label_xywh
[0] = X
+(icon_w
/2-1+cwidth
);
628 int &by
= _label_xywh
[1] = Y
;
629 int &bw
= _label_xywh
[2] = W
-(icon_w
/2-1+cwidth
);
630 int &bh
= _label_xywh
[3] = H
;
631 // Draw bg only if different from tree's bg
632 if ( bg
!= tree
->color() || is_selected() ) {
633 if ( is_selected() ) {
634 // Selected? Use selectbox() style
635 fl_draw_box(prefs
.selectbox(), bx
, by
, bw
, bh
, bg
);
637 // Not Selected? use plain filled rectangle
639 fl_rectf(bx
, by
, bw
, bh
);
642 // Draw user icon (if any)
643 int useroff
= (icon_w
/2-1+cwidth
);
645 // Item has user icon? Use it
646 useroff
+= prefs
.usericonmarginleft();
647 icon_y
= textycenter
- (usericon()->h() >> 1);
648 usericon()->draw(X
+useroff
,icon_y
);
649 useroff
+= usericon()->w();
650 } else if ( prefs
.usericon() ) {
651 // Prefs has user icon? Use it
652 useroff
+= prefs
.usericonmarginleft();
653 icon_y
= textycenter
- (prefs
.usericon()->h() >> 1);
654 prefs
.usericon()->draw(X
+useroff
,icon_y
);
655 useroff
+= prefs
.usericon()->w();
657 useroff
+= prefs
.labelmarginleft();
663 int lw
= widget()->w();
665 if ( widget()->x() != lx
|| widget()->y() != ly
||
666 widget()->w() != lw
|| widget()->h() != lh
) {
667 widget()->resize(lx
, ly
, lw
, lh
); // fltk will handle drawing this
670 // No label widget? Draw text label
673 fl_draw(_label
, X
+useroff
, Y
+H
-fl_descent()-1);
676 if ( this == itemfocus
&& Fl::visible_focus() && Fl::focus() == tree
) {
677 // Draw focus box around this item
678 draw_item_focus(FL_NO_BOX
,bg
,bx
+1,by
+1,bw
-1,bh
-1);
683 if ( has_children() && is_open() ) {
684 int child_x
= drawthis
? // offset children to right,
685 (hcenterx
- (icon_w
/2) + 1) : X
; // unless didn't drawthis
686 int child_w
= W
- (child_x
-X
);
687 int child_y_start
= Y
;
688 for ( int t
=0; t
<children(); t
++ ) {
689 int lastchild
= ((t
+1)==children()) ? 1 : 0;
690 _children
[t
]->draw(child_x
, Y
, child_w
, tree
, itemfocus
, prefs
, lastchild
);
692 if ( has_children() && is_open() ) {
693 Y
+= prefs
.openchild_marginbottom(); // offset below open child tree
696 draw_vertical_connector(hstartx
, child_y_start
, Y
, prefs
);
701 /// Was the event on the 'collapse' button?
703 int Fl_Tree_Item::event_on_collapse_icon(const Fl_Tree_Prefs
&prefs
) const {
704 if ( _visible
&& _active
&& has_children() && prefs
.showcollapse() ) {
705 return(event_inside(_collapse_xywh
) ? 1 : 0);
711 /// Was event on the label()?
713 int Fl_Tree_Item::event_on_label(const Fl_Tree_Prefs
&prefs
) const {
714 if ( _visible
&& _active
) {
715 return(event_inside(_label_xywh
) ? 1 : 0);
721 /// Internal: Show the FLTK widget() for this item and all children.
722 /// Used by open() to re-show widgets that were hidden by a previous close()
724 void Fl_Tree_Item::show_widgets() {
725 if ( _widget
) _widget
->show();
727 for ( int t
=0; t
<_children
.total(); t
++ ) {
728 _children
[t
]->show_widgets();
733 /// Internal: Hide the FLTK widget() for this item and all children.
734 /// Used by close() to hide widgets.
736 void Fl_Tree_Item::hide_widgets() {
737 if ( _widget
) _widget
->hide();
738 for ( int t
=0; t
<_children
.total(); t
++ ) {
739 _children
[t
]->hide_widgets();
743 /// Open this item and all its children.
744 void Fl_Tree_Item::open() {
746 // Tell children to show() their widgets
747 for ( int t
=0; t
<_children
.total(); t
++ ) {
748 _children
[t
]->show_widgets();
752 /// Close this item and all its children.
753 void Fl_Tree_Item::close() {
755 // Tell children to hide() their widgets
756 for ( int t
=0; t
<_children
.total(); t
++ ) {
757 _children
[t
]->hide_widgets();
761 /// Returns how many levels deep this item is in the hierarchy.
763 /// For instance; root has a depth of zero, and its immediate children
764 /// would have a depth of 1, and so on.
766 int Fl_Tree_Item::depth() const {
768 const Fl_Tree_Item
*item
= parent();
771 item
= item
->parent();
776 /// Return the next item in the tree.
778 /// This method can be used to walk the tree forward.
779 /// For an example of how to use this method, see Fl_Tree::first().
781 /// \returns the next item in the tree, or 0 if there's no more items.
783 Fl_Tree_Item
*Fl_Tree_Item::next() {
784 Fl_Tree_Item
*p
, *c
= this;
785 if ( c
->has_children() ) {
788 while ( ( p
= c
->parent() ) != NULL
) { // loop upwards through parents
789 int t
= p
->find_child(c
); // find our position in parent's children[] array
790 if ( ++t
< p
->children() ) // not last child?
791 return(p
->child(t
)); // return next child
792 c
= p
; // child becomes parent to move up generation
793 } // loop: moves up to next parent
794 return(0); // hit root? done
797 /// Return the previous item in the tree.
799 /// This method can be used to walk the tree backwards.
800 /// For an example of how to use this method, see Fl_Tree::last().
802 /// \returns the previous item in the tree, or 0 if there's no item above this one (hit the root).
804 Fl_Tree_Item
*Fl_Tree_Item::prev() {
805 Fl_Tree_Item
*p
=parent(); // start with parent
806 if ( ! p
) return(0); // hit root? done
807 int t
= p
->find_child(this); // find our position in parent's children[] array
808 if ( --t
== -1 ) { // are we first child?
809 return(p
); // return immediate parent
811 p
= p
->child(t
); // take parent's previous child
812 while ( p
->has_children() ) { // has children?
813 p
= p
->child(p
->children()-1); // take last child
818 /// Return this item's next sibling.
820 /// Moves to the next item below us at the same level (sibling).
821 /// Use this to move down the tree without moving deeper into the tree,
822 /// effectively skipping over this item's children/descendents.
824 /// \returns item's next sibling, or 0 if none.
826 Fl_Tree_Item
*Fl_Tree_Item::next_sibling() {
827 if ( !parent() ) return(0); // No parent (root)? We have no siblings
828 int index
= parent()->find_child(this); // find our position in parent's child() array
829 if ( index
== -1 ) return(0); // parent doesn't know us? weird
830 if ( (index
+1) < parent()->children() ) // is there a next child?
831 return(parent()->child(index
+1)); // return next child if there's one below us
832 return(0); // no siblings below us
835 /// Return this item's previous sibling.
837 /// Moves to the previous item above us at the same level (sibling).
838 /// Use this to move up the tree without moving deeper into the tree.
840 /// \returns This item's previous sibling, or 0 if none.
842 Fl_Tree_Item
*Fl_Tree_Item::prev_sibling() {
843 if ( !parent() ) return(0); // No parent (root)? We have no siblings
844 int index
= parent()->find_child(this); // find next position up in parent's child() array
845 if ( index
== -1 ) return(0); // parent doesn't know us? weird
846 if ( index
> 0 ) return(parent()->child(index
-1)); // return previous child if there's one above us
847 return(0); // no siblings above us
850 /// Return the next visible item. (If this item has children and is closed, children are skipped)
852 /// This method can be used to walk the tree forward, skipping items
853 /// that are not currently visible to the user.
855 /// \returns the next visible item below us, or 0 if there's no more items.
857 Fl_Tree_Item
*Fl_Tree_Item::next_displayed(Fl_Tree_Prefs
&prefs
) {
858 Fl_Tree_Item
*c
= this;
860 if ( c
->is_root() && !prefs
.showroot() ) { // on root and can't show it?
861 c
= c
->next(); // skip ahead, try again
864 if ( c
->has_children() && c
->is_close() ) { // item has children and: invisible or closed?
865 // Skip children, take next sibling. If none, try parent's sibling, repeat
867 Fl_Tree_Item
*sib
= c
->next_sibling(); // get sibling
868 if ( sib
) { c
= sib
; break; } // Found? let outer loop test it
869 c
= c
->parent(); // No sibling? move up tree, try parent's sibling
871 } else { // has children and isn't closed, or no children
872 c
= c
->next(); // use normal 'next'
874 if ( !c
) return(0); // no more? done
875 // Check all parents to be sure none are closed.
876 // If closed, move up to that level and repeat until sure none are closed.
877 Fl_Tree_Item
*p
= c
->parent();
879 if ( !p
|| p
->is_root() ) return(c
); // hit top? then we're displayed, return c
880 if ( p
->is_close() ) c
= p
; // found closed parent? make it current
881 p
= p
->parent(); // continue up tree
883 if ( c
&& c
->visible() ) return(c
); // item visible? return it
885 return(0); // hit end: no more items
888 /// Return the previous visible item. (If this item above us has children and is closed, its children are skipped)
890 /// This method can be used to walk the tree backward,
891 /// skipping items that are not currently visible to the user.
893 /// \returns the previous visible item above us, or 0 if there's no more items.
895 Fl_Tree_Item
*Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs
&prefs
) {
896 Fl_Tree_Item
*c
= this;
898 c
= c
->prev(); // previous item
899 if ( !c
) break; // no more items? done
900 if ( c
->is_root() ) // root
901 return((prefs
.showroot()&&c
->visible()) ? c
: 0); // return root if visible
902 if ( !c
->visible() ) continue; // item not visible? skip
903 // Check all parents to be sure none are closed.
904 // If closed, move up to that level and repeat until sure none are closed.
905 Fl_Tree_Item
*p
= c
->parent();
907 if ( !p
|| p
->is_root() ) return(c
); // hit top? then we're displayed, return c
908 if ( p
->is_close() ) c
= p
; // found closed parent? make it current
909 p
= p
->parent(); // continue up tree
912 return(0); // hit end: no more items
915 /// Returns if item and all its parents are visible.
916 /// Also takes into consideration if any parent is close()ed.
918 /// 1 -- item and its parents are visible/open()
919 /// 0 -- item (or parents) invisible or close()ed.
921 int Fl_Tree_Item::visible_r() const {
922 for (const Fl_Tree_Item
*p
=this; p
; p
=p
->parent()) // move up through parents
923 if (!p
->visible() || p
->is_close()) return(0); // any parent not visible or closed?
928 // End of "$Id: Fl_Tree_Item.cxx 8589 2011-04-14 13:15:13Z manolo $".