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