../fltk-1.3.x-svn-current/src/Fl_Tree_Item.cxx | src/Fl_Tree_Item.cxx | |||
---|---|---|---|---|
1093 lines 36072 bytes Last modified : Thu Dec 12 14:27:14 2013 |
1371 lines 47005 bytes Last modified : Thu Dec 12 06:31:46 2013 |
|||
1 | // | 1 | // | |
2 | // "$Id: Fl_Tree_Item.cxx 10018 2013-11-10 04:33:44Z greg.ercolano $" | 2 | // "$Id: Fl_Tree_Item.cxx 10018 2013-11-10 04:33:44Z greg.ercolano $" | |
3 | // | 3 | // | |
4 | 4 | |||
5 | #include <stdio.h> | 5 | #include <stdio.h> | |
6 | #include <stdlib.h> | 6 | #include <stdlib.h> | |
7 | #include <string.h> | 7 | #include <string.h> | |
8 | #include <FL/Fl_Widget.H> | 8 | #include <FL/Fl_Widget.H> | |
9 | #include <FL/Fl_Tree_Item.H> | 9 | #include <FL/Fl_Tree_Item.H> | |
10 | #include <FL/Fl_Tree_Prefs.H> | 10 | #include <FL/Fl_Tree_Prefs.H> | |
11 | #include <FL/Fl_Tree.H> | 11 | #include <FL/Fl_Tree.H> | |
12 | 12 | |||
13 | ////////////////////// | 13 | ////////////////////// | |
14 | // Fl_Tree_Item.cxx | 14 | // Fl_Tree_Item.cxx | |
15 | ////////////////////// | 15 | ////////////////////// | |
16 | // | 16 | // | |
17 | // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK | 17 | // Fl_Tree -- This file is part of the Fl_Tree widget for FLTK | |
18 | // Copyright (C) 2009-2010 by Greg Ercolano. | 18 | // Copyright (C) 2009-2010 by Greg Ercolano. | |
19 | // | 19 | // | |
20 | // This library is free software. Distribution and use rights are outlined in | 20 | // This library is free software. Distribution and use rights are outlined in | |
21 | // the file "COPYING" which should have been included with this file. If this | 21 | // the file "COPYING" which should have been included with this file. If this | |
22 | // file is missing or damaged, see the license at: | 22 | // file is missing or damaged, see the license at: | |
23 | // | 23 | // | |
24 | // http://www.fltk.org/COPYING.php | 24 | // http://www.fltk.org/COPYING.php | |
25 | // | 25 | // | |
26 | // Please report all bugs and problems on the following page: | 26 | // Please report all bugs and problems on the following page: | |
27 | // | 27 | // | |
28 | // http://www.fltk.org/str.php | 28 | // http://www.fltk.org/str.php | |
29 | // | 29 | // | |
30 | /////////////////////////////////////////////////////////////////////////// 80 / | 30 | /////////////////////////////////////////////////////////////////////////// 80 / | |
31 | 31 | |||
32 | // Was the last event inside the specified xywh? | 32 | // Was the last event inside the specified xywh? | |
33 | static int event_inside(const int xywh[4]) { | 33 | static int event_inside(const int xywh[4]) { | |
34 | return(Fl::event_inside(xywh[0],xywh[1],xywh[2],xywh[3])); | 34 | return(Fl::event_inside(xywh[0],xywh[1],xywh[2],xywh[3])); | |
35 | } | 35 | } | |
36 | 36 | |||
37 | /// Constructor. | 37 | /// Constructor. | |
38 | /// Makes a new instance of Fl_Tree_Item using defaults from 'prefs'. | 38 | /// Makes a new instance of Fl_Tree_Item using defaults from 'prefs'. | |
39 | /// \note Deprecated in 1.3.3 ABI -- use Fl_Tree_Item(Fl_Tree*) instead. | |||
39 | /// | 40 | /// | |
40 | Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) { | 41 | Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Prefs &prefs) { | |
42 | _Init(prefs, 0); | |||
43 | } | |||
44 | ||||
45 | // Initialize the tree item | |||
46 | // Used by constructors | |||
47 | // | |||
48 | void Fl_Tree_Item::_Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree) { | |||
49 | #if FLTK_ABI_VERSION >= 10303 | |||
50 | _tree = tree; | |||
51 | #endif | |||
41 | _label = 0; | 52 | _label = 0; | |
42 | _labelfont = prefs.labelfont(); | 53 | _labelfont = prefs.labelfont(); | |
43 | _labelsize = prefs.labelsize(); | 54 | _labelsize = prefs.labelsize(); | |
44 | _labelfgcolor = prefs.labelfgcolor(); | 55 | _labelfgcolor = prefs.labelfgcolor(); | |
45 | _labelbgcolor = prefs.labelbgcolor(); | 56 | _labelbgcolor = prefs.labelbgcolor(); | |
46 | _widget = 0; | 57 | _widget = 0; | |
47 | #if FLTK_ABI_VERSION >= 10301 | 58 | #if FLTK_ABI_VERSION >= 10301 | |
48 | _flags = OPEN|VISIBLE|ACTIVE; | 59 | _flags = OPEN|VISIBLE|ACTIVE; | |
49 | #else /*FLTK_ABI_VERSION*/ | 60 | #else /*FLTK_ABI_VERSION*/ | |
50 | _open = 1; | 61 | _open = 1; | |
51 | _visible = 1; | 62 | _visible = 1; | |
52 | _active = 1; | 63 | _active = 1; | |
53 | _selected = 0; | 64 | _selected = 0; | |
54 | #endif /*FLTK_ABI_VERSION*/ | 65 | #endif /*FLTK_ABI_VERSION*/ | |
55 | _xywh[0] = 0; | 66 | _xywh[0] = 0; | |
56 | _xywh[1] = 0; | 67 | _xywh[1] = 0; | |
57 | _xywh[2] = 0; | 68 | _xywh[2] = 0; | |
58 | _xywh[3] = 0; | 69 | _xywh[3] = 0; | |
59 | _collapse_xywh[0] = 0; | 70 | _collapse_xywh[0] = 0; | |
60 | _collapse_xywh[1] = 0; | 71 | _collapse_xywh[1] = 0; | |
61 | _collapse_xywh[2] = 0; | 72 | _collapse_xywh[2] = 0; | |
62 | _collapse_xywh[3] = 0; | 73 | _collapse_xywh[3] = 0; | |
63 | _label_xywh[0] = 0; | 74 | _label_xywh[0] = 0; | |
64 | _label_xywh[1] = 0; | 75 | _label_xywh[1] = 0; | |
65 | _label_xywh[2] = 0; | 76 | _label_xywh[2] = 0; | |
66 | _label_xywh[3] = 0; | 77 | _label_xywh[3] = 0; | |
67 | _usericon = 0; | 78 | _usericon = 0; | |
68 | _userdata = 0; | 79 | _userdata = 0; | |
69 | _parent = 0; | 80 | _parent = 0; | |
70 | #if FLTK_ABI_VERSION >= 10303 | 81 | #if FLTK_ABI_VERSION >= 10303 | |
71 | _children.manage_item_destroy(1); // let array's dtor manage destroying Fl_Tree_Items | 82 | _children.manage_item_destroy(1); // let array's dtor manage destroying Fl_Tree_Items | |
72 | #endif | 83 | #endif | |
73 | #if FLTK_ABI_VERSION >= 10301 | 84 | #if FLTK_ABI_VERSION >= 10301 | |
74 | _prev_sibling = 0; | 85 | _prev_sibling = 0; | |
75 | _next_sibling = 0; | 86 | _next_sibling = 0; | |
76 | #endif /*FLTK_ABI_VERSION*/ | 87 | #endif /*FLTK_ABI_VERSION*/ | |
77 | } | 88 | } | |
78 | 89 | |||
90 | #if FLTK_ABI_VERSION >= 10303 | |||
91 | /// Constructor. | |||
92 | /// Makes a new instance of Fl_Tree_Item for 'tree'. | |||
93 | /// | |||
94 | Fl_Tree_Item::Fl_Tree_Item(Fl_Tree *tree) { | |||
95 | _Init(tree->_prefs, tree); | |||
96 | } | |||
97 | #endif | |||
98 | ||||
79 | // DTOR | 99 | // DTOR | |
80 | Fl_Tree_Item::~Fl_Tree_Item() { | 100 | Fl_Tree_Item::~Fl_Tree_Item() { | |
81 | if ( _label ) { | 101 | if ( _label ) { | |
82 | free((void*)_label); | 102 | free((void*)_label); | |
83 | _label = 0; | 103 | _label = 0; | |
84 | } | 104 | } | |
85 | _widget = 0; // Fl_Group will handle destruction | 105 | _widget = 0; // Fl_Group will handle destruction | |
86 | _usericon = 0; // user handled allocation | 106 | _usericon = 0; // user handled allocation | |
87 | //_children.clear(); // array's destructor handles itself | 107 | //_children.clear(); // array's destructor handles itself | |
88 | } | 108 | } | |
89 | 109 | |||
90 | /// Copy constructor. | 110 | /// Copy constructor. | |
91 | Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Item *o) { | 111 | Fl_Tree_Item::Fl_Tree_Item(const Fl_Tree_Item *o) { | |
112 | #if FLTK_ABI_VERSION >= 10303 | |||
113 | _tree = o->_tree; | |||
114 | #endif | |||
92 | _label = o->label() ? strdup(o->label()) : 0; | 115 | _label = o->label() ? strdup(o->label()) : 0; | |
93 | _labelfont = o->labelfont(); | 116 | _labelfont = o->labelfont(); | |
94 | _labelsize = o->labelsize(); | 117 | _labelsize = o->labelsize(); | |
95 | _labelfgcolor = o->labelfgcolor(); | 118 | _labelfgcolor = o->labelfgcolor(); | |
96 | _labelbgcolor = o->labelbgcolor(); | 119 | _labelbgcolor = o->labelbgcolor(); | |
97 | _widget = o->widget(); | 120 | _widget = o->widget(); | |
98 | #if FLTK_ABI_VERSION >= 10301 | 121 | #if FLTK_ABI_VERSION >= 10301 | |
99 | _flags = o->_flags; | 122 | _flags = o->_flags; | |
100 | #else /*FLTK_ABI_VERSION*/ | 123 | #else /*FLTK_ABI_VERSION*/ | |
101 | _open = o->_open; | 124 | _open = o->_open; | |
102 | _visible = o->_visible; | 125 | _visible = o->_visible; | |
103 | _active = o->_active; | 126 | _active = o->_active; | |
104 | _selected = o->_selected; | 127 | _selected = o->_selected; | |
105 | #endif /*FLTK_ABI_VERSION*/ | 128 | #endif /*FLTK_ABI_VERSION*/ | |
106 | _xywh[0] = o->_xywh[0]; | 129 | _xywh[0] = o->_xywh[0]; | |
107 | _xywh[1] = o->_xywh[1]; | 130 | _xywh[1] = o->_xywh[1]; | |
108 | _xywh[2] = o->_xywh[2]; | 131 | _xywh[2] = o->_xywh[2]; | |
109 | _xywh[3] = o->_xywh[3]; | 132 | _xywh[3] = o->_xywh[3]; | |
110 | _collapse_xywh[0] = o->_collapse_xywh[0]; | 133 | _collapse_xywh[0] = o->_collapse_xywh[0]; | |
111 | _collapse_xywh[1] = o->_collapse_xywh[1]; | 134 | _collapse_xywh[1] = o->_collapse_xywh[1]; | |
112 | _collapse_xywh[2] = o->_collapse_xywh[2]; | 135 | _collapse_xywh[2] = o->_collapse_xywh[2]; | |
113 | _collapse_xywh[3] = o->_collapse_xywh[3]; | 136 | _collapse_xywh[3] = o->_collapse_xywh[3]; | |
114 | _label_xywh[0] = o->_label_xywh[0]; | 137 | _label_xywh[0] = o->_label_xywh[0]; | |
115 | _label_xywh[1] = o->_label_xywh[1]; | 138 | _label_xywh[1] = o->_label_xywh[1]; | |
116 | _label_xywh[2] = o->_label_xywh[2]; | 139 | _label_xywh[2] = o->_label_xywh[2]; | |
117 | _label_xywh[3] = o->_label_xywh[3]; | 140 | _label_xywh[3] = o->_label_xywh[3]; | |
118 | _usericon = o->usericon(); | 141 | _usericon = o->usericon(); | |
119 | _userdata = o->user_data(); | 142 | _userdata = o->user_data(); | |
120 | _parent = o->_parent; | 143 | _parent = o->_parent; | |
121 | #if FLTK_ABI_VERSION >= 10301 | 144 | #if FLTK_ABI_VERSION >= 10301 | |
122 | _prev_sibling = 0; // do not copy ptrs! use update_prev_next() | 145 | _prev_sibling = 0; // do not copy ptrs! use update_prev_next() | |
123 | _next_sibling = 0; // do not copy ptrs! use update_prev_next() | 146 | _next_sibling = 0; // do not copy ptrs! use update_prev_next() | |
124 | #endif /*FLTK_ABI_VERSION*/ | 147 | #endif /*FLTK_ABI_VERSION*/ | |
125 | } | 148 | } | |
126 | 149 | |||
127 | /// Print the tree as 'ascii art' to stdout. | 150 | /// Print the tree as 'ascii art' to stdout. | |
128 | /// Used mainly for debugging. | 151 | /// Used mainly for debugging. | |
129 | /// | 152 | /// | |
130 | void Fl_Tree_Item::show_self(const char *indent) const { | 153 | void Fl_Tree_Item::show_self(const char *indent) const { | |
131 | if ( label() ) { | 154 | if ( label() ) { | |
132 | #if FLTK_ABI_VERSION >= 10301 | 155 | #if FLTK_ABI_VERSION >= 10301 | |
133 | printf("%s-%s (%d children, this=%p, parent=%p, prev=%p, next=%p, depth=%d)\n", | 156 | printf("%s-%s (%d children, this=%p, parent=%p, prev=%p, next=%p, depth=%d)\n", | |
134 | indent,label(),children(),(void*)this, (void*)_parent, | 157 | indent,label(),children(),(void*)this, (void*)_parent, | |
135 | _prev_sibling, _next_sibling, depth()); | 158 | _prev_sibling, _next_sibling, depth()); | |
136 | #else /*FLTK_ABI_VERSION*/ | 159 | #else /*FLTK_ABI_VERSION*/ | |
137 | printf("%s-%s (%d children, this=%p, parent=%p depth=%d)\n", | 160 | printf("%s-%s (%d children, this=%p, parent=%p depth=%d)\n", | |
138 | indent,label(),children(),(void*)this, (void*)_parent, depth()); | 161 | indent,label(),children(),(void*)this, (void*)_parent, depth()); | |
139 | #endif /*FLTK_ABI_VERSION*/ | 162 | #endif /*FLTK_ABI_VERSION*/ | |
140 | } | 163 | } | |
141 | if ( children() ) { | 164 | if ( children() ) { | |
142 | char *i2 = (char*)malloc(strlen(indent) + 2); | 165 | char *i2 = (char*)malloc(strlen(indent) + 2); | |
143 | strcpy(i2, indent); | 166 | strcpy(i2, indent); | |
144 | strcat(i2, " |"); | 167 | strcat(i2, " |"); | |
145 | for ( int t=0; t<children(); t++ ) { | 168 | for ( int t=0; t<children(); t++ ) { | |
146 | child(t)->show_self(i2); | 169 | child(t)->show_self(i2); | |
147 | } | 170 | } | |
148 | } | 171 | } | |
149 | fflush(stdout); | 172 | fflush(stdout); | |
150 | } | 173 | } | |
151 | 174 | |||
152 | /// Set the label. Makes a copy of the name. | 175 | /// Set the label. Makes a copy of the name. | |
153 | void Fl_Tree_Item::label(const char *name) { | 176 | void Fl_Tree_Item::label(const char *name) { | |
154 | if ( _label ) { free((void*)_label); _label = 0; } | 177 | if ( _label ) { free((void*)_label); _label = 0; } | |
155 | _label = name ? strdup(name) : 0; | 178 | _label = name ? strdup(name) : 0; | |
179 | recalc_tree(); // may change label geometry | |||
156 | } | 180 | } | |
157 | 181 | |||
158 | /// Return the label. | 182 | /// Return the label. | |
159 | const char *Fl_Tree_Item::label() const { | 183 | const char *Fl_Tree_Item::label() const { | |
160 | return(_label); | 184 | return(_label); | |
161 | } | 185 | } | |
162 | 186 | |||
163 | /// Return child item for the specified 'index'. | 187 | /// Return child item for the specified 'index'. | |
164 | const Fl_Tree_Item *Fl_Tree_Item::child(int index) const { | 188 | const Fl_Tree_Item *Fl_Tree_Item::child(int index) const { | |
165 | return(_children[index]); | 189 | return(_children[index]); | |
166 | } | 190 | } | |
167 | 191 | |||
168 | /// Clear all the children for this item. | 192 | /// Clear all the children for this item. | |
169 | void Fl_Tree_Item::clear_children() { | 193 | void Fl_Tree_Item::clear_children() { | |
170 | _children.clear(); | 194 | _children.clear(); | |
195 | recalc_tree(); // may change tree geometry | |||
171 | } | 196 | } | |
172 | 197 | |||
173 | /// Return the index of the immediate child of this item that has the label 'name'. | 198 | /// Return the index of the immediate child of this item that has the label 'name'. | |
174 | /// | 199 | /// | |
175 | /// \returns index of found item, or -1 if not found. | 200 | /// \returns index of found item, or -1 if not found. | |
176 | /// | 201 | /// | |
177 | int Fl_Tree_Item::find_child(const char *name) { | 202 | int Fl_Tree_Item::find_child(const char *name) { | |
178 | if ( name ) { | 203 | if ( name ) { | |
179 | for ( int t=0; t<children(); t++ ) { | 204 | for ( int t=0; t<children(); t++ ) { | |
180 | if ( child(t)->label() ) { | 205 | if ( child(t)->label() ) { | |
181 | if ( strcmp(child(t)->label(), name) == 0 ) { | 206 | if ( strcmp(child(t)->label(), name) == 0 ) { | |
182 | return(t); | 207 | return(t); | |
183 | } | 208 | } | |
184 | } | 209 | } | |
185 | } | 210 | } | |
186 | } | 211 | } | |
187 | return(-1); | 212 | return(-1); | |
188 | } | 213 | } | |
189 | 214 | |||
190 | /// Find child item by descending array of names. Does not include self in search. | 215 | /// Find child item by descending array of names. Does not include self in search. | |
191 | /// Only Fl_Tree should need this method. | 216 | /// Only Fl_Tree should need this method. | |
192 | /// | 217 | /// | |
193 | /// \returns item, or 0 if not found | 218 | /// \returns item, or 0 if not found | |
194 | /// | 219 | /// | |
195 | const Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) const { | 220 | const Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) const { | |
196 | for ( int t=0; t<children(); t++ ) { | 221 | for ( int t=0; t<children(); t++ ) { | |
197 | if ( child(t)->label() ) { | 222 | if ( child(t)->label() ) { | |
198 | if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? | 223 | if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? | |
199 | if ( *(arr+1) ) { // more in arr? descend | 224 | if ( *(arr+1) ) { // more in arr? descend | |
200 | return(_children[t]->find_item(arr+1)); | 225 | return(_children[t]->find_item(arr+1)); | |
201 | } else { // end of arr? done | 226 | } else { // end of arr? done | |
202 | return(_children[t]); | 227 | return(_children[t]); | |
203 | } | 228 | } | |
204 | } | 229 | } | |
205 | } | 230 | } | |
206 | } | 231 | } | |
207 | return(0); | 232 | return(0); | |
208 | } | 233 | } | |
209 | 234 | |||
210 | /// Find child item by descending array of names. Does not include self in search. | 235 | /// Find child item by descending array of names. Does not include self in search. | |
211 | /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. | 236 | /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. | |
212 | /// | 237 | /// | |
213 | /// \returns item, or 0 if not found | 238 | /// \returns item, or 0 if not found | |
214 | /// | 239 | /// | |
215 | Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) { | 240 | Fl_Tree_Item *Fl_Tree_Item::find_child_item(char **arr) { | |
216 | for ( int t=0; t<children(); t++ ) { | 241 | for ( int t=0; t<children(); t++ ) { | |
217 | if ( child(t)->label() ) { | 242 | if ( child(t)->label() ) { | |
218 | if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? | 243 | if ( strcmp(child(t)->label(), *arr) == 0 ) { // match? | |
219 | if ( *(arr+1) ) { // more in arr? descend | 244 | if ( *(arr+1) ) { // more in arr? descend | |
220 | return(_children[t]->find_item(arr+1)); | 245 | return(_children[t]->find_item(arr+1)); | |
221 | } else { // end of arr? done | 246 | } else { // end of arr? done | |
222 | return(_children[t]); | 247 | return(_children[t]); | |
223 | } | 248 | } | |
224 | } | 249 | } | |
225 | } | 250 | } | |
226 | } | 251 | } | |
227 | return(0); | 252 | return(0); | |
228 | } | 253 | } | |
229 | 254 | |||
230 | /// Find item by descending array of \p names. Includes self in search. | 255 | /// Find item by descending array of \p names. Includes self in search. | |
231 | /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. | 256 | /// Only Fl_Tree should need this method. Use Fl_Tree::find_item() instead. | |
232 | /// | 257 | /// | |
233 | /// \returns item, or 0 if not found | 258 | /// \returns item, or 0 if not found | |
234 | /// | 259 | /// | |
235 | const Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) const { | 260 | const Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) const { | |
236 | if ( label() && strcmp(label(), *names) == 0 ) { // match self? | 261 | if ( label() && strcmp(label(), *names) == 0 ) { // match self? | |
237 | if ( *(names+1) == 0 ) { // end of names, | 262 | if ( *(names+1) == 0 ) { // end of names, | |
238 | return(this); // found ourself. | 263 | return(this); // found ourself. | |
239 | } | 264 | } | |
240 | } | 265 | } | |
241 | if ( children() ) { // check children.. | 266 | if ( children() ) { // check children.. | |
242 | return(find_child_item(names)); | 267 | return(find_child_item(names)); | |
243 | } | 268 | } | |
244 | return(0); | 269 | return(0); | |
245 | } | 270 | } | |
246 | 271 | |||
247 | /// Find item by descending array of \p names. Includes self in search. | 272 | /// Find item by descending array of \p names. Includes self in search. | |
248 | /// Only Fl_Tree should need this method. | 273 | /// Only Fl_Tree should need this method. | |
249 | /// | 274 | /// | |
250 | /// \returns item, or 0 if not found | 275 | /// \returns item, or 0 if not found | |
251 | /// | 276 | /// | |
252 | Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) { | 277 | Fl_Tree_Item *Fl_Tree_Item::find_item(char **names) { | |
253 | if ( label() && strcmp(label(), *names) == 0 ) { // match self? | 278 | if ( label() && strcmp(label(), *names) == 0 ) { // match self? | |
254 | if ( *(names+1) == 0 ) { // end of names, | 279 | if ( *(names+1) == 0 ) { // end of names, | |
255 | return(this); // found ourself. | 280 | return(this); // found ourself. | |
256 | } | 281 | } | |
257 | } | 282 | } | |
258 | if ( children() ) { // check children.. | 283 | if ( children() ) { // check children.. | |
259 | return(find_child_item(names)); | 284 | return(find_child_item(names)); | |
260 | } | 285 | } | |
261 | return(0); | 286 | return(0); | |
262 | } | 287 | } | |
263 | 288 | |||
264 | /// Find the index number for the specified 'item' | 289 | /// Find the index number for the specified 'item' | |
265 | /// in the current item's list of children. | 290 | /// in the current item's list of children. | |
266 | /// | 291 | /// | |
267 | /// \returns the index, or -1 if not found. | 292 | /// \returns the index, or -1 if not found. | |
268 | /// | 293 | /// | |
269 | int Fl_Tree_Item::find_child(Fl_Tree_Item *item) { | 294 | int Fl_Tree_Item::find_child(Fl_Tree_Item *item) { | |
270 | for ( int t=0; t<children(); t++ ) { | 295 | for ( int t=0; t<children(); t++ ) { | |
271 | if ( item == child(t) ) { | 296 | if ( item == child(t) ) { | |
272 | return(t); | 297 | return(t); | |
273 | } | 298 | } | |
274 | } | 299 | } | |
275 | return(-1); | 300 | return(-1); | |
276 | } | 301 | } | |
277 | 302 | |||
278 | /// Add a new child to this item with the name 'new_label', with defaults from 'prefs'. | 303 | /// Add a new child to this item with the name 'new_label', with defaults from 'prefs'. | |
279 | /// An internally managed copy is made of the label string. | 304 | /// An internally managed copy is made of the label string. | |
280 | /// Adds the item based on the value of prefs.sortorder(). | 305 | /// Adds the item based on the value of prefs.sortorder(). | |
281 | /// | 306 | /// | |
282 | Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, const char *new_label) { | 307 | Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, const char *new_label) { | |
308 | #if FLTK_ABI_VERSION >= 10303 | |||
309 | Fl_Tree_Item *item = new Fl_Tree_Item(_tree); | |||
310 | #else | |||
283 | Fl_Tree_Item *item = new Fl_Tree_Item(prefs); | 311 | Fl_Tree_Item *item = new Fl_Tree_Item(prefs); | |
312 | #endif | |||
313 | recalc_tree(); // may change tree geometry | |||
284 | item->label(new_label); | 314 | item->label(new_label); | |
285 | item->_parent = this; | 315 | item->_parent = this; | |
286 | switch ( prefs.sortorder() ) { | 316 | switch ( prefs.sortorder() ) { | |
287 | case FL_TREE_SORT_NONE: { | 317 | case FL_TREE_SORT_NONE: { | |
288 | _children.add(item); | 318 | _children.add(item); | |
289 | return(item); | 319 | return(item); | |
290 | } | 320 | } | |
291 | case FL_TREE_SORT_ASCENDING: { | 321 | case FL_TREE_SORT_ASCENDING: { | |
292 | for ( int t=0; t<_children.total(); t++ ) { | 322 | for ( int t=0; t<_children.total(); t++ ) { | |
293 | Fl_Tree_Item *c = _children[t]; | 323 | Fl_Tree_Item *c = _children[t]; | |
294 | if ( c->label() && strcmp(c->label(), new_label) > 0 ) { | 324 | if ( c->label() && strcmp(c->label(), new_label) > 0 ) { | |
295 | _children.insert(t, item); | 325 | _children.insert(t, item); | |
296 | return(item); | 326 | return(item); | |
297 | } | 327 | } | |
298 | } | 328 | } | |
299 | _children.add(item); | 329 | _children.add(item); | |
300 | return(item); | 330 | return(item); | |
301 | } | 331 | } | |
302 | case FL_TREE_SORT_DESCENDING: { | 332 | case FL_TREE_SORT_DESCENDING: { | |
303 | for ( int t=0; t<_children.total(); t++ ) { | 333 | for ( int t=0; t<_children.total(); t++ ) { | |
304 | Fl_Tree_Item *c = _children[t]; | 334 | Fl_Tree_Item *c = _children[t]; | |
305 | if ( c->label() && strcmp(c->label(), new_label) < 0 ) { | 335 | if ( c->label() && strcmp(c->label(), new_label) < 0 ) { | |
306 | _children.insert(t, item); | 336 | _children.insert(t, item); | |
307 | return(item); | 337 | return(item); | |
308 | } | 338 | } | |
309 | } | 339 | } | |
310 | _children.add(item); | 340 | _children.add(item); | |
311 | return(item); | 341 | return(item); | |
312 | } | 342 | } | |
313 | } | 343 | } | |
314 | return(item); | 344 | return(item); | |
315 | } | 345 | } | |
316 | 346 | |||
317 | /// Descend into the path specified by \p arr, and add a new child there. | 347 | /// Descend into the path specified by \p arr, and add a new child there. | |
318 | /// Should be used only by Fl_Tree's internals. | 348 | /// Should be used only by Fl_Tree's internals. | |
319 | /// Adds the item based on the value of prefs.sortorder(). | 349 | /// Adds the item based on the value of prefs.sortorder(). | |
320 | /// \returns the item added. | 350 | /// \returns the item added. | |
321 | /// | 351 | /// | |
322 | Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) { | 352 | Fl_Tree_Item *Fl_Tree_Item::add(const Fl_Tree_Prefs &prefs, char **arr) { | |
323 | int t = (*arr && *(arr+1)) ? find_child(*arr) : -1; | 353 | int t = (*arr && *(arr+1)) ? find_child(*arr) : -1; | |
324 | Fl_Tree_Item *item = 0; | 354 | Fl_Tree_Item *item = 0; | |
325 | if ( t == -1 ) { | 355 | if ( t == -1 ) { | |
326 | item = (Fl_Tree_Item*)add(prefs, *arr); | 356 | item = (Fl_Tree_Item*)add(prefs, *arr); | |
327 | } else { | 357 | } else { | |
328 | item = (Fl_Tree_Item*)child(t); | 358 | item = (Fl_Tree_Item*)child(t); | |
329 | } | 359 | } | |
360 | recalc_tree(); // may change tree geometry | |||
330 | if ( *(arr+1) ) { // descend? | 361 | if ( *(arr+1) ) { // descend? | |
331 | return(item->add(prefs, arr+1)); | 362 | return(item->add(prefs, arr+1)); | |
332 | } else { | 363 | } else { | |
333 | return(item); // end? done | 364 | return(item); // end? done | |
334 | } | 365 | } | |
335 | } | 366 | } | |
336 | 367 | |||
337 | /// Insert a new item into current item's children at a specified position. | 368 | /// Insert a new item into current item's children at a specified position. | |
338 | /// \returns the new item inserted. | 369 | /// \returns the new item inserted. | |
339 | /// | 370 | /// | |
340 | Fl_Tree_Item *Fl_Tree_Item::insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos) { | 371 | Fl_Tree_Item *Fl_Tree_Item::insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos) { | |
372 | #if FLTK_ABI_VERSION >= 10303 | |||
373 | Fl_Tree_Item *item = new Fl_Tree_Item(_tree); | |||
374 | #else | |||
341 | Fl_Tree_Item *item = new Fl_Tree_Item(prefs); | 375 | Fl_Tree_Item *item = new Fl_Tree_Item(prefs); | |
376 | #endif | |||
342 | item->label(new_label); | 377 | item->label(new_label); | |
343 | item->_parent = this; | 378 | item->_parent = this; | |
344 | _children.insert(pos, item); | 379 | _children.insert(pos, item); | |
380 | recalc_tree(); // may change tree geometry | |||
345 | return(item); | 381 | return(item); | |
346 | } | 382 | } | |
347 | 383 | |||
348 | /// Insert a new item above this item. | 384 | /// Insert a new item above this item. | |
349 | /// \returns the new item inserted, or 0 if an error occurred. | 385 | /// \returns the new item inserted, or 0 if an error occurred. | |
350 | /// | 386 | /// | |
351 | Fl_Tree_Item *Fl_Tree_Item::insert_above(const Fl_Tree_Prefs &prefs, const char *new_label) { | 387 | Fl_Tree_Item *Fl_Tree_Item::insert_above(const Fl_Tree_Prefs &prefs, const char *new_label) { | |
352 | Fl_Tree_Item *p = _parent; | 388 | Fl_Tree_Item *p = _parent; | |
353 | if ( ! p ) return(0); | 389 | if ( ! p ) return(0); | |
354 | // Walk our parent's children to find ourself | 390 | // Walk our parent's children to find ourself | |
355 | for ( int t=0; t<p->children(); t++ ) { | 391 | for ( int t=0; t<p->children(); t++ ) { | |
356 | Fl_Tree_Item *c = p->child(t); | 392 | Fl_Tree_Item *c = p->child(t); | |
357 | if ( this == c ) { | 393 | if ( this == c ) { | |
358 | return(p->insert(prefs, new_label, t)); | 394 | return(p->insert(prefs, new_label, t)); | |
359 | } | 395 | } | |
360 | } | 396 | } | |
361 | return(0); | 397 | return(0); | |
362 | } | 398 | } | |
363 | 399 | |||
364 | /// Remove child by item. | 400 | /// Remove child by item. | |
365 | /// \returns 0 if removed, -1 if item not an immediate child. | 401 | /// \returns 0 if removed, -1 if item not an immediate child. | |
366 | /// | 402 | /// | |
367 | int Fl_Tree_Item::remove_child(Fl_Tree_Item *item) { | 403 | int Fl_Tree_Item::remove_child(Fl_Tree_Item *item) { | |
368 | for ( int t=0; t<children(); t++ ) { | 404 | for ( int t=0; t<children(); t++ ) { | |
369 | if ( child(t) == item ) { | 405 | if ( child(t) == item ) { | |
370 | item->clear_children(); | 406 | item->clear_children(); | |
371 | _children.remove(t); | 407 | _children.remove(t); | |
408 | recalc_tree(); // may change tree geometry | |||
372 | return(0); | 409 | return(0); | |
373 | } | 410 | } | |
374 | } | 411 | } | |
375 | return(-1); | 412 | return(-1); | |
376 | } | 413 | } | |
377 | 414 | |||
378 | /// Remove immediate child (and its children) by its label 'name'. | 415 | /// Remove immediate child (and its children) by its label 'name'. | |
379 | /// \returns 0 if removed, -1 if not found. | 416 | /// \returns 0 if removed, -1 if not found. | |
380 | /// | 417 | /// | |
381 | int Fl_Tree_Item::remove_child(const char *name) { | 418 | int Fl_Tree_Item::remove_child(const char *name) { | |
382 | for ( int t=0; t<children(); t++ ) { | 419 | for ( int t=0; t<children(); t++ ) { | |
383 | if ( child(t)->label() ) { | 420 | if ( child(t)->label() ) { | |
384 | if ( strcmp(child(t)->label(), name) == 0 ) { | 421 | if ( strcmp(child(t)->label(), name) == 0 ) { | |
385 | _children.remove(t); | 422 | _children.remove(t); | |
423 | recalc_tree(); // may change tree geometry | |||
386 | return(0); | 424 | return(0); | |
387 | } | 425 | } | |
388 | } | 426 | } | |
389 | } | 427 | } | |
390 | return(-1); | 428 | return(-1); | |
391 | } | 429 | } | |
392 | 430 | |||
393 | /// Swap two of our children, given two child index values. | 431 | /// Swap two of our children, given two child index values. | |
394 | /// Use this eg. for sorting. | 432 | /// Use this eg. for sorting. | |
395 | /// | 433 | /// | |
396 | /// This method is FAST, and does not involve lookups. | 434 | /// This method is FAST, and does not involve lookups. | |
397 | /// | 435 | /// | |
398 | /// No range checking is done on either index value. | 436 | /// No range checking is done on either index value. | |
399 | /// | 437 | /// | |
400 | /// \returns | 438 | /// \returns | |
401 | /// - 0 : OK | 439 | /// - 0 : OK | |
402 | /// - -1 : failed: 'a' or 'b' is not our immediate child | 440 | /// - -1 : failed: 'a' or 'b' is not our immediate child | |
403 | /// | 441 | /// | |
404 | void Fl_Tree_Item::swap_children(int ax, int bx) { | 442 | void Fl_Tree_Item::swap_children(int ax, int bx) { | |
405 | _children.swap(ax, bx); | 443 | _children.swap(ax, bx); | |
406 | } | 444 | } | |
407 | 445 | |||
408 | /// Swap two of our children, given item pointers. | 446 | /// Swap two of our children, given item pointers. | |
409 | /// Use this eg. for sorting. | 447 | /// Use this eg. for sorting. | |
410 | /// | 448 | /// | |
411 | /// This method is SLOW because it involves linear lookups. | 449 | /// This method is SLOW because it involves linear lookups. | |
412 | /// For speed, use swap_children(int,int) instead. | 450 | /// For speed, use swap_children(int,int) instead. | |
413 | /// | 451 | /// | |
414 | /// \returns | 452 | /// \returns | |
415 | /// - 0 : OK | 453 | /// - 0 : OK | |
416 | /// - -1 : failed: 'a' or 'b' is not our immediate child | 454 | /// - -1 : failed: 'a' or 'b' is not our immediate child | |
417 | /// | 455 | /// | |
418 | int Fl_Tree_Item::swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b) { | 456 | int Fl_Tree_Item::swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b) { | |
419 | int ax = -1, bx = -1; | 457 | int ax = -1, bx = -1; | |
420 | for ( int t=0; t<children(); t++ ) { // find index for a and b | 458 | for ( int t=0; t<children(); t++ ) { // find index for a and b | |
421 | if ( _children[t] == a ) { ax = t; if ( bx != -1 ) break; else continue; } | 459 | if ( _children[t] == a ) { ax = t; if ( bx != -1 ) break; else continue; } | |
422 | if ( _children[t] == b ) { bx = t; if ( ax != -1 ) break; else continue; } | 460 | if ( _children[t] == b ) { bx = t; if ( ax != -1 ) break; else continue; } | |
423 | } | 461 | } | |
424 | if ( ax == -1 || bx == -1 ) return(-1); // not found? fail | 462 | if ( ax == -1 || bx == -1 ) return(-1); // not found? fail | |
425 | swap_children(ax,bx); | 463 | swap_children(ax,bx); | |
426 | return(0); | 464 | return(0); | |
427 | } | 465 | } | |
428 | 466 | |||
429 | /// Internal: Horizontal connector line based on preference settings. | 467 | /// Internal: Horizontal connector line based on preference settings. | |
430 | void Fl_Tree_Item::draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs) { | 468 | void Fl_Tree_Item::draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs) { | |
431 | fl_color(prefs.connectorcolor()); | 469 | fl_color(prefs.connectorcolor()); | |
432 | switch ( prefs.connectorstyle() ) { | 470 | switch ( prefs.connectorstyle() ) { | |
433 | case FL_TREE_CONNECTOR_SOLID: | 471 | case FL_TREE_CONNECTOR_SOLID: | |
434 | y |= 1; // force alignment w/dot pattern | 472 | y |= 1; // force alignment w/dot pattern | |
435 | fl_line(x1,y,x2,y); | 473 | fl_line(x1,y,x2,y); | |
436 | return; | 474 | return; | |
437 | case FL_TREE_CONNECTOR_DOTTED: | 475 | case FL_TREE_CONNECTOR_DOTTED: | |
438 | { | 476 | { | |
439 | y |= 1; // force alignment w/dot pattern | 477 | y |= 1; // force alignment w/dot pattern | |
440 | for ( int xx=x1; xx<=x2; xx++ ) { | 478 | for ( int xx=x1; xx<=x2; xx++ ) { | |
441 | if ( !(xx & 1) ) fl_point(xx, y); | 479 | if ( !(xx & 1) ) fl_point(xx, y); | |
442 | } | 480 | } | |
443 | } | 481 | } | |
444 | return; | 482 | return; | |
445 | case FL_TREE_CONNECTOR_NONE: | 483 | case FL_TREE_CONNECTOR_NONE: | |
446 | return; | 484 | return; | |
447 | } | 485 | } | |
448 | } | 486 | } | |
449 | 487 | |||
450 | /// Internal: Vertical connector line based on preference settings. | 488 | /// Internal: Vertical connector line based on preference settings. | |
451 | void Fl_Tree_Item::draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs) { | 489 | void Fl_Tree_Item::draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs) { | |
452 | fl_color(prefs.connectorcolor()); | 490 | fl_color(prefs.connectorcolor()); | |
453 | switch ( prefs.connectorstyle() ) { | 491 | switch ( prefs.connectorstyle() ) { | |
454 | case FL_TREE_CONNECTOR_SOLID: | 492 | case FL_TREE_CONNECTOR_SOLID: | |
455 | y1 |= 1; // force alignment w/dot pattern | 493 | y1 |= 1; // force alignment w/dot pattern | |
456 | y2 |= 1; // force alignment w/dot pattern | 494 | y2 |= 1; // force alignment w/dot pattern | |
457 | fl_line(x,y1,x,y2); | 495 | fl_line(x,y1,x,y2); | |
458 | return; | 496 | return; | |
459 | case FL_TREE_CONNECTOR_DOTTED: | 497 | case FL_TREE_CONNECTOR_DOTTED: | |
460 | { | 498 | { | |
461 | y1 |= 1; // force alignment w/dot pattern | 499 | y1 |= 1; // force alignment w/dot pattern | |
462 | y2 |= 1; // force alignment w/dot pattern | 500 | y2 |= 1; // force alignment w/dot pattern | |
463 | for ( int yy=y1; yy<=y2; yy++ ) { | 501 | for ( int yy=y1; yy<=y2; yy++ ) { | |
464 | if ( yy & 1 ) fl_point(x, yy); | 502 | if ( yy & 1 ) fl_point(x, yy); | |
465 | } | 503 | } | |
466 | } | 504 | } | |
467 | return; | 505 | return; | |
468 | case FL_TREE_CONNECTOR_NONE: | 506 | case FL_TREE_CONNECTOR_NONE: | |
469 | return; | 507 | return; | |
470 | } | 508 | } | |
471 | } | 509 | } | |
472 | 510 | |||
473 | /// Find the item that the last event was over. | 511 | // Internal | |
474 | /// | 512 | const Fl_Tree_Item *Fl_Tree_Item::find_clicked_(const Fl_Tree_Prefs &prefs, int yonly) const { | |
475 | /// Returns the item if it is visible, and mouse is over it. | |||
476 | /// Works even if widget deactivated. | |||
477 | /// Use event_on_collapse_icon() to determine if collapse button was pressed. | |||
478 | /// | |||
479 | /// \returns const visible item under the event if found, or 0 if none. | |||
480 | /// | |||
481 | const Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs) const { | |||
482 | if ( ! is_visible() ) return(0); | 513 | if ( ! is_visible() ) return(0); | |
483 | if ( is_root() && !prefs.showroot() ) { | 514 | if ( is_root() && !prefs.showroot() ) { | |
484 | // skip event check if we're root but root not being shown | 515 | // skip event check if we're root but root not being shown | |
485 | } else { | 516 | } else { | |
486 | // See if event is over us | 517 | // See if event is over us | |
518 | if ( yonly ) { | |||
519 | if ( Fl::event_y() >= _xywh[1] && | |||
520 | Fl::event_y() <= (_xywh[1]+_xywh[3]) ) { | |||
521 | return(this); | |||
522 | } | |||
523 | } else { | |||
487 | if ( event_inside(_xywh) ) { // event within this item? | 524 | if ( event_inside(_xywh) ) { // event within this item? | |
488 | return(this); // found | 525 | return(this); // found | |
489 | } | 526 | } | |
490 | } | 527 | } | |
528 | } | |||
491 | if ( is_open() ) { // open? check children of this item | 529 | if ( is_open() ) { // open? check children of this item | |
492 | for ( int t=0; t<children(); t++ ) { | 530 | for ( int t=0; t<children(); t++ ) { | |
493 | const Fl_Tree_Item *item; | 531 | const Fl_Tree_Item *item; | |
494 | if ( ( item = _children[t]->find_clicked(prefs) ) != NULL) { // check child and its descendents | 532 | if ( (item = _children[t]->find_clicked(prefs, yonly)) != NULL) { // check child and its descendents | |
495 | return(item); // found? | 533 | return(item); // found? | |
496 | } | 534 | } | |
497 | } | 535 | } | |
498 | } | 536 | } | |
499 | return(0); | 537 | return(0); | |
500 | } | 538 | } | |
501 | 539 | |||
540 | /// Find the item that last event was over. | |||
541 | /// | |||
542 | /// Returns the item if it is visible, and mouse is over it. | |||
543 | /// Works even if widget deactivated. | |||
544 | /// Use event_on_collapse_icon() to determine if collapse button was pressed. | |||
545 | /// | |||
546 | /// If \a yonly is set, only the mouse Y position is checked. | |||
547 | /// | |||
548 | /// \returns const visible item under the event if found, or 0 if none. | |||
549 | /// | |||
550 | const Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs, int yonly) const { | |||
551 | return(find_clicked_(prefs, yonly)); | |||
552 | } | |||
553 | ||||
502 | /// Non-const version of the above. | 554 | /// Non-const version of the above. | |
503 | /// Find the item that the last event was over. | 555 | /// Find the item that last event was over. | |
504 | /// | 556 | /// | |
505 | /// Returns the item if it is visible, and mouse is over it. | 557 | /// Returns the item if it is visible, and mouse is over it. | |
506 | /// Works even if widget deactivated. | 558 | /// Works even if widget deactivated. | |
507 | /// Use event_on_collapse_icon() to determine if collapse button was pressed. | 559 | /// Use event_on_collapse_icon() to determine if collapse button was pressed. | |
508 | /// | 560 | /// | |
509 | /// \returns the visible item under the event if found, or 0 if none. | 561 | /// \returns the visible item under the event if found, or 0 if none. | |
510 | /// | 562 | /// | |
511 | Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs) { | 563 | Fl_Tree_Item *Fl_Tree_Item::find_clicked(const Fl_Tree_Prefs &prefs, int yonly) { | |
512 | if ( ! is_visible() ) return(0); | 564 | return((Fl_Tree_Item*)find_clicked_(prefs, yonly)); | |
513 | if ( is_root() && !prefs.showroot() ) { | |||
514 | // skip event check if we're root but root not being shown | |||
515 | } else { | |||
516 | // See if event is over us | |||
517 | if ( event_inside(_xywh) ) { // event within this item? | |||
518 | return(this); // found | |||
519 | } | |||
520 | } | |||
521 | if ( is_open() ) { // open? check children of this item | |||
522 | for ( int t=0; t<children(); t++ ) { | |||
523 | Fl_Tree_Item *item; | |||
524 | if ( ( item = _children[t]->find_clicked(prefs) ) != NULL ) { // check child and its descendents | |||
525 | return(item); // found? | |||
526 | } | |||
527 | } | |||
528 | } | |||
529 | return(0); | |||
530 | } | 565 | } | |
531 | 566 | |||
532 | static void draw_item_focus(Fl_Boxtype B, Fl_Color fg, Fl_Color bg, int X, int Y, int W, int H) { | 567 | static void draw_item_focus(Fl_Boxtype B, Fl_Color fg, Fl_Color bg, int X, int Y, int W, int H) { | |
533 | if (!Fl::visible_focus()) return; | 568 | if (!Fl::visible_focus()) return; | |
534 | switch (B) { | 569 | switch (B) { | |
535 | case FL_DOWN_BOX: | 570 | case FL_DOWN_BOX: | |
536 | case FL_DOWN_FRAME: | 571 | case FL_DOWN_FRAME: | |
537 | case FL_THIN_DOWN_BOX: | 572 | case FL_THIN_DOWN_BOX: | |
538 | case FL_THIN_DOWN_FRAME: | 573 | case FL_THIN_DOWN_FRAME: | |
539 | X ++; | 574 | X ++; | |
540 | Y ++; | 575 | Y ++; | |
541 | default: | 576 | default: | |
542 | break; | 577 | break; | |
543 | } | 578 | } | |
544 | fl_color(fl_contrast(fg, bg)); | 579 | fl_color(fl_contrast(fg, bg)); | |
545 | 580 | |||
546 | #if defined(USE_X11) || defined(__APPLE_QUARTZ__) | 581 | #if defined(USE_X11) || defined(__APPLE_QUARTZ__) | |
547 | fl_line_style(FL_DOT); | 582 | fl_line_style(FL_DOT); | |
548 | fl_rect(X + Fl::box_dx(B), Y + Fl::box_dy(B), | 583 | fl_rect(X + Fl::box_dx(B), Y + Fl::box_dy(B), | |
549 | W - Fl::box_dw(B) - 1, H - Fl::box_dh(B) - 1); | 584 | W - Fl::box_dw(B) - 1, H - Fl::box_dh(B) - 1); | |
550 | fl_line_style(FL_SOLID); | 585 | fl_line_style(FL_SOLID); | |
551 | #else | 586 | #else | |
552 | // Some platforms don't implement dotted line style, so draw | 587 | // Some platforms don't implement dotted line style, so draw | |
553 | // every other pixel around the focus area... | 588 | // every other pixel around the focus area... | |
554 | // | 589 | // | |
555 | // Also, QuickDraw (MacOS) does not support line styles specifically, | 590 | // Also, QuickDraw (MacOS) does not support line styles specifically, | |
556 | // and the hack we use in fl_line_style() will not draw horizontal lines | 591 | // and the hack we use in fl_line_style() will not draw horizontal lines | |
557 | // on odd-numbered rows... | 592 | // on odd-numbered rows... | |
558 | int i, xx, yy; | 593 | int i, xx, yy; | |
559 | 594 | |||
560 | X += Fl::box_dx(B); | 595 | X += Fl::box_dx(B); | |
561 | Y += Fl::box_dy(B); | 596 | Y += Fl::box_dy(B); | |
562 | W -= Fl::box_dw(B) + 2; | 597 | W -= Fl::box_dw(B) + 2; | |
563 | H -= Fl::box_dh(B) + 2; | 598 | H -= Fl::box_dh(B) + 2; | |
564 | 599 | |||
565 | for (xx = 0, i = 1; xx < W; xx ++, i ++) if (i & 1) fl_point(X + xx, Y); | 600 | for (xx = 0, i = 1; xx < W; xx ++, i ++) if (i & 1) fl_point(X + xx, Y); | |
566 | for (yy = 0; yy < H; yy ++, i ++) if (i & 1) fl_point(X + W, Y + yy); | 601 | for (yy = 0; yy < H; yy ++, i ++) if (i & 1) fl_point(X + W, Y + yy); | |
567 | for (xx = W; xx > 0; xx --, i ++) if (i & 1) fl_point(X + xx, Y + H); | 602 | for (xx = W; xx > 0; xx --, i ++) if (i & 1) fl_point(X + xx, Y + H); | |
568 | for (yy = H; yy > 0; yy --, i ++) if (i & 1) fl_point(X, Y + yy); | 603 | for (yy = H; yy > 0; yy --, i ++) if (i & 1) fl_point(X, Y + yy); | |
569 | #endif | 604 | #endif | |
570 | } | 605 | } | |
571 | 606 | |||
572 | /// Return the item's 'visible' height. | 607 | /// Return the item's 'visible' height. | |
573 | /// Doesn't include linespacing(); prevents affecting eg. height of widget(). | 608 | /// Doesn't include linespacing(); prevents affecting eg. height of widget(). | |
574 | /// | 609 | /// | |
575 | int Fl_Tree_Item::calc_item_height(const Fl_Tree_Prefs &prefs) const { | 610 | int Fl_Tree_Item::calc_item_height(const Fl_Tree_Prefs &prefs) const { | |
576 | if ( ! is_visible() ) return(0); | 611 | if ( ! is_visible() ) return(0); | |
577 | int H = 0; | 612 | int H = 0; | |
578 | if ( _label ) { | 613 | if ( _label ) { | |
579 | fl_font(_labelfont, _labelsize); // fl_descent() needs this :/ | 614 | fl_font(_labelfont, _labelsize); // fl_descent() needs this :/ | |
580 | H = _labelsize + fl_descent() + 1; // at least one pixel space below descender | 615 | H = _labelsize + fl_descent() + 1; // at least one pixel space below descender | |
581 | } | 616 | } | |
582 | #if FLTK_ABI_VERSION >= 10301 | 617 | #if FLTK_ABI_VERSION >= 10301 | |
583 | if ( widget() && | 618 | if ( widget() && | |
584 | (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) && | 619 | (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) && | |
585 | H < widget()->h()) { | 620 | H < widget()->h()) { | |
586 | H = widget()->h(); | 621 | H = widget()->h(); | |
587 | } | 622 | } | |
588 | #endif /*FLTK_ABI_VERSION*/ | 623 | #endif /*FLTK_ABI_VERSION*/ | |
589 | if ( has_children() && prefs.openicon() && H<prefs.openicon()->h() ) | 624 | if ( has_children() && prefs.openicon() && H<prefs.openicon()->h() ) | |
590 | H = prefs.openicon()->h(); | 625 | H = prefs.openicon()->h(); | |
591 | if ( usericon() && H<usericon()->h() ) | 626 | if ( usericon() && H<usericon()->h() ) | |
592 | H = usericon()->h(); | 627 | H = usericon()->h(); | |
593 | return(H); | 628 | return(H); | |
594 | } | 629 | } | |
595 | 630 | |||
631 | #if FLTK_ABI_VERSION >= 10303 | |||
596 | /// Draw this item and its children. | 632 | /// Draw this item and its children. | |
633 | /// | |||
634 | /// \param[in] X Horizontal position for item being drawn | |||
635 | /// \param[in,out] Y Vertical position for item being drawn, returns new position for next item | |||
636 | /// \param[in] W Recommended width of item | |||
637 | /// \param[in] itemfocus The tree's current focus item (if any) | |||
638 | /// \param[in,out] tree_item_xmax The tree's running xmax (right-most edge so far). | |||
639 | /// Mainly used by parent tree when render==0 to calculate tree's max width. | |||
640 | /// \param[in] lastchild Is this item the last child in a subtree? | |||
641 | /// \param[in] render Whether or not to render the item: | |||
642 | /// - 0 -- no rendering, just calculate size. | |||
643 | /// (used to calculate size of tree without doing drawing) | |||
644 | /// - 1 -- render the item as well as doing size calculations | |||
645 | /// | |||
646 | void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, | |||
647 | int &tree_item_xmax, int lastchild, int render) { | |||
648 | Fl_Tree_Prefs &prefs = _tree->_prefs; | |||
649 | if ( !is_visible() ) return; | |||
650 | int tree_top = _tree->_tiy; | |||
651 | int tree_bot = tree_top + _tree->_tih; | |||
652 | int H = calc_item_height(prefs); // height of item | |||
653 | int H2 = H + prefs.linespacing(); // height of item with line spacing | |||
654 | ||||
655 | // Update the xywh of this item | |||
656 | _xywh[0] = X; | |||
657 | _xywh[1] = Y; | |||
658 | _xywh[2] = W; | |||
659 | _xywh[3] = H; | |||
660 | ||||
661 | // Determine collapse icon's xywh | |||
662 | // Note: calculate collapse icon's xywh for possible mouse click detection. | |||
663 | // We don't care about items clipped off the viewport; they won't get mouse events. | |||
664 | // | |||
665 | int item_y_center = Y+(H/2); | |||
666 | _collapse_xywh[2] = prefs.openicon()->w(); | |||
667 | int &icon_w = _collapse_xywh[2]; | |||
668 | _collapse_xywh[0] = X + (icon_w + prefs.connectorwidth())/2 - 3; | |||
669 | int &icon_x = _collapse_xywh[0]; | |||
670 | _collapse_xywh[1] = item_y_center - (prefs.openicon()->h()/2); | |||
671 | int &icon_y = _collapse_xywh[1]; | |||
672 | _collapse_xywh[3] = prefs.openicon()->h(); | |||
673 | ||||
674 | // Horizontal connector values | |||
675 | // Must calculate these even if(clipped) because 'draw children' code (below) | |||
676 | // needs hconn_x_center value. (Otherwise, these calculations could be 'clipped') | |||
677 | // | |||
678 | int hconn_x = X+icon_w/2-1; | |||
679 | int hconn_x2 = hconn_x + prefs.connectorwidth(); | |||
680 | int hconn_x_center = X + icon_w + ((hconn_x2 - (X + icon_w)) / 2); | |||
681 | int cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth(); | |||
682 | int conn_w = cw1>cw2 ? cw1 : cw2; | |||
683 | ||||
684 | // Background xywh | |||
685 | int &bg_x = _label_xywh[0] = X+(icon_w/2-1+conn_w); | |||
686 | int &bg_y = _label_xywh[1] = Y; | |||
687 | int &bg_w = _label_xywh[2] = _tree->_tix + _tree->_tiw - bg_x; | |||
688 | int &bg_h = _label_xywh[3] = H; | |||
689 | ||||
690 | // Usericon position | |||
691 | int uicon_x = bg_x + ( (usericon() || prefs.usericon()) ? prefs.usericonmarginleft() : 0); | |||
692 | int uicon_w = usericon() ? usericon()->w() : prefs.usericon() ? prefs.usericon()->w() : 0; | |||
693 | ||||
694 | // Label position | |||
695 | int label_x = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); | |||
696 | ||||
697 | // Begin calc of this item's max width.. | |||
698 | // It might not even be visible, so start at zero. | |||
699 | // | |||
700 | int ixmax = 0; | |||
701 | ||||
702 | // Recalc widget position | |||
703 | // Do this whether clipped or not, so that when scrolled, | |||
704 | // the widgets move to appropriate 'offscreen' positions | |||
705 | // (so that they don't get mouse events, etc) | |||
706 | // | |||
707 | if ( widget() ) { | |||
708 | int wx = label_x; | |||
709 | int wy = bg_y; | |||
710 | int ww = widget()->w(); // use widget's width | |||
711 | int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) | |||
712 | ? widget()->h() : H; | |||
713 | if ( _label && | |||
714 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) { | |||
715 | fl_font(_labelfont, _labelsize); // fldescent() needs this | |||
716 | int lw=0, lh=0; | |||
717 | fl_measure(_label,lw,lh); // get box around text (including white space) | |||
718 | wx += (lw + prefs.widgetmarginleft()); | |||
719 | } | |||
720 | if ( widget()->x() != wx || widget()->y() != wy || | |||
721 | widget()->w() != ww || widget()->h() != wh ) { | |||
722 | widget()->resize(wx,wy,ww,wh); // we'll handle redraw below | |||
723 | } | |||
724 | } | |||
725 | char clipped = ((Y+H) < tree_top) || (Y>tree_bot) ? 1 : 0; | |||
726 | if (!render) clipped = 0; // NOT rendering? Then don't clip, so we calc unclipped items | |||
727 | char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; | |||
728 | if ( !clipped ) { | |||
729 | Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, _tree->selection_color()) | |||
730 | : is_active() ? _labelfgcolor | |||
731 | : fl_inactive(_labelfgcolor); | |||
732 | Fl_Color bg = is_selected() ? is_active() ? _tree->selection_color() | |||
733 | : fl_inactive(_tree->selection_color()) | |||
734 | : _labelbgcolor == 0xffffffff ? _tree->color() // transparent bg? | |||
735 | : _labelbgcolor; | |||
736 | // See if we should draw this item | |||
737 | // If this item is root, and showroot() is disabled, don't draw. | |||
738 | // 'clipped' is an optimization to prevent drawing anything offscreen. | |||
739 | // | |||
740 | if ( drawthis ) { // draw this item at all? | |||
741 | if ( (_tree->damage() & ~FL_DAMAGE_CHILD) || !render ) { // non-child damage? | |||
742 | // Draw connectors | |||
743 | if ( render && prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { | |||
744 | // Horiz connector between center of icon and text | |||
745 | // if this is root, the connector should not dangle in thin air on the left | |||
746 | if (is_root()) draw_horizontal_connector(hconn_x_center, hconn_x2, item_y_center, prefs); | |||
747 | else draw_horizontal_connector(hconn_x, hconn_x2, item_y_center, prefs); | |||
748 | // Small vertical line down to children | |||
749 | if ( has_children() && is_open() ) | |||
750 | draw_vertical_connector(hconn_x_center, item_y_center, Y+H2, prefs); | |||
751 | // Connectors for last child | |||
752 | if ( !is_root() ) { | |||
753 | if ( lastchild ) draw_vertical_connector(hconn_x, Y, item_y_center, prefs); | |||
754 | else draw_vertical_connector(hconn_x, Y, Y+H2, prefs); | |||
755 | } | |||
756 | } | |||
757 | // Draw collapse icon | |||
758 | if ( render && has_children() && prefs.showcollapse() ) { | |||
759 | // Draw icon image | |||
760 | if ( is_open() ) { | |||
761 | prefs.closeicon()->draw(icon_x,icon_y); | |||
762 | } else { | |||
763 | prefs.openicon()->draw(icon_x,icon_y); | |||
764 | } | |||
765 | } | |||
766 | // Background for this item | |||
767 | // Draw bg only if different from tree's bg | |||
768 | if ( render && (bg != _tree->color() || is_selected()) ) { | |||
769 | if ( is_selected() ) { // Selected? Use selectbox() style | |||
770 | fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); | |||
771 | } else { // Not Selected? use plain filled rectangle | |||
772 | fl_color(bg); | |||
773 | fl_rectf(bg_x,bg_y,bg_w,bg_h); | |||
774 | } | |||
775 | if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it | |||
776 | } | |||
777 | // Draw user icon (if any) | |||
778 | if ( render && usericon() ) { | |||
779 | // Item has user icon? Use it | |||
780 | int uicon_y = item_y_center - (usericon()->h() >> 1); | |||
781 | usericon()->draw(uicon_x,uicon_y); | |||
782 | } else if ( render && prefs.usericon() ) { | |||
783 | // Prefs has user icon? Use it | |||
784 | int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); | |||
785 | prefs.usericon()->draw(uicon_x,uicon_y); | |||
786 | } | |||
787 | // Draw label | |||
788 | if ( _label && | |||
789 | ( !widget() || | |||
790 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) { | |||
791 | if ( render ) { | |||
792 | fl_color(fg); | |||
793 | fl_font(_labelfont, _labelsize); | |||
794 | } | |||
795 | int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; | |||
796 | ||||
797 | int lw=0, lh=0; | |||
798 | fl_measure(_label, lw, lh); // get box around text (including white space) | |||
799 | if ( render ) fl_draw(_label, label_x, label_y); | |||
800 | ixmax = label_x + lw; // update max width of drawn item | |||
801 | } | |||
802 | } // end non-child damage | |||
803 | // Draw child FLTK widget? | |||
804 | if ( widget() ) { | |||
805 | if (render) | |||
806 | _tree->draw_child(*widget()); // let group handle drawing child | |||
807 | if ( widget()->label() && render ) | |||
808 | _tree->draw_outside_label(*widget()); // label too | |||
809 | ixmax = widget()->x() + widget()->w(); // update max width of widget | |||
810 | } | |||
811 | // Draw focus box around item's bg last | |||
812 | if ( render && | |||
813 | this == itemfocus && | |||
814 | Fl::visible_focus() && | |||
815 | Fl::focus() == _tree && | |||
816 | prefs.selectmode() != FL_TREE_SELECT_NONE ) { | |||
817 | draw_item_focus(FL_NO_BOX,fg,bg,bg_x+1,bg_y+1,bg_w-1,bg_h-1); | |||
818 | } | |||
819 | } // end drawthis | |||
820 | } // end clipped | |||
821 | if ( drawthis ) Y += H2; // adjust Y (even if clipped) | |||
822 | // Manage tree_item_xmax | |||
823 | if ( ixmax > tree_item_xmax ) | |||
824 | tree_item_xmax = ixmax; | |||
825 | // Draw child items (if any) | |||
826 | if ( has_children() && is_open() ) { | |||
827 | int child_x = drawthis ? (hconn_x_center - (icon_w/2) + 1) // offset children to right, | |||
828 | : X; // unless didn't drawthis | |||
829 | int child_w = W - (child_x-X); | |||
830 | int child_y_start = Y; | |||
831 | for ( int t=0; t<children(); t++ ) { | |||
832 | int lastchild = ((t+1)==children()) ? 1 : 0; | |||
833 | _children[t]->draw(child_x, Y, child_w, itemfocus, tree_item_xmax, lastchild, render); | |||
834 | } | |||
835 | if ( has_children() && is_open() ) { | |||
836 | Y += prefs.openchild_marginbottom(); // offset below open child tree | |||
837 | } | |||
838 | if ( ! lastchild ) { | |||
839 | // Special 'clipped' calculation. (intentional variable shadowing) | |||
840 | int clipped = ((child_y_start < tree_top) && (Y < tree_top)) || | |||
841 | ((child_y_start > tree_bot) && (Y > tree_bot)); | |||
842 | if (render && !clipped ) | |||
843 | draw_vertical_connector(hconn_x, child_y_start, Y, prefs); | |||
844 | } | |||
845 | } | |||
846 | } | |||
847 | ||||
848 | #else | |||
849 | ||||
850 | /// Draw this item and its children. | |||
851 | /// | |||
852 | /// \param[in] X Horizontal position for item being drawn | |||
853 | /// \param[in,out] Y Vertical position for item being drawn, returns new position for next item | |||
854 | /// \param[in] W Recommended width of item | |||
855 | /// \param[in] tree The parent tree | |||
856 | /// \param[in] itemfocus The tree's current focus item (if any) | |||
857 | /// \param[in] prefs The tree's preferences | |||
858 | /// \param[in] lastchild Is this item the last child in a subtree? | |||
859 | /// | |||
597 | void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, | 860 | void Fl_Tree_Item::draw(int X, int &Y, int W, Fl_Widget *tree, | |
598 | Fl_Tree_Item *itemfocus, | 861 | Fl_Tree_Item *itemfocus, | |
599 | const Fl_Tree_Prefs &prefs, int lastchild) { | 862 | const Fl_Tree_Prefs &prefs, int lastchild) { | |
600 | if ( ! is_visible() ) return; | 863 | if ( ! is_visible() ) return; | |
601 | int tree_top = tree->y(); | 864 | int tree_top = tree->y(); | |
602 | int tree_bot = tree_top + tree->h(); | 865 | int tree_bot = tree_top + tree->h(); | |
603 | int H = calc_item_height(prefs); // height of item | 866 | int H = calc_item_height(prefs); // height of item | |
604 | int H2 = H + prefs.linespacing(); // height of item with line spacing | 867 | int H2 = H + prefs.linespacing(); // height of item with line spacing | |
605 | 868 | |||
606 | // Update the xywh of this item | 869 | // Update the xywh of this item | |
607 | _xywh[0] = X; | 870 | _xywh[0] = X; | |
608 | _xywh[1] = Y; | 871 | _xywh[1] = Y; | |
609 | _xywh[2] = W; | 872 | _xywh[2] = W; | |
610 | _xywh[3] = H; | 873 | _xywh[3] = H; | |
611 | 874 | |||
612 | // Determine collapse icon's xywh | 875 | // Determine collapse icon's xywh | |
613 | // Note: calculate collapse icon's xywh for possible mouse click detection. | 876 | // Note: calculate collapse icon's xywh for possible mouse click detection. | |
614 | // We don't care about items clipped off the viewport; they won't get mouse events. | 877 | // We don't care about items clipped off the viewport; they won't get mouse events. | |
615 | // | 878 | // | |
616 | int item_y_center = Y+(H/2); | 879 | int item_y_center = Y+(H/2); | |
617 | _collapse_xywh[2] = prefs.openicon()->w(); | 880 | _collapse_xywh[2] = prefs.openicon()->w(); | |
618 | int &icon_w = _collapse_xywh[2]; | 881 | int &icon_w = _collapse_xywh[2]; | |
619 | _collapse_xywh[0] = X + (icon_w + prefs.connectorwidth())/2 - 3; | 882 | _collapse_xywh[0] = X + (icon_w + prefs.connectorwidth())/2 - 3; | |
620 | int &icon_x = _collapse_xywh[0]; | 883 | int &icon_x = _collapse_xywh[0]; | |
621 | _collapse_xywh[1] = item_y_center - (prefs.openicon()->h()/2); | 884 | _collapse_xywh[1] = item_y_center - (prefs.openicon()->h()/2); | |
622 | int &icon_y = _collapse_xywh[1]; | 885 | int &icon_y = _collapse_xywh[1]; | |
623 | _collapse_xywh[3] = prefs.openicon()->h(); | 886 | _collapse_xywh[3] = prefs.openicon()->h(); | |
624 | 887 | |||
625 | // Horizontal connector values | 888 | // Horizontal connector values | |
626 | // XXX: Must calculate these even if(clipped) because 'draw children' code (below) | 889 | // XXX: Must calculate these even if(clipped) because 'draw children' code (below) | |
627 | // needs hconn_x_center value. (Otherwise, these calculations could be 'clipped') | 890 | // needs hconn_x_center value. (Otherwise, these calculations could be 'clipped') | |
628 | // | 891 | // | |
629 | int hconn_x = X+icon_w/2-1; | 892 | int hconn_x = X+icon_w/2-1; | |
630 | int hconn_x2 = hconn_x + prefs.connectorwidth(); | 893 | int hconn_x2 = hconn_x + prefs.connectorwidth(); | |
631 | int hconn_x_center = X + icon_w + ((hconn_x2 - (X + icon_w)) / 2); | 894 | int hconn_x_center = X + icon_w + ((hconn_x2 - (X + icon_w)) / 2); | |
632 | int cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth(); | 895 | int cw1 = icon_w+prefs.connectorwidth()/2, cw2 = prefs.connectorwidth(); | |
633 | int conn_w = cw1>cw2 ? cw1 : cw2; | 896 | int conn_w = cw1>cw2 ? cw1 : cw2; | |
634 | 897 | |||
635 | // Background position | 898 | // Background position | |
636 | int &bg_x = _label_xywh[0] = X+(icon_w/2-1+conn_w); | 899 | int &bg_x = _label_xywh[0] = X+(icon_w/2-1+conn_w); | |
637 | int &bg_y = _label_xywh[1] = Y; | 900 | int &bg_y = _label_xywh[1] = Y; | |
638 | int &bg_w = _label_xywh[2] = W-(icon_w/2-1+conn_w); | 901 | int &bg_w = _label_xywh[2] = W-(icon_w/2-1+conn_w); | |
639 | int &bg_h = _label_xywh[3] = H; | 902 | int &bg_h = _label_xywh[3] = H; | |
640 | 903 | |||
641 | // Usericon position | 904 | // Usericon position | |
642 | int uicon_x = bg_x + ( (usericon() || prefs.usericon()) ? prefs.usericonmarginleft() : 0); | 905 | int uicon_x = bg_x + ( (usericon() || prefs.usericon()) ? prefs.usericonmarginleft() : 0); | |
643 | int uicon_w = usericon() ? usericon()->w() : prefs.usericon() ? prefs.usericon()->w() : 0; | 906 | int uicon_w = usericon() ? usericon()->w() : prefs.usericon() ? prefs.usericon()->w() : 0; | |
644 | 907 | |||
645 | // Label position | 908 | // Label position | |
646 | int label_x = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); | 909 | int label_x = uicon_x + uicon_w + (_label ? prefs.labelmarginleft() : 0); | |
647 | 910 | |||
648 | // Recalc widget position | 911 | // Recalc widget position | |
649 | // Do this whether clipped or not, so that when scrolled, | 912 | // Do this whether clipped or not, so that when scrolled, | |
650 | // the widgets move to appropriate 'offscreen' positions | 913 | // the widgets move to appropriate 'offscreen' positions | |
651 | // (so that they don't get mouse events, etc) | 914 | // (so that they don't get mouse events, etc) | |
652 | // | 915 | // | |
653 | if ( widget() ) { | 916 | if ( widget() ) { | |
654 | int wx = label_x; | 917 | int wx = label_x; | |
655 | int wy = bg_y; | 918 | int wy = bg_y; | |
656 | int ww = widget()->w(); // use widget's width | 919 | int ww = widget()->w(); // use widget's width | |
657 | #if FLTK_ABI_VERSION >= 10301 | 920 | #if FLTK_ABI_VERSION >= 10301 | |
658 | int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) | 921 | int wh = (prefs.item_draw_mode() & FL_TREE_ITEM_HEIGHT_FROM_WIDGET) | |
659 | ? widget()->h() : H; | 922 | ? widget()->h() : H; | |
660 | if ( _label && | 923 | if ( _label && | |
661 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) { | 924 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) { | |
662 | #else /*FLTK_ABI_VERSION*/ | 925 | #else /*FLTK_ABI_VERSION*/ | |
663 | int wh = H; // lock widget's height to item height | 926 | int wh = H; // lock widget's height to item height | |
664 | if ( _label && !widget() ) { // back compat: don't draw label if widget() present | 927 | if ( _label && !widget() ) { // back compat: don't draw label if widget() present | |
665 | #endif /*FLTK_ABI_VERSION*/ | 928 | #endif /*FLTK_ABI_VERSION*/ | |
666 | fl_font(_labelfont, _labelsize); // fldescent() needs this | 929 | fl_font(_labelfont, _labelsize); // fldescent() needs this | |
667 | int lw=0, lh=0; | 930 | int lw=0, lh=0; | |
668 | fl_measure(_label,lw,lh); // get box around text (including white space) | 931 | fl_measure(_label,lw,lh); // get box around text (including white space) | |
669 | #if FLTK_ABI_VERSION >= 10301 | 932 | #if FLTK_ABI_VERSION >= 10301 | |
670 | // NEW | 933 | // NEW | |
671 | wx += (lw + prefs.widgetmarginleft()); | 934 | wx += (lw + prefs.widgetmarginleft()); | |
672 | #else /*FLTK_ABI_VERSION*/ | 935 | #else /*FLTK_ABI_VERSION*/ | |
673 | // OLD | 936 | // OLD | |
674 | wx += (lw + 3); | 937 | wx += (lw + 3); | |
675 | #endif /*FLTK_ABI_VERSION*/ | 938 | #endif /*FLTK_ABI_VERSION*/ | |
676 | } | 939 | } | |
677 | if ( widget()->x() != wx || widget()->y() != wy || | 940 | if ( widget()->x() != wx || widget()->y() != wy || | |
678 | widget()->w() != ww || widget()->h() != wh ) { | 941 | widget()->w() != ww || widget()->h() != wh ) { | |
679 | widget()->resize(wx,wy,ww,wh); // we'll handle redraw below | 942 | widget()->resize(wx,wy,ww,wh); // we'll handle redraw below | |
680 | } | 943 | } | |
681 | } | 944 | } | |
682 | char clipped = ((Y+H) < tree_top) || (Y>tree_bot) ? 1 : 0; | 945 | char clipped = ((Y+H) < tree_top) || (Y>tree_bot) ? 1 : 0; | |
683 | char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; | 946 | char drawthis = ( is_root() && prefs.showroot() == 0 ) ? 0 : 1; | |
684 | if ( !clipped ) { | 947 | if ( !clipped ) { | |
685 | Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, tree->selection_color()) | 948 | Fl_Color fg = is_selected() ? fl_contrast(_labelfgcolor, tree->selection_color()) | |
686 | : is_active() ? _labelfgcolor | 949 | : is_active() ? _labelfgcolor | |
687 | : fl_inactive(_labelfgcolor); | 950 | : fl_inactive(_labelfgcolor); | |
688 | Fl_Color bg = is_selected() ? is_active() ? tree->selection_color() | 951 | Fl_Color bg = is_selected() ? is_active() ? tree->selection_color() | |
689 | : fl_inactive(tree->selection_color()) | 952 | : fl_inactive(tree->selection_color()) | |
690 | : _labelbgcolor == 0xffffffff ? tree->color() // transparent bg? | 953 | : _labelbgcolor == 0xffffffff ? tree->color() // transparent bg? | |
691 | : _labelbgcolor; | 954 | : _labelbgcolor; | |
692 | // See if we should draw this item | 955 | // See if we should draw this item | |
693 | // If this item is root, and showroot() is disabled, don't draw. | 956 | // If this item is root, and showroot() is disabled, don't draw. | |
694 | // 'clipped' is an optimization to prevent drawing anything offscreen. | 957 | // 'clipped' is an optimization to prevent drawing anything offscreen. | |
695 | // | 958 | // | |
696 | if ( drawthis ) { // draw this item at all? | 959 | if ( drawthis ) { // draw this item at all? | |
697 | if ( tree->damage() & ~FL_DAMAGE_CHILD ) { // non-child damage? | 960 | if ( tree->damage() & ~FL_DAMAGE_CHILD ) { // non-child damage? | |
698 | // Draw connectors | 961 | // Draw connectors | |
699 | if ( prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { | 962 | if ( prefs.connectorstyle() != FL_TREE_CONNECTOR_NONE ) { | |
700 | // Horiz connector between center of icon and text | 963 | // Horiz connector between center of icon and text | |
701 | // if this is root, the connector should not dangle in thin air on the left | 964 | // if this is root, the connector should not dangle in thin air on the left | |
702 | if (is_root()) draw_horizontal_connector(hconn_x_center, hconn_x2, item_y_center, prefs); | 965 | if (is_root()) draw_horizontal_connector(hconn_x_center, hconn_x2, item_y_center, prefs); | |
703 | else draw_horizontal_connector(hconn_x, hconn_x2, item_y_center, prefs); | 966 | else draw_horizontal_connector(hconn_x, hconn_x2, item_y_center, prefs); | |
704 | // Small vertical line down to children | 967 | // Small vertical line down to children | |
705 | if ( has_children() && is_open() ) | 968 | if ( has_children() && is_open() ) | |
706 | draw_vertical_connector(hconn_x_center, item_y_center, Y+H2, prefs); | 969 | draw_vertical_connector(hconn_x_center, item_y_center, Y+H2, prefs); | |
707 | // Connectors for last child | 970 | // Connectors for last child | |
708 | if ( !is_root() ) { | 971 | if ( !is_root() ) { | |
709 | if ( lastchild ) draw_vertical_connector(hconn_x, Y, item_y_center, prefs); | 972 | if ( lastchild ) draw_vertical_connector(hconn_x, Y, item_y_center, prefs); | |
710 | else draw_vertical_connector(hconn_x, Y, Y+H2, prefs); | 973 | else draw_vertical_connector(hconn_x, Y, Y+H2, prefs); | |
711 | } | 974 | } | |
712 | } | 975 | } | |
713 | // Draw collapse icon | 976 | // Draw collapse icon | |
714 | if ( has_children() && prefs.showcollapse() ) { | 977 | if ( has_children() && prefs.showcollapse() ) { | |
715 | // Draw icon image | 978 | // Draw icon image | |
716 | if ( is_open() ) { | 979 | if ( is_open() ) { | |
717 | prefs.closeicon()->draw(icon_x,icon_y); | 980 | prefs.closeicon()->draw(icon_x,icon_y); | |
718 | } else { | 981 | } else { | |
719 | prefs.openicon()->draw(icon_x,icon_y); | 982 | prefs.openicon()->draw(icon_x,icon_y); | |
720 | } | 983 | } | |
721 | } | 984 | } | |
722 | // Draw the item | 985 | // Draw the item | |
723 | #if FLTK_ABI_VERSION >= 10303 | 986 | #if FLTK_ABI_VERSION >= 10303 | |
724 | if ( !widget() && prefs.item_draw_callback() ) { | 987 | if ( !widget() && prefs.item_draw_callback() ) { | |
725 | // Draw item using user supplied custom item draw callback | 988 | // Draw item using user supplied custom item draw callback | |
726 | prefs.do_item_draw_callback(this); | 989 | prefs.do_item_draw_callback(this); | |
727 | } | 990 | } | |
728 | else | 991 | else | |
729 | #endif | 992 | #endif | |
730 | { | 993 | { | |
731 | // Background for this item | 994 | // Background for this item | |
732 | // Draw bg only if different from tree's bg | 995 | // Draw bg only if different from tree's bg | |
733 | if ( bg != tree->color() || is_selected() ) { | 996 | if ( bg != tree->color() || is_selected() ) { | |
734 | if ( is_selected() ) { // Selected? Use selectbox() style | 997 | if ( is_selected() ) { // Selected? Use selectbox() style | |
735 | fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); | 998 | fl_draw_box(prefs.selectbox(),bg_x,bg_y,bg_w,bg_h,bg); | |
736 | } else { // Not Selected? use plain filled rectangle | 999 | } else { // Not Selected? use plain filled rectangle | |
737 | fl_color(bg); | 1000 | fl_color(bg); | |
738 | fl_rectf(bg_x,bg_y,bg_w,bg_h); | 1001 | fl_rectf(bg_x,bg_y,bg_w,bg_h); | |
739 | } | 1002 | } | |
740 | if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it | 1003 | if ( widget() ) widget()->damage(FL_DAMAGE_ALL); // if there's a child widget, we just damaged it | |
741 | } | 1004 | } | |
742 | // Draw user icon (if any) | 1005 | // Draw user icon (if any) | |
743 | if ( usericon() ) { | 1006 | if ( usericon() ) { | |
744 | // Item has user icon? Use it | 1007 | // Item has user icon? Use it | |
745 | int uicon_y = item_y_center - (usericon()->h() >> 1); | 1008 | int uicon_y = item_y_center - (usericon()->h() >> 1); | |
746 | usericon()->draw(uicon_x,uicon_y); | 1009 | usericon()->draw(uicon_x,uicon_y); | |
747 | } else if ( prefs.usericon() ) { | 1010 | } else if ( prefs.usericon() ) { | |
748 | // Prefs has user icon? Use it | 1011 | // Prefs has user icon? Use it | |
749 | int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); | 1012 | int uicon_y = item_y_center - (prefs.usericon()->h() >> 1); | |
750 | prefs.usericon()->draw(uicon_x,uicon_y); | 1013 | prefs.usericon()->draw(uicon_x,uicon_y); | |
751 | } | 1014 | } | |
752 | // Draw label | 1015 | // Draw label | |
753 | #if FLTK_ABI_VERSION >= 10301 | 1016 | #if FLTK_ABI_VERSION >= 10301 | |
754 | if ( _label && | 1017 | if ( _label && | |
755 | ( !widget() || | 1018 | ( !widget() || | |
756 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) | 1019 | (prefs.item_draw_mode() & FL_TREE_ITEM_DRAW_LABEL_AND_WIDGET) ) ) | |
757 | #else /*FLTK_ABI_VERSION*/ | 1020 | #else /*FLTK_ABI_VERSION*/ | |
758 | if ( _label && !widget() ) // back compat: don't draw label if widget() present | 1021 | if ( _label && !widget() ) // back compat: don't draw label if widget() present | |
759 | #endif /*FLTK_ABI_VERSION*/ | 1022 | #endif /*FLTK_ABI_VERSION*/ | |
760 | { | 1023 | { | |
761 | fl_color(fg); | 1024 | fl_color(fg); | |
762 | fl_font(_labelfont, _labelsize); | 1025 | fl_font(_labelfont, _labelsize); | |
763 | int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; | 1026 | int label_y = Y+(H/2)+(_labelsize/2)-fl_descent()/2; | |
764 | fl_draw(_label, label_x, label_y); | 1027 | fl_draw(_label, label_x, label_y); | |
765 | } | 1028 | } | |
766 | } // end non-custom draw | 1029 | } // end non-custom draw | |
767 | } // end non-child damage | 1030 | } // end non-child damage | |
768 | // Draw child FLTK widget? | 1031 | // Draw child FLTK widget? | |
769 | if ( widget() ) { | 1032 | if ( widget() ) { | |
770 | ((Fl_Tree*)tree)->draw_child(*widget()); // let group handle drawing child | 1033 | ((Fl_Tree*)tree)->draw_child(*widget()); // let group handle drawing child | |
771 | if ( widget()->label() ) | 1034 | if ( widget()->label() ) | |
772 | ((Fl_Tree*)tree)->draw_outside_label(*widget()); // label too | 1035 | ((Fl_Tree*)tree)->draw_outside_label(*widget()); // label too | |
773 | } | 1036 | } | |
774 | // Draw focus box around item's bg last | 1037 | // Draw focus box around item's bg last | |
775 | if ( this == itemfocus && | 1038 | if ( this == itemfocus && | |
776 | Fl::visible_focus() && | 1039 | Fl::visible_focus() && | |
777 | Fl::focus() == tree && | 1040 | Fl::focus() == tree && | |
778 | prefs.selectmode() != FL_TREE_SELECT_NONE ) { | 1041 | prefs.selectmode() != FL_TREE_SELECT_NONE ) { | |
779 | draw_item_focus(FL_NO_BOX,fg,bg,bg_x+1,bg_y+1,bg_w-1,bg_h-1); | 1042 | draw_item_focus(FL_NO_BOX,fg,bg,bg_x+1,bg_y+1,bg_w-1,bg_h-1); | |
780 | } | 1043 | } | |
781 | } // end drawthis | 1044 | } // end drawthis | |
782 | } // end clipped | 1045 | } // end clipped | |
783 | if ( drawthis ) Y += H2; // adjust Y (even if clipped) | 1046 | if ( drawthis ) Y += H2; // adjust Y (even if clipped) | |
784 | // Draw child items (if any) | 1047 | // Draw child items (if any) | |
785 | if ( has_children() && is_open() ) { | 1048 | if ( has_children() && is_open() ) { | |
786 | int child_x = drawthis ? (hconn_x_center - (icon_w/2) + 1) // offset children to right, | 1049 | int child_x = drawthis ? (hconn_x_center - (icon_w/2) + 1) // offset children to right, | |
787 | : X; // unless didn't drawthis | 1050 | : X; // unless didn't drawthis | |
788 | int child_w = W - (child_x-X); | 1051 | int child_w = W - (child_x-X); | |
789 | int child_y_start = Y; | 1052 | int child_y_start = Y; | |
790 | for ( int t=0; t<children(); t++ ) { | 1053 | for ( int t=0; t<children(); t++ ) { | |
791 | int lastchild = ((t+1)==children()) ? 1 : 0; | 1054 | int lastchild = ((t+1)==children()) ? 1 : 0; | |
792 | _children[t]->draw(child_x, Y, child_w, tree, itemfocus, prefs, lastchild); | 1055 | _children[t]->draw(child_x, Y, child_w, tree, itemfocus, prefs, lastchild); | |
793 | } | 1056 | } | |
794 | if ( has_children() && is_open() ) { | 1057 | if ( has_children() && is_open() ) { | |
795 | Y += prefs.openchild_marginbottom(); // offset below open child tree | 1058 | Y += prefs.openchild_marginbottom(); // offset below open child tree | |
796 | } | 1059 | } | |
797 | if ( ! lastchild ) { | 1060 | if ( ! lastchild ) { | |
798 | // Special 'clipped' calculation. (intentional variable shadowing) | 1061 | // Special 'clipped' calculation. (intentional variable shadowing) | |
799 | int clipped = ((child_y_start < tree_top) && (Y < tree_top)) || | 1062 | int clipped = ((child_y_start < tree_top) && (Y < tree_top)) || | |
800 | ((child_y_start > tree_bot) && (Y > tree_bot)); | 1063 | ((child_y_start > tree_bot) && (Y > tree_bot)); | |
801 | if (!clipped) draw_vertical_connector(hconn_x, child_y_start, Y, prefs); | 1064 | if (!clipped) draw_vertical_connector(hconn_x, child_y_start, Y, prefs); | |
802 | } | 1065 | } | |
803 | } | 1066 | } | |
804 | } | 1067 | } | |
1068 | #endif | |||
805 | 1069 | |||
806 | /// Was the event on the 'collapse' button? | 1070 | /// Was the event on the 'collapse' button? | |
807 | /// | 1071 | /// | |
808 | int Fl_Tree_Item::event_on_collapse_icon(const Fl_Tree_Prefs &prefs) const { | 1072 | int Fl_Tree_Item::event_on_collapse_icon(const Fl_Tree_Prefs &prefs) const { | |
809 | if ( is_visible() && is_active() && has_children() && prefs.showcollapse() ) { | 1073 | if ( is_visible() && is_active() && has_children() && prefs.showcollapse() ) { | |
810 | return(event_inside(_collapse_xywh) ? 1 : 0); | 1074 | return(event_inside(_collapse_xywh) ? 1 : 0); | |
811 | } else { | 1075 | } else { | |
812 | return(0); | 1076 | return(0); | |
813 | } | 1077 | } | |
814 | } | 1078 | } | |
815 | 1079 | |||
816 | /// Was event on the label()? | 1080 | /// Was event on the label()? | |
817 | /// | 1081 | /// | |
818 | int Fl_Tree_Item::event_on_label(const Fl_Tree_Prefs &prefs) const { | 1082 | int Fl_Tree_Item::event_on_label(const Fl_Tree_Prefs &prefs) const { | |
819 | if ( is_visible() && is_active() ) { | 1083 | if ( is_visible() && is_active() ) { | |
820 | return(event_inside(_label_xywh) ? 1 : 0); | 1084 | return(event_inside(_label_xywh) ? 1 : 0); | |
821 | } else { | 1085 | } else { | |
822 | return(0); | 1086 | return(0); | |
823 | } | 1087 | } | |
824 | } | 1088 | } | |
825 | 1089 | |||
826 | /// Internal: Show the FLTK widget() for this item and all children. | 1090 | /// Internal: Show the FLTK widget() for this item and all children. | |
827 | /// Used by open() to re-show widgets that were hidden by a previous close() | 1091 | /// Used by open() to re-show widgets that were hidden by a previous close() | |
828 | /// | 1092 | /// | |
829 | void Fl_Tree_Item::show_widgets() { | 1093 | void Fl_Tree_Item::show_widgets() { | |
830 | if ( _widget ) _widget->show(); | 1094 | if ( _widget ) _widget->show(); | |
831 | if ( is_open() ) { | 1095 | if ( is_open() ) { | |
832 | for ( int t=0; t<_children.total(); t++ ) { | 1096 | for ( int t=0; t<_children.total(); t++ ) { | |
833 | _children[t]->show_widgets(); | 1097 | _children[t]->show_widgets(); | |
834 | } | 1098 | } | |
835 | } | 1099 | } | |
836 | } | 1100 | } | |
837 | 1101 | |||
838 | /// Internal: Hide the FLTK widget() for this item and all children. | 1102 | /// Internal: Hide the FLTK widget() for this item and all children. | |
839 | /// Used by close() to hide widgets. | 1103 | /// Used by close() to hide widgets. | |
840 | /// | 1104 | /// | |
841 | void Fl_Tree_Item::hide_widgets() { | 1105 | void Fl_Tree_Item::hide_widgets() { | |
842 | if ( _widget ) _widget->hide(); | 1106 | if ( _widget ) _widget->hide(); | |
843 | for ( int t=0; t<_children.total(); t++ ) { | 1107 | for ( int t=0; t<_children.total(); t++ ) { | |
844 | _children[t]->hide_widgets(); | 1108 | _children[t]->hide_widgets(); | |
845 | } | 1109 | } | |
846 | } | 1110 | } | |
847 | 1111 | |||
848 | /// Open this item and all its children. | 1112 | /// Open this item and all its children. | |
849 | void Fl_Tree_Item::open() { | 1113 | void Fl_Tree_Item::open() { | |
850 | set_flag(OPEN,1); | 1114 | set_flag(OPEN,1); | |
851 | // Tell children to show() their widgets | 1115 | // Tell children to show() their widgets | |
852 | for ( int t=0; t<_children.total(); t++ ) { | 1116 | for ( int t=0; t<_children.total(); t++ ) { | |
853 | _children[t]->show_widgets(); | 1117 | _children[t]->show_widgets(); | |
854 | } | 1118 | } | |
1119 | recalc_tree(); // may change tree geometry | |||
855 | } | 1120 | } | |
856 | 1121 | |||
857 | /// Close this item and all its children. | 1122 | /// Close this item and all its children. | |
858 | void Fl_Tree_Item::close() { | 1123 | void Fl_Tree_Item::close() { | |
859 | set_flag(OPEN,0); | 1124 | set_flag(OPEN,0); | |
860 | // Tell children to hide() their widgets | 1125 | // Tell children to hide() their widgets | |
861 | for ( int t=0; t<_children.total(); t++ ) { | 1126 | for ( int t=0; t<_children.total(); t++ ) { | |
862 | _children[t]->hide_widgets(); | 1127 | _children[t]->hide_widgets(); | |
863 | } | 1128 | } | |
1129 | recalc_tree(); // may change tree geometry | |||
864 | } | 1130 | } | |
865 | 1131 | |||
866 | /// Returns how many levels deep this item is in the hierarchy. | 1132 | /// Returns how many levels deep this item is in the hierarchy. | |
867 | /// | 1133 | /// | |
868 | /// For instance; root has a depth of zero, and its immediate children | 1134 | /// For instance; root has a depth of zero, and its immediate children | |
869 | /// would have a depth of 1, and so on. | 1135 | /// would have a depth of 1, and so on. | |
870 | /// | 1136 | /// | |
871 | int Fl_Tree_Item::depth() const { | 1137 | int Fl_Tree_Item::depth() const { | |
872 | int count = 0; | 1138 | int count = 0; | |
873 | const Fl_Tree_Item *item = parent(); | 1139 | const Fl_Tree_Item *item = parent(); | |
874 | while ( item ) { | 1140 | while ( item ) { | |
875 | ++count; | 1141 | ++count; | |
876 | item = item->parent(); | 1142 | item = item->parent(); | |
877 | } | 1143 | } | |
878 | return(count); | 1144 | return(count); | |
879 | } | 1145 | } | |
880 | 1146 | |||
881 | /// Return the next item in the tree. | 1147 | /// Return the next item in the tree. | |
882 | /// | 1148 | /// | |
883 | /// This method can be used to walk the tree forward. | 1149 | /// This method can be used to walk the tree forward. | |
884 | /// For an example of how to use this method, see Fl_Tree::first(). | 1150 | /// For an example of how to use this method, see Fl_Tree::first(). | |
885 | /// | 1151 | /// | |
886 | /// \returns the next item in the tree, or 0 if there's no more items. | 1152 | /// \returns the next item in the tree, or 0 if there's no more items. | |
887 | /// | 1153 | /// | |
888 | Fl_Tree_Item *Fl_Tree_Item::next() { | 1154 | Fl_Tree_Item *Fl_Tree_Item::next() { | |
889 | Fl_Tree_Item *p, *c = this; | 1155 | Fl_Tree_Item *p, *c = this; | |
890 | if ( c->has_children() ) { | 1156 | if ( c->has_children() ) { | |
891 | return(c->child(0)); | 1157 | return(c->child(0)); | |
892 | } | 1158 | } | |
893 | #if FLTK_ABI_VERSION >= 10301 | 1159 | #if FLTK_ABI_VERSION >= 10301 | |
894 | // NEW | 1160 | // NEW | |
895 | while ( ( p = c->parent() ) != NULL ) { // loop upwards through parents | 1161 | while ( ( p = c->parent() ) != NULL ) { // loop upwards through parents | |
896 | if ( c->_next_sibling ) // not last child? | 1162 | if ( c->_next_sibling ) // not last child? | |
897 | return(c->_next_sibling); // return next child | 1163 | return(c->_next_sibling); // return next child | |
898 | c = p; // child becomes parent to move up generation | 1164 | c = p; // child becomes parent to move up generation | |
899 | } // loop: moves up to next parent | 1165 | } // loop: moves up to next parent | |
900 | #else /*FLTK_ABI_VERSION*/ | 1166 | #else /*FLTK_ABI_VERSION*/ | |
901 | // OLD | 1167 | // OLD | |
902 | while ( ( p = c->parent() ) != NULL ) { // loop upwards through parents | 1168 | while ( ( p = c->parent() ) != NULL ) { // loop upwards through parents | |
903 | int t = p->find_child(c); // find our position in parent's children[] array | 1169 | int t = p->find_child(c); // find our position in parent's children[] array | |
904 | if ( ++t < p->children() ) // not last child? | 1170 | if ( ++t < p->children() ) // not last child? | |
905 | return(p->child(t)); // return next child | 1171 | return(p->child(t)); // return next child | |
906 | c = p; // child becomes parent to move up generation | 1172 | c = p; // child becomes parent to move up generation | |
907 | } // loop: moves up to next parent | 1173 | } // loop: moves up to next parent | |
908 | #endif /*FLTK_ABI_VERSION*/ | 1174 | #endif /*FLTK_ABI_VERSION*/ | |
909 | return(0); // hit root? done | 1175 | return(0); // hit root? done | |
910 | } | 1176 | } | |
911 | 1177 | |||
912 | /// Return the previous item in the tree. | 1178 | /// Return the previous item in the tree. | |
913 | /// | 1179 | /// | |
914 | /// This method can be used to walk the tree backwards. | 1180 | /// This method can be used to walk the tree backwards. | |
915 | /// For an example of how to use this method, see Fl_Tree::last(). | 1181 | /// For an example of how to use this method, see Fl_Tree::last(). | |
916 | /// | 1182 | /// | |
917 | /// \returns the previous item in the tree, or 0 if there's no item above this one (hit the root). | 1183 | /// \returns the previous item in the tree, or 0 if there's no item above this one (hit the root). | |
918 | /// | 1184 | /// | |
919 | Fl_Tree_Item *Fl_Tree_Item::prev() { | 1185 | Fl_Tree_Item *Fl_Tree_Item::prev() { | |
920 | #if FLTK_ABI_VERSION >= 10301 | 1186 | #if FLTK_ABI_VERSION >= 10301 | |
921 | // NEW | 1187 | // NEW | |
922 | if ( !parent() ) return(0); // hit root? done | 1188 | if ( !parent() ) return(0); // hit root? done | |
923 | if ( !_prev_sibling ) { // are we first child? | 1189 | if ( !_prev_sibling ) { // are we first child? | |
924 | return(parent()); // return parent | 1190 | return(parent()); // return parent | |
925 | } | 1191 | } | |
926 | // Tricky: in the following example our current position | 1192 | // Tricky: in the following example our current position | |
927 | // in the tree is 'j', and we need to move "up one" to 'i': | 1193 | // in the tree is 'j', and we need to move "up one" to 'i': | |
928 | // | 1194 | // | |
929 | // ROOT | 1195 | // ROOT | |
930 | // |-- a | 1196 | // |-- a | |
931 | // b-- c | 1197 | // b-- c | |
932 | // | d-- e | 1198 | // | d-- e | |
933 | // | | f | 1199 | // | | f | |
934 | // | | | 1200 | // | | | |
935 | // | g-- h | 1201 | // | g-- h | |
936 | // | i | 1202 | // | i | |
937 | // j | 1203 | // j | |
938 | // | 1204 | // | |
939 | // We do this via b->g->i: | 1205 | // We do this via b->g->i: | |
940 | // 1. Find j's prev_sibling (b) _ | 1206 | // 1. Find j's prev_sibling (b) _ | |
941 | // 2. Find b's 'last child' (g) |_ while loop | 1207 | // 2. Find b's 'last child' (g) |_ while loop | |
942 | // 3. Find g's 'last child' (i) _| | 1208 | // 3. Find g's 'last child' (i) _| | |
943 | // | 1209 | // | |
944 | Fl_Tree_Item *p = _prev_sibling; // focus on our prev sibling | 1210 | Fl_Tree_Item *p = _prev_sibling; // focus on our prev sibling | |
945 | while ( p->has_children() ) { // item has children? | 1211 | while ( p->has_children() ) { // item has children? | |
946 | p = p->child(p->children()-1); // descend hierarchy finding deepest 'last child' | 1212 | p = p->child(p->children()-1); // descend hierarchy finding deepest 'last child' | |
947 | } | 1213 | } | |
948 | return(p); | 1214 | return(p); | |
949 | #else /*FLTK_ABI_VERSION*/ | 1215 | #else /*FLTK_ABI_VERSION*/ | |
950 | // OLD | 1216 | // OLD | |
951 | Fl_Tree_Item *p=parent(); // start with parent | 1217 | Fl_Tree_Item *p=parent(); // start with parent | |
952 | if ( ! p ) return(0); // hit root? done | 1218 | if ( ! p ) return(0); // hit root? done | |
953 | int t = p->find_child(this); // find our position in parent's children[] array | 1219 | int t = p->find_child(this); // find our position in parent's children[] array | |
954 | if ( --t == -1 ) { // are we first child? | 1220 | if ( --t == -1 ) { // are we first child? | |
955 | return(p); // return immediate parent | 1221 | return(p); // return immediate parent | |
956 | } | 1222 | } | |
957 | p = p->child(t); // take parent's previous child | 1223 | p = p->child(t); // take parent's previous child | |
958 | while ( p->has_children() ) { // has children? | 1224 | while ( p->has_children() ) { // has children? | |
959 | p = p->child(p->children()-1); // take last child | 1225 | p = p->child(p->children()-1); // take last child | |
960 | } | 1226 | } | |
961 | return(p); | 1227 | return(p); | |
962 | #endif /*FLTK_ABI_VERSION*/ | 1228 | #endif /*FLTK_ABI_VERSION*/ | |
963 | } | 1229 | } | |
964 | 1230 | |||
965 | /// Return this item's next sibling. | 1231 | /// Return this item's next sibling. | |
966 | /// | 1232 | /// | |
967 | /// Moves to the next item below us at the same level (sibling). | 1233 | /// Moves to the next item below us at the same level (sibling). | |
968 | /// Use this to move down the tree without moving deeper into the tree, | 1234 | /// Use this to move down the tree without moving deeper into the tree, | |
969 | /// effectively skipping over this item's children/descendents. | 1235 | /// effectively skipping over this item's children/descendents. | |
970 | /// | 1236 | /// | |
971 | /// \returns item's next sibling, or 0 if none. | 1237 | /// \returns item's next sibling, or 0 if none. | |
972 | /// | 1238 | /// | |
973 | Fl_Tree_Item *Fl_Tree_Item::next_sibling() { | 1239 | Fl_Tree_Item *Fl_Tree_Item::next_sibling() { | |
974 | #if FLTK_ABI_VERSION >= 10301 | 1240 | #if FLTK_ABI_VERSION >= 10301 | |
975 | // NEW | 1241 | // NEW | |
976 | return(_next_sibling); | 1242 | return(_next_sibling); | |
977 | #else /*FLTK_ABI_VERSION*/ | 1243 | #else /*FLTK_ABI_VERSION*/ | |
978 | // OLD | 1244 | // OLD | |
979 | if ( !parent() ) return(0); // No parent (root)? We have no siblings | 1245 | if ( !parent() ) return(0); // No parent (root)? We have no siblings | |
980 | int index = parent()->find_child(this); // find our position in parent's child() array | 1246 | int index = parent()->find_child(this); // find our position in parent's child() array | |
981 | if ( index == -1 ) return(0); // parent doesn't know us? weird | 1247 | if ( index == -1 ) return(0); // parent doesn't know us? weird | |
982 | if ( (index+1) < parent()->children() ) // is there a next child? | 1248 | if ( (index+1) < parent()->children() ) // is there a next child? | |
983 | return(parent()->child(index+1)); // return next child if there's one below us | 1249 | return(parent()->child(index+1)); // return next child if there's one below us | |
984 | return(0); // no siblings below us | 1250 | return(0); // no siblings below us | |
985 | #endif /*FLTK_ABI_VERSION*/ | 1251 | #endif /*FLTK_ABI_VERSION*/ | |
986 | } | 1252 | } | |
987 | 1253 | |||
988 | /// Return this item's previous sibling. | 1254 | /// Return this item's previous sibling. | |
989 | /// | 1255 | /// | |
990 | /// Moves to the previous item above us at the same level (sibling). | 1256 | /// Moves to the previous item above us at the same level (sibling). | |
991 | /// Use this to move up the tree without moving deeper into the tree. | 1257 | /// Use this to move up the tree without moving deeper into the tree. | |
992 | /// | 1258 | /// | |
993 | /// \returns This item's previous sibling, or 0 if none. | 1259 | /// \returns This item's previous sibling, or 0 if none. | |
994 | /// | 1260 | /// | |
995 | Fl_Tree_Item *Fl_Tree_Item::prev_sibling() { | 1261 | Fl_Tree_Item *Fl_Tree_Item::prev_sibling() { | |
996 | #if FLTK_ABI_VERSION >= 10301 | 1262 | #if FLTK_ABI_VERSION >= 10301 | |
997 | // NEW | 1263 | // NEW | |
998 | return(_prev_sibling); | 1264 | return(_prev_sibling); | |
999 | #else /*FLTK_ABI_VERSION*/ | 1265 | #else /*FLTK_ABI_VERSION*/ | |
1000 | // OLD | 1266 | // OLD | |
1001 | if ( !parent() ) return(0); // No parent (root)? We have no siblings | 1267 | if ( !parent() ) return(0); // No parent (root)? We have no siblings | |
1002 | int index = parent()->find_child(this); // find next position up in parent's child() array | 1268 | int index = parent()->find_child(this); // find next position up in parent's child() array | |
1003 | if ( index == -1 ) return(0); // parent doesn't know us? weird | 1269 | if ( index == -1 ) return(0); // parent doesn't know us? weird | |
1004 | if ( index > 0 ) return(parent()->child(index-1)); // return previous child if there's one above us | 1270 | if ( index > 0 ) return(parent()->child(index-1)); // return previous child if there's one above us | |
1005 | return(0); // no siblings above us | 1271 | return(0); // no siblings above us | |
1006 | #endif /*FLTK_ABI_VERSION*/ | 1272 | #endif /*FLTK_ABI_VERSION*/ | |
1007 | } | 1273 | } | |
1008 | 1274 | |||
1009 | /// Update our _prev_sibling and _next_sibling pointers to point to neighbors, | 1275 | /// Update our _prev_sibling and _next_sibling pointers to point to neighbors, | |
1010 | /// given \p index as being our current position in the parent's item array. | 1276 | /// given \p index as being our current position in the parent's item array. | |
1011 | /// Call this whenever items in the array are added/removed/moved/swapped. | 1277 | /// Call this whenever items in the array are added/removed/moved/swapped. | |
1012 | /// | 1278 | /// | |
1013 | void Fl_Tree_Item::update_prev_next(int index) { | 1279 | void Fl_Tree_Item::update_prev_next(int index) { | |
1014 | #if FLTK_ABI_VERSION >= 10301 | 1280 | #if FLTK_ABI_VERSION >= 10301 | |
1015 | // NEW | 1281 | // NEW | |
1016 | int pchildren = parent() ? parent()->children() : 0; | 1282 | int pchildren = parent() ? parent()->children() : 0; | |
1017 | int index_prev = index-1; | 1283 | int index_prev = index-1; | |
1018 | int index_next = index+1; | 1284 | int index_next = index+1; | |
1019 | // Get pointers to prev+next items | 1285 | // Get pointers to prev+next items | |
1020 | Fl_Tree_Item *item_prev = (index_prev>=0)&&(index_prev<pchildren) ? parent()->child(index_prev) : 0; | 1286 | Fl_Tree_Item *item_prev = (index_prev>=0)&&(index_prev<pchildren) ? parent()->child(index_prev) : 0; | |
1021 | Fl_Tree_Item *item_next = (index_next>=0)&&(index_next<pchildren) ? parent()->child(index_next) : 0; | 1287 | Fl_Tree_Item *item_next = (index_next>=0)&&(index_next<pchildren) ? parent()->child(index_next) : 0; | |
1022 | // Adjust our prev+next ptrs | 1288 | // Adjust our prev+next ptrs | |
1023 | _prev_sibling = item_prev; | 1289 | _prev_sibling = item_prev; | |
1024 | _next_sibling = item_next; | 1290 | _next_sibling = item_next; | |
1025 | // Adjust neighbors to point to us | 1291 | // Adjust neighbors to point to us | |
1026 | if ( item_prev ) item_prev->_next_sibling = this; | 1292 | if ( item_prev ) item_prev->_next_sibling = this; | |
1027 | if ( item_next ) item_next->_prev_sibling = this; | 1293 | if ( item_next ) item_next->_prev_sibling = this; | |
1028 | #else /*FLTK_ABI_VERSION*/ | 1294 | #else /*FLTK_ABI_VERSION*/ | |
1029 | // OLD | 1295 | // OLD | |
1030 | // -- does nothing -- | 1296 | // -- does nothing -- | |
1031 | #endif /*FLTK_ABI_VERSION*/ | 1297 | #endif /*FLTK_ABI_VERSION*/ | |
1032 | } | 1298 | } | |
1033 | 1299 | |||
1034 | /// Return the next visible item. (If this item has children and is closed, children are skipped) | 1300 | /// Return the next open, visible item. | |
1301 | /// (If this item has children and is closed, children are skipped) | |||
1035 | /// | 1302 | /// | |
1036 | /// This method can be used to walk the tree forward, skipping items | 1303 | /// This method can be used to walk the tree forward, skipping items | |
1037 | /// that are not currently visible to the user. | 1304 | /// that are not currently open/visible to the user. | |
1038 | /// | 1305 | /// | |
1039 | /// \returns the next visible item below us, or 0 if there's no more items. | 1306 | /// \returns the next open visible item below us, or 0 if there's no more items. | |
1040 | /// | 1307 | /// | |
1041 | Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) { | 1308 | Fl_Tree_Item *Fl_Tree_Item::next_displayed(Fl_Tree_Prefs &prefs) { | |
1042 | Fl_Tree_Item *item = this; | 1309 | Fl_Tree_Item *item = this; | |
1043 | while ( 1 ) { | 1310 | while ( 1 ) { | |
1044 | item = item->next(); | 1311 | item = item->next(); | |
1045 | if ( !item ) return 0; | 1312 | if ( !item ) return 0; | |
1046 | if ( item->is_root() && !prefs.showroot() ) continue; | 1313 | if ( item->is_root() && !prefs.showroot() ) continue; | |
1047 | if ( item->visible_r() ) return(item); | 1314 | if ( item->visible_r() ) return(item); | |
1048 | } | 1315 | } | |
1049 | } | 1316 | } | |
1050 | 1317 | |||
1051 | /// Return the previous visible item. (If this item above us has children and is closed, its children are skipped) | 1318 | /// Return the previous open, visible item. | |
1319 | /// (If this item above us has children and is closed, its children are skipped) | |||
1052 | /// | 1320 | /// | |
1053 | /// This method can be used to walk the tree backward, | 1321 | /// This method can be used to walk the tree backward, | |
1054 | /// skipping items that are not currently visible to the user. | 1322 | /// skipping items that are not currently open/visible to the user. | |
1055 | /// | 1323 | /// | |
1056 | /// \returns the previous visible item above us, or 0 if there's no more items. | 1324 | /// \returns the previous open visible item above us, or 0 if there's no more items. | |
1057 | /// | 1325 | /// | |
1058 | Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) { | 1326 | Fl_Tree_Item *Fl_Tree_Item::prev_displayed(Fl_Tree_Prefs &prefs) { | |
1059 | Fl_Tree_Item *c = this; | 1327 | Fl_Tree_Item *c = this; | |
1060 | while ( c ) { | 1328 | while ( c ) { | |
1061 | c = c->prev(); // previous item | 1329 | c = c->prev(); // previous item | |
1062 | if ( !c ) break; // no more items? done | 1330 | if ( !c ) break; // no more items? done | |
1063 | if ( c->is_root() ) // root | 1331 | if ( c->is_root() ) // root | |
1064 | return((prefs.showroot()&&c->visible()) ? c : 0); // return root if visible | 1332 | return((prefs.showroot()&&c->visible()) ? c : 0); // return root if visible | |
1065 | if ( !c->visible() ) continue; // item not visible? skip | 1333 | if ( !c->visible() ) continue; // item not visible? skip | |
1066 | // Check all parents to be sure none are closed. | 1334 | // Check all parents to be sure none are closed. | |
1067 | // If closed, move up to that level and repeat until sure none are closed. | 1335 | // If closed, move up to that level and repeat until sure none are closed. | |
1068 | Fl_Tree_Item *p = c->parent(); | 1336 | Fl_Tree_Item *p = c->parent(); | |
1069 | while (1) { | 1337 | while (1) { | |
1070 | if ( !p || p->is_root() ) return(c); // hit top? then we're displayed, return c | 1338 | if ( !p || p->is_root() ) return(c); // hit top? then we're displayed, return c | |
1071 | if ( p->is_close() ) c = p; // found closed parent? make it current | 1339 | if ( p->is_close() ) c = p; // found closed parent? make it current | |
1072 | p = p->parent(); // continue up tree | 1340 | p = p->parent(); // continue up tree | |
1073 | } | 1341 | } | |
1074 | } | 1342 | } | |
1075 | return(0); // hit end: no more items | 1343 | return(0); // hit end: no more items | |
1076 | } | 1344 | } | |
1077 | 1345 | |||
1078 | /// Returns if item and all its parents are visible. | 1346 | /// Returns if item and all its parents are visible. | |
1079 | /// Also takes into consideration if any parent is close()ed. | 1347 | /// Also takes into consideration if any parent is close()ed. | |
1080 | /// \returns | 1348 | /// \returns | |
1081 | /// 1 -- item and its parents are visible/open() | 1349 | /// 1 -- item and its parents are visible/open() | |
1082 | /// 0 -- item (or parents) invisible or close()ed. | 1350 | /// 0 -- item (or parents) invisible or close()ed. | |
1083 | /// | 1351 | /// | |
1084 | int Fl_Tree_Item::visible_r() const { | 1352 | int Fl_Tree_Item::visible_r() const { | |
1085 | if ( !visible() ) return(0); | 1353 | if ( !visible() ) return(0); | |
1086 | for (const Fl_Tree_Item *p=parent(); p; p=p->parent())// move up through parents | 1354 | for (const Fl_Tree_Item *p=parent(); p; p=p->parent())// move up through parents | |
1087 | if (!p->visible() || p->is_close()) return(0); // any parent not visible or closed? | 1355 | if (!p->visible() || p->is_close()) return(0); // any parent not visible or closed? | |
1088 | return(1); | 1356 | return(1); | |
1089 | } | 1357 | } | |
1090 | 1358 | |||
1359 | /// Call this when our geometry is changed. (Font size, label contents, etc) | |||
1360 | /// Schedules tree to recalculate itself, as changes to us may affect tree widget's | |||
1361 | /// scrollbar visibility and tab sizes. | |||
1362 | /// | |||
1363 | void Fl_Tree_Item::recalc_tree() { | |||
1364 | #if FLTK_ABI_VERSION >= 10303 | |||
1365 | _tree->recalc_tree(); | |||
1366 | #endif | |||
1367 | } | |||
1368 | ||||
1091 | // | 1369 | // | |
1092 | // End of "$Id: Fl_Tree_Item.cxx 10018 2013-11-10 04:33:44Z greg.ercolano $". | 1370 | // End of "$Id: Fl_Tree_Item.cxx 10018 2013-11-10 04:33:44Z greg.ercolano $". | |
1093 | // | 1371 | // |