1 //
2 // "$Id: Fl_Tree_Item.cxx 8589 2011-04-14 13:15:13Z manolo $"
3 //
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <FL/Fl_Widget.H>
9 #include <FL/Fl_Tree_Item.H>
10 #include <FL/Fl_Tree_Prefs.H>
11 
12 //////////////////////
13 // Fl_Tree_Item.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; 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.
23 //
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.
28 //
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
32 // USA.
33 //
34 
35 // Was the last event inside the specified xywh?
event_inside(const int xywh[4])36 static int event_inside(const int xywh[4]) {
37   return(Fl::event_inside(xywh[0],xywh[1],xywh[2],xywh[3]));
38 }
39 
40 /// Constructor.
41 ///     Makes a new instance of Fl_Tree_Item using defaults from 'prefs'.
42 ///
Fl_Tree_Item(const Fl_Tree_Prefs & prefs)43 Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) {
44   _label        = 0;
45   _labelfont    = prefs.labelfont();
46   _labelsize    = prefs.labelsize();
47   _labelfgcolor = prefs.labelfgcolor();
48   _labelbgcolor = prefs.labelbgcolor();
49   _widget       = 0;
50   _open         = 1;
51   _visible      = 1;
52   _active       = 1;
53   _selected     = 0;
54   _xywh[0]      = 0;
55   _xywh[1]      = 0;
56   _xywh[2]      = 0;
57   _xywh[3]      = 0;
58   _collapse_xywh[0] = 0;
59   _collapse_xywh[1] = 0;
60   _collapse_xywh[2] = 0;
61   _collapse_xywh[3] = 0;
62   _label_xywh[0]    = 0;
63   _label_xywh[1]    = 0;
64   _label_xywh[2]    = 0;
65   _label_xywh[3]    = 0;
66   _usericon         = 0;
67   _userdata         = 0;
68   _parent           = 0;
69 }
70 
71 // DTOR
~Fl_Tree_Item()72 Fl_Tree_Item::~Fl_Tree_Item() {
73   if ( _label ) {
74     free((void*)_label);
75     _label = 0;
76   }
77   _widget = 0;			// Fl_Group will handle destruction
78   _usericon = 0;		// user handled allocation
79   //_children.clear();		// array's destructor handles itself
80 }
81 
82 /// Copy constructor.
Fl_Tree_Item(const Fl_Tree_Item * o)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();
90   _open         = o->_open;
91   _visible      = o->_visible;
92   _active       = o->_active;
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;
109 }
110 
111 /// Print the tree as 'ascii art' to stdout.
112 /// Used mainly for debugging.
113 ///
show_self(const char * indent) const114 void Fl_Tree_Item::show_self(const char *indent) const {
115   if ( label() ) {
116     printf("%s-%s (%d children, this=%p, parent=%p depth=%d)\n",
117            indent,label(),children(),(void*)this, (void*)_parent, depth());
118   }
119   if ( children() ) {
120     char *i2 = (char*)malloc(strlen(indent) + 2);
121     strcpy(i2, indent);
122     strcat(i2, " |");
123     for ( int t=0; t<children(); t++ ) {
124       child(t)->show_self(i2);
125     }
126   }
127   fflush(stdout);
128 }
129 
130 /// Set the label. Makes a copy of the name.
label(const char * name)131 void Fl_Tree_Item::label(const char *name) {
132   if ( _label ) { free((void*)_label); _label = 0; }
133   _label = name ? strdup(name) : 0;
134 }
135 
136 /// Return the label.
label() const137 const char *Fl_Tree_Item::label() const {
138   return(_label);
139 }
140 
141 /// Return child item for the specified 'index'.
child(int index) const142 const Fl_Tree_Item *Fl_Tree_Item::child(int index) const {
143   return(_children[index]);
144 }
145 
146 /// Clear all the children for this item.
clear_children()147 void Fl_Tree_Item::clear_children() {
148   _children.clear();
149 }
150 
151 /// Return the index of the immediate child of this item that has the label 'name'.
152 ///
153 /// \returns index of found item, or -1 if not found.
154 ///
find_child(const char * name)155 int Fl_Tree_Item::find_child(const char *name) {
156   if ( name ) {
157     for ( int t=0; t<children(); t++ ) {
158       if ( child(t)->label() ) {
159         if ( strcmp(child(t)->label(), name) == 0 ) {
160           return(t);
161         }
162       }
163     }
164   }
165   return(-1);
166 }
167 
168 /// Find child item by descending array of names. Does not include self in search.
169 /// Only Fl_Tree should need this method.
170 ///
171 /// \returns item, or 0 if not found
172 ///
find_child_item(char ** arr) const173 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]);
181         }
182       }
183     }
184   }
185   return(0);
186 }
187 
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.
190 ///
191 /// \returns item, or 0 if not found
192 ///
find_child_item(char ** arr)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]);
201         }
202       }
203     }
204   }
205   return(0);
206 }
207 
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.
210 ///
211 /// \returns item, or 0 if not found
212 ///
find_item(char ** names) const213 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.
217     }
218   }
219   if ( children() ) {					// check children..
220     return(find_child_item(names));
221   }
222   return(0);
223 }
224 
225 /// Find item by descending array of \p names. Includes self in search.
226 /// Only Fl_Tree should need this method.
227 ///
228 /// \returns item, or 0 if not found
229 ///
find_item(char ** names)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.
234     }
235   }
236   if ( children() ) {					// check children..
237     return(find_child_item(names));
238   }
239   return(0);
240 }
241 
242 /// Find the index number for the specified 'item'
243 /// in the current item's list of children.
244 ///
245 /// \returns the index, or -1 if not found.
246 ///
find_child(Fl_Tree_Item * item)247 int Fl_Tree_Item::find_child(Fl_Tree_Item *item) {
248   for ( int t=0; t<children(); t++ ) {
249     if ( item == child(t) ) {
250       return(t);
251     }
252   }
253   return(-1);
254 }
255 
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().
259 ///
add(const Fl_Tree_Prefs & prefs,const char * new_label)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: {
266       _children.add(item);
267       return(item);
268     }
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);
274           return(item);
275         }
276       }
277       _children.add(item);
278       return(item);
279     }
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);
285           return(item);
286         }
287       }
288       _children.add(item);
289       return(item);
290     }
291   }
292   return(item);
293 }
294 
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.
299 ///
add(const Fl_Tree_Prefs & prefs,char ** arr)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;
303   if ( t == -1 ) {
304     item = (Fl_Tree_Item*)add(prefs, *arr);
305   } else {
306     item = (Fl_Tree_Item*)child(t);
307   }
308   if ( *(arr+1) ) {		// descend?
309     return(item->add(prefs, arr+1));
310   } else {
311     return(item);		// end? done
312   }
313 }
314 
315 /// Insert a new item into current item's children at a specified position.
316 /// \returns the new item inserted.
317 ///
insert(const Fl_Tree_Prefs & prefs,const char * new_label,int pos)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);
323   return(item);
324 }
325 
326 /// Insert a new item above this item.
327 /// \returns the new item inserted, or 0 if an error occurred.
328 ///
insert_above(const Fl_Tree_Prefs & prefs,const char * new_label)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);
335     if ( this == c ) {
336       return(p->insert(prefs, new_label, t));
337     }
338   }
339   return(0);
340 }
341 
342 /// Remove child by item.
343 ///    \returns 0 if removed, -1 if item not an immediate child.
344 ///
remove_child(Fl_Tree_Item * item)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();
349       _children.remove(t);
350       return(0);
351     }
352   }
353   return(-1);
354 }
355 
356 /// Remove immediate child (and its children) by its label 'name'.
357 /// \returns 0 if removed, -1 if not found.
358 ///
remove_child(const char * name)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 ) {
363         _children.remove(t);
364         return(0);
365       }
366     }
367   }
368   return(-1);
369 }
370 
371 /// Swap two of our children, given two child index values.
372 /// Use this eg. for sorting.
373 ///
374 /// This method is FAST, and does not involve lookups.
375 ///
376 /// No range checking is done on either index value.
377 ///
378 /// \returns
379 ///    -    0 : OK
380 ///    -   -1 : failed: 'a' or 'b' is not our immediate child
381 ///
swap_children(int ax,int bx)382 void Fl_Tree_Item::swap_children(int ax, int bx) {
383   _children.swap(ax, bx);
384 }
385 
386 /// Swap two of our children, given item pointers.
387 /// Use this eg. for sorting.
388 ///
389 /// This method is SLOW because it involves linear lookups.
390 /// For speed, use swap_children(int,int) instead.
391 ///
392 /// \returns
393 ///    -    0 : OK
394 ///    -   -1 : failed: 'a' or 'b' is not our immediate child
395 ///
swap_children(Fl_Tree_Item * a,Fl_Tree_Item * b)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; }
401   }
402   if ( ax == -1 || bx == -1 ) return(-1);	// not found? fail
403   swap_children(ax,bx);
404   return(0);
405 }
406 
407 /// Internal: Horizontal connector line based on preference settings.
draw_horizontal_connector(int x1,int x2,int y,const Fl_Tree_Prefs & prefs)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
413       fl_line(x1,y,x2,y);
414       return;
415     case FL_TREE_CONNECTOR_DOTTED:
416         {
417             y |= 1;				// force alignment w/dot pattern
418             for ( int xx=x1; xx<=x2; xx++ ) {
419                 if ( !(xx & 1) ) fl_point(xx, y);
420             }
421         }
422       return;
423     case FL_TREE_CONNECTOR_NONE:
424       return;
425   }
426 }
427 
428 /// Internal: Vertical connector line based on preference settings.
draw_vertical_connector(int x,int y1,int y2,const Fl_Tree_Prefs & prefs)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
435       fl_line(x,y1,x,y2);
436       return;
437     case FL_TREE_CONNECTOR_DOTTED:
438         {
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);
443             }
444         }
445         return;
446     case FL_TREE_CONNECTOR_NONE:
447       return;
448   }
449 }
450 
451 /// Find the item that the last event was over.
452 ///
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.
456 ///
457 ///    \returns const visible item under the event if found, or 0 if none.
458 ///
find_clicked(const Fl_Tree_Prefs & prefs) const459 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
463   } else {
464     // See if event is over us
465     if ( event_inside(_xywh) ) {		// event within this item?
466       return(this);				// found
467     }
468   }
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?
474       }
475     }
476   }
477   return(0);
478 }
479 
480 /// Non-const version of the above.
481 /// Find the item that the last event was over.
482 ///
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.
486 ///
487 ///    \returns the visible item under the event if found, or 0 if none.
488 ///
find_clicked(const Fl_Tree_Prefs & prefs)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
493   } else {
494     // See if event is over us
495     if ( event_inside(_xywh) ) {		// event within this item?
496       return(this);				// found
497     }
498   }
499   if ( is_open() ) {				// open? check children of this item
500     for ( int t=0; t<children(); t++ ) {
501       Fl_Tree_Item *item;
502       if ( ( item = _children[t]->find_clicked(prefs) ) != NULL ) {	// check child and its descendents
503         return(item);							// found?
504       }
505     }
506   }
507   return(0);
508 }
509 
draw_item_focus(Fl_Boxtype B,Fl_Color C,int X,int Y,int W,int H)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;
512   switch (B) {
513     case FL_DOWN_BOX:
514     case FL_DOWN_FRAME:
515     case FL_THIN_DOWN_BOX:
516     case FL_THIN_DOWN_FRAME:
517       X ++;
518       Y ++;
519     default:
520       break;
521   }
522   fl_color(fl_contrast(FL_BLACK, C));
523 
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);
529 #else
530   // Some platforms don't implement dotted line style, so draw
531   // every other pixel around the focus area...
532   //
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...
536   int i, xx, yy;
537 
538   X += Fl::box_dx(B);
539   Y += Fl::box_dy(B);
540   W -= Fl::box_dw(B) + 2;
541   H -= Fl::box_dh(B) + 2;
542 
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);
547 #endif
548 }
549 
550 /// Draw this item and its children.
draw(int X,int & Y,int W,Fl_Widget * tree,Fl_Tree_Item * itemfocus,const Fl_Tree_Prefs & prefs,int lastchild)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);
556   int H = _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();
563   }
564   // Colors, fonts
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())
570                           : _labelbgcolor;
571   // Update the xywh of this item
572   _xywh[0] = X;
573   _xywh[1] = Y;
574   _xywh[2] = W;
575   _xywh[3] = H;
576   // Text size
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);
588 
589   // See if we should draw this item
590   //    If this item is root, and showroot() is disabled, don't draw.
591   //
592   char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1;
593   if ( drawthis ) {
594     // Draw connectors
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
598       if (is_root())
599         draw_horizontal_connector(hcenterx, hendx, textycenter, prefs);
600       else
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);
605       }
606       // Connectors for last child
607       if ( ! is_root() ) {
608         if ( lastchild ) {
609           draw_vertical_connector(hstartx, Y, textycenter, prefs);
610         } else {
611           draw_vertical_connector(hstartx, Y, Y+H, prefs);
612         }
613       }
614     }
615     // Draw collapse icon
616     if ( has_children() && prefs.showcollapse() ) {
617       // Draw icon image
618       if ( is_open() ) {
619         prefs.closeicon()->draw(icon_x,icon_y);
620       } else {
621         prefs.openicon()->draw(icon_x,icon_y);
622       }
623     }
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);
636       } else {
637         // Not Selected? use plain filled rectangle
638         fl_color(bg);
639         fl_rectf(bx, by, bw, bh);
640       }
641     }
642     // Draw user icon (if any)
643     int useroff = (icon_w/2-1+cwidth);
644     if ( usericon() ) {
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();
656     }
657     useroff += prefs.labelmarginleft();
658     // Draw label
659     if ( widget() ) {
660       // Widget? Draw it
661       int lx = X+useroff;
662       int ly = by;
663       int lw = widget()->w();
664       int lh = bh;
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
668       }
669     } else {
670       // No label widget? Draw text label
671       if ( _label ) {
672         fl_color(fg);
673         fl_draw(_label, X+useroff, Y+H-fl_descent()-1);
674       }
675     }
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);
679     }
680     Y += H;
681   }			// end drawthis
682   // Draw children
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);
691     }
692     if ( has_children() && is_open() ) {
693       Y += prefs.openchild_marginbottom();		// offset below open child tree
694     }
695     if ( ! lastchild ) {
696       draw_vertical_connector(hstartx, child_y_start, Y, prefs);
697     }
698   }
699 }
700 
701 /// Was the event on the 'collapse' button?
702 ///
event_on_collapse_icon(const Fl_Tree_Prefs & prefs) const703 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);
706   } else {
707     return(0);
708   }
709 }
710 
711 /// Was event on the label()?
712 ///
event_on_label(const Fl_Tree_Prefs & prefs) const713 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);
716   } else {
717     return(0);
718   }
719 }
720 
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()
723 ///
show_widgets()724 void Fl_Tree_Item::show_widgets() {
725   if ( _widget ) _widget->show();
726   if ( is_open() ) {
727     for ( int t=0; t<_children.total(); t++ ) {
728       _children[t]->show_widgets();
729     }
730   }
731 }
732 
733 /// Internal: Hide the FLTK widget() for this item and all children.
734 /// Used by close() to hide widgets.
735 ///
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();
740   }
741 }
742 
743 /// Open this item and all its children.
open()744 void Fl_Tree_Item::open() {
745   _open = 1;
746   // Tell children to show() their widgets
747   for ( int t=0; t<_children.total(); t++ ) {
748     _children[t]->show_widgets();
749   }
750 }
751 
752 /// Close this item and all its children.
close()753 void Fl_Tree_Item::close() {
754   _open = 0;
755   // Tell children to hide() their widgets
756   for ( int t=0; t<_children.total(); t++ ) {
757     _children[t]->hide_widgets();
758   }
759 }
760 
761 /// Returns how many levels deep this item is in the hierarchy.
762 ///
763 /// For instance; root has a depth of zero, and its immediate children
764 /// would have a depth of 1, and so on.
765 ///
depth() const766 int Fl_Tree_Item::depth() const {
767   int count = 0;
768   const Fl_Tree_Item *item = parent();
769   while ( item ) {
770     ++count;
771     item = item->parent();
772   }
773   return(count);
774 }
775 
776 /// Return the next item in the tree.
777 ///
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().
780 ///
781 /// \returns the next item in the tree, or 0 if there's no more items.
782 ///
next()783 Fl_Tree_Item *Fl_Tree_Item::next() {
784   Fl_Tree_Item *p, *c = this;
785   if ( c->has_children() ) {
786     return(c->child(0));
787   }
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
795 }
796 
797 /// Return the previous item in the tree.
798 ///
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().
801 ///
802 /// \returns the previous item in the tree, or 0 if there's no item above this one (hit the root).
803 ///
prev()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
810   }
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
814   }
815   return(p);
816 }
817 
818 /// Return this item's next sibling.
819 ///
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.
823 ///
824 /// \returns item's next sibling, or 0 if none.
825 ///
next_sibling()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
833 }
834 
835 /// Return this item's previous sibling.
836 ///
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.
839 ///
840 /// \returns This item's previous sibling, or 0 if none.
841 ///
prev_sibling()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
848 }
849 
850 /// Return the next visible item. (If this item has children and is closed, children are skipped)
851 ///
852 /// This method can be used to walk the tree forward, skipping items
853 /// that are not currently visible to the user.
854 ///
855 /// \returns the next visible item below us, or 0 if there's no more items.
856 ///
next_displayed(Fl_Tree_Prefs & prefs)857 Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) {
858   Fl_Tree_Item *c = this;
859   while ( c ) {
860     if ( c->is_root() && !prefs.showroot() ) {		// on root and can't show it?
861       c = c->next();					// skip ahead, try again
862       continue;
863     }
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
866       while ( c ) {
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
870       }
871     } else {						// has children and isn't closed, or no children
872       c = c->next();					// use normal 'next'
873     }
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();
878     while (1) {
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
882     }
883     if ( c && c->visible() ) return(c);			// item visible? return it
884   }
885   return(0);						// hit end: no more items
886 }
887 
888 /// Return the previous visible item. (If this item above us has children and is closed, its children are skipped)
889 ///
890 /// This method can be used to walk the tree backward,
891 /// skipping items that are not currently visible to the user.
892 ///
893 /// \returns the previous visible item above us, or 0 if there's no more items.
894 ///
prev_displayed(Fl_Tree_Prefs & prefs)895 Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) {
896   Fl_Tree_Item *c = this;
897   while ( c ) {
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();
906     while (1) {
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
910     }
911   }
912   return(0);						// hit end: no more items
913 }
914 
915 /// Returns if item and all its parents are visible.
916 /// Also takes into consideration if any parent is close()ed.
917 /// \returns
918 ///    1 -- item and its parents are visible/open()
919 ///    0 -- item (or parents) invisible or close()ed.
920 ///
visible_r() const921 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?
924   return(1);
925 }
926 
927 //
928 // End of "$Id: Fl_Tree_Item.cxx 8589 2011-04-14 13:15:13Z manolo $".
929 //
930