1 
2 /*
3  * bltTreeView.c --
4  *
5  *	This module implements an hierarchy widget for the BLT toolkit.
6  *
7  * Copyright 1998-1999 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies or any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  *
27  *	The "treeview" widget was created by George A. Howlett.
28  *      Extensive cleanups and enhancements by Peter MacDonald.
29  *
30  * Derived from the blt2.5 sources from sourceforge which
31  * according to George, uses the BSD license.
32  */
33 
34 #include "bltInt.h"
35 
36 #ifndef NO_TREEVIEW
37 
38 #include "bltTreeView.h"
39 
40 #define BUTTON_PAD		2
41 #define BUTTON_IPAD		1
42 #define BUTTON_SIZE		7
43 #define COLUMN_PAD		2
44 #define FOCUS_WIDTH		1
45 #define ICON_PADX		2
46 #define ICON_PADY		1
47 #define LABEL_PADX		3
48 #define LABEL_PADY		0
49 
50 #include <X11/Xutil.h>
51 #include <X11/Xatom.h>
52 
53 #define DEF_ICON_WIDTH		8
54 #define DEF_ICON_HEIGHT		8
55 
56 static Blt_TreeApplyProc DeleteApplyProc;
57 static Blt_TreeApplyProc CreateApplyProc;
58 
59 static Blt_OptionParseProc ObjToTree;
60 static Blt_OptionPrintProc TreeToObj;
61 static Blt_OptionFreeProc FreeTree;
62 Blt_CustomOption bltTreeViewTreeOption =
63 {
64     ObjToTree, TreeToObj, FreeTree, NULL,
65 };
66 
67 static Blt_OptionParseProc ObjToIcons;
68 static Blt_OptionPrintProc IconsToObj;
69 static Blt_OptionFreeProc FreeIcons;
70 
71 Blt_CustomOption bltTreeViewIconsOption =
72 {
73     /* Contains a pointer to the widget that's currently being
74      * configured.  This is used in the custom configuration parse
75      * routine for icons.  */
76     ObjToIcons, IconsToObj, FreeIcons, NULL,
77 };
78 
79 
80 static Blt_OptionParseProc ObjToIsopen;
81 static Blt_OptionPrintProc IsopenToObj;
82 
83 Blt_CustomOption bltTreeViewIsOpenOption = {
84     ObjToIsopen, IsopenToObj, NULL, NULL,
85 };
86 
87 static Blt_OptionParseProc ObjToButton;
88 static Blt_OptionPrintProc ButtonToObj;
89 static Blt_CustomOption buttonOption = {
90     ObjToButton, ButtonToObj, NULL, NULL,
91 };
92 
93 static Blt_OptionParseProc ObjToUid;
94 static Blt_OptionPrintProc UidToObj;
95 static Blt_OptionFreeProc FreeUid;
96 Blt_CustomOption bltTreeViewUidOption = {
97     ObjToUid, UidToObj, FreeUid, NULL,
98 };
99 
100 static Blt_OptionParseProc ObjToScrollmode;
101 static Blt_OptionPrintProc ScrollmodeToObj;
102 static Blt_CustomOption scrollmodeOption = {
103     ObjToScrollmode, ScrollmodeToObj, NULL, NULL,
104 };
105 
106 static Blt_OptionParseProc ObjToSelectmode;
107 static Blt_OptionPrintProc SelectmodeToObj;
108 static Blt_CustomOption selectmodeOption = {
109     ObjToSelectmode, SelectmodeToObj, NULL, NULL,
110 };
111 
112 static Blt_OptionParseProc ObjToSeparator;
113 static Blt_OptionPrintProc SeparatorToObj;
114 static Blt_OptionFreeProc FreeSeparator;
115 static Blt_CustomOption separatorOption = {
116     ObjToSeparator, SeparatorToObj, FreeSeparator, NULL,
117 };
118 
119 static Blt_OptionParseProc ObjToLabel;
120 static Blt_OptionPrintProc LabelToObj;
121 static Blt_OptionFreeProc FreeLabel;
122 Blt_CustomOption bltTreeViewLabelOption =
123 {
124     ObjToLabel, LabelToObj, FreeLabel, NULL,
125 };
126 
127 
128 #define DEF_BUTTON_ACTIVE_BACKGROUND	RGB_WHITE
129 #define DEF_BUTTON_ACTIVE_BG_MONO	STD_ACTIVE_BG_MONO
130 #define DEF_BUTTON_ACTIVE_FOREGROUND	STD_ACTIVE_FOREGROUND
131 #define DEF_BUTTON_ACTIVE_FG_MONO	STD_ACTIVE_FG_MONO
132 #define DEF_BUTTON_BORDERWIDTH		"1"
133 #if (TK_MAJOR_VERSION == 4)
134 #define DEF_BUTTON_CLOSE_RELIEF		"flat"
135 #define DEF_BUTTON_OPEN_RELIEF		"flat"
136 #else
137 #define DEF_BUTTON_CLOSE_RELIEF		"solid"
138 #define DEF_BUTTON_OPEN_RELIEF		"solid"
139 #endif
140 #define DEF_BUTTON_NORMAL_BACKGROUND	RGB_WHITE
141 #define DEF_BUTTON_NORMAL_BG_MONO	STD_NORMAL_BG_MONO
142 #define DEF_BUTTON_NORMAL_FOREGROUND	STD_NORMAL_FOREGROUND
143 #define DEF_BUTTON_NORMAL_FG_MONO	STD_NORMAL_FG_MONO
144 #define DEF_BUTTON_SIZE			"7"
145 
146 /* RGB_LIGHTBLUE1 */
147 
148 #define DEF_TV_ACT_COL_SHOW "False"
149 #define DEF_TV_ACT_ENTRY_SHOW "False"
150 #define DEF_TV_ACTIVE_FOREGROUND	"black"
151 #define DEF_TV_ACTIVE_ICONS (char*)NULL
152 #define DEF_TV_ACTIVE_LEAFICONS (char*)NULL
153 #define DEF_TV_ACTIVE_RELIEF	"flat"
154 #define DEF_TV_ACTIVE_STIPPLE	"gray25"
155 #define DEF_TV_ALLOW_DUPLICATES	"yes"
156 #define DEF_TV_BACKGROUND	"white"
157 #define DEF_TV_BORDERWIDTH	STD_BORDERWIDTH
158 #define DEF_TV_BUTTON		"auto"
159 #define DEF_TV_DASHES		"dot"
160 #define DEF_TV_EXPORT_SELECTION	"no"
161 #define DEF_TV_FOREGROUND		STD_NORMAL_FOREGROUND
162 #define DEF_TV_FG_MONO		STD_NORMAL_FG_MONO
163 #define DEF_TV_FILL_NULL	"yes"
164 #define DEF_TV_FLAT		"no"
165 #define DEF_TV_FOCUS_DASHES	"dot"
166 #define DEF_TV_FOCUS_EDIT	"no"
167 #define DEF_TV_FOCUS_FOREGROUND	STD_ACTIVE_FOREGROUND
168 #define DEF_TV_FOCUS_FG_MONO	STD_ACTIVE_FG_MONO
169 #define DEF_TV_FONT		"Helvetica -12"
170 #define DEF_TV_TITLEFONT	"Helvetica -12 bold"
171 #define DEF_TV_HEIGHT		"200"
172 #define DEF_TV_HIDE_LEAVES	"no"
173 #define DEF_TV_HIDE_ICONS	"no"
174 #define DEF_TV_HIDE_STYLE_ICONS	"no"
175 #define DEF_TV_HIDE_STYLE_TEXT	"no"
176 #define DEF_TV_HIDE_ROOT	"yes"
177 #define DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND	STD_NORMAL_BACKGROUND
178 #define DEF_TV_FOCUS_HIGHLIGHT_COLOR		"black"
179 #define DEF_TV_FOCUS_HIGHLIGHT_WIDTH		"2"
180 #define DEF_TV_ICONS "blt::tv::normalOpenFolder blt::tv::normalCloseFolder"
181 #define DEF_TV_LEAFICONS  "blt::tv::empty"
182 #define DEF_TV_VLINE_COLOR	RGB_GREY50
183 #define DEF_TV_VLINE_MONO	STD_NORMAL_FG_MONO
184 #define DEF_TV_INLINEIMG "yes"
185 #define DEF_TV_INSERTFIRST "1"
186 #define DEF_TV_FOCUSHEIGHT "1"
187 #define DEF_TV_LINESPACING	"0"
188 #define DEF_TV_LINEWIDTH	"1"
189 #define DEF_TV_MINHEIGHT	"0"
190 #define DEF_TV_MAKE_PATH	"no"
191 #define DEF_TV_NEW_TAGS		"no"
192 #define DEF_TV_NOAUTO_CLOSE_LEAF		"no"
193 #define DEF_TV_NORMAL_BACKGROUND 	STD_NORMAL_BACKGROUND
194 #define DEF_TV_NORMAL_FG_MONO	STD_ACTIVE_FG_MONO
195 #define DEF_TV_RELIEF		"sunken"
196 #define DEF_TV_OPENANCHOR		"center"
197 #define DEF_TV_RESIZE_CURSOR	"arrow"
198 #define DEF_TV_RULEWIDTH	"0"
199 #define DEF_TV_SCROLL_INCREMENT "20"
200 #define DEF_TV_SCROLL_MODE	"hierbox"
201 #define DEF_TV_SELECT_BACKGROUND 	"#4A6983"
202 #define DEF_TV_ROOT_NODE "0"
203 #define DEF_TV_RULE_BACKGROUND 	"black"
204 #define DEF_TV_RULE_WIDTH 	"0"
205 #define DEF_TV_SELECT_BG_MONO  	STD_SELECT_BG_MONO
206 #define DEF_TV_SELECT_BORDERWIDTH "1"
207 #define DEF_TV_SELECT_FOREGROUND 	"#ffffff"
208 #define DEF_TV_SELECT_FG_MONO  	STD_SELECT_FG_MONO
209 #define DEF_TV_SELECT_MODE	"single"
210 #define DEF_TV_SELECT_RELIEF	"flat"
211 #define DEF_TV_SHOW_ROOT	"yes"
212 #define DEF_TV_SHOW_TITLES	"yes"
213 #define DEF_TV_SORT_SELECTION	"no"
214 #define DEF_TV_TAKE_FOCUS	"1"
215 #define DEF_TV_TEXT_COLOR	STD_NORMAL_FOREGROUND
216 #define DEF_TV_TEXT_MONO	STD_NORMAL_FG_MONO
217 #define DEF_TV_TEXT_DISABLED_COLOR	"DarkGray"
218 #define DEF_TV_TRIMLEFT		""
219 #define DEF_TV_WIDTH		"200"
220 #define DEF_TV_SCROLL_TILE      "no"
221 
222 extern Tk_CustomOption bltTileOption;
223 
224 Blt_ConfigSpec bltTreeViewButtonSpecs[] =
225 {
226     {BLT_CONFIG_BORDER, "-activebackground", "activeButBackground", "ButBackground",
227 	(char *)NULL, Blt_Offset(TreeView, button.activeBorder),
228         BLT_CONFIG_NULL_OK},
229     {BLT_CONFIG_SYNONYM, "-activebg", (char *)NULL, (char *)NULL,
230 	(char *)NULL, 0, 0, (ClientData)"-activebackground"},
231     {BLT_CONFIG_SYNONYM, "-activefg", (char *)NULL, (char *)NULL,
232 	(char *)NULL, 0, 0, (ClientData)"-activeforeground"},
233     {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
234 	DEF_BUTTON_ACTIVE_FOREGROUND,
235 	Blt_Offset(TreeView, button.activeFgColor), 0},
236     {BLT_CONFIG_CUSTOM, "-activeimages", "activeImages", "Icons",
237 	(char *)NULL, Blt_Offset(TreeView, button.activeicons), BLT_CONFIG_NULL_OK,
238 	&bltTreeViewIconsOption},
239     {BLT_CONFIG_BORDER, "-background", "Butbackground", "ButBackground",
240 	(char*)NULL, Blt_Offset(TreeView, button.border), BLT_CONFIG_NULL_OK},
241     {BLT_CONFIG_SYNONYM, "-bd", (char *)NULL, (char *)NULL, (char *)NULL, 0,
242 	0, (ClientData)"-borderwidth"},
243     {BLT_CONFIG_SYNONYM, "-bg", (char *)NULL, (char *)NULL, (char *)NULL, 0, 0, (ClientData)"-background"},
244     {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
245 	DEF_BUTTON_BORDERWIDTH, Blt_Offset(TreeView, button.borderWidth),
246 	BLT_CONFIG_DONT_SET_DEFAULT},
247     {BLT_CONFIG_RELIEF, "-closerelief", "closeRelief", "Relief",
248 	DEF_BUTTON_CLOSE_RELIEF, Blt_Offset(TreeView, button.closeRelief),
249 	BLT_CONFIG_DONT_SET_DEFAULT},
250     {BLT_CONFIG_SYNONYM, "-fg", (char *)NULL, (char *)NULL, (char *)NULL, 0, 0,
251         (ClientData)"-foreground"},
252     {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
253 	DEF_BUTTON_NORMAL_FOREGROUND, Blt_Offset(TreeView, button.fgColor), 0},
254     {BLT_CONFIG_CUSTOM, "-images", "images", "Icons",
255 	(char *)NULL, Blt_Offset(TreeView, button.icons), BLT_CONFIG_NULL_OK,
256 	&bltTreeViewIconsOption},
257     {BLT_CONFIG_RELIEF, "-openrelief", "openRelief", "Relief",
258 	DEF_BUTTON_OPEN_RELIEF, Blt_Offset(TreeView, button.openRelief),
259 	BLT_CONFIG_DONT_SET_DEFAULT},
260     {BLT_CONFIG_DISTANCE, "-size", "size", "Size",
261 	DEF_BUTTON_SIZE, Blt_Offset(TreeView, button.reqSize), 0},
262     {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
263 	(char *)NULL, 0, 0}
264 };
265 
266 Blt_ConfigSpec bltTreeViewEntrySpecs[] =
267 {
268     {BLT_CONFIG_CUSTOM, "-activeicons", (char *)NULL, (char *)NULL,
269 	(char *)NULL, Blt_Offset(TreeViewEntry, activeIcons),
270 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewIconsOption},
271     {BLT_CONFIG_BORDER, "-background", "entryBackground", "EntryBackground",
272         (char *)NULL, Blt_Offset(TreeViewEntry, border), BLT_CONFIG_NULL_OK},
273     {BLT_CONFIG_SYNONYM, "-bg", (char *)NULL,  (char *)NULL, (char *)NULL,
274 	0, 0, (ClientData)"-background"},
275     {BLT_CONFIG_CUSTOM, "-bindtags", (char *)NULL, (char *)NULL,
276 	(char *)NULL, Blt_Offset(TreeViewEntry, tagsUid),
277 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewUidOption},
278     {BLT_CONFIG_CUSTOM, "-button", (char *)NULL, (char *)NULL,
279 	DEF_TV_BUTTON, Blt_Offset(TreeViewEntry, flags),
280 	BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
281     {BLT_CONFIG_CUSTOM, "-closecommand", (char *)NULL, (char *)NULL,
282 	(char *)NULL, Blt_Offset(TreeViewEntry, closeCmd),
283 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewUidOption},
284     {BLT_CONFIG_CUSTOM, "-data", (char *)NULL, (char *)NULL,
285 	"", 0, 0, &bltTreeViewDataOption},
286     {BLT_CONFIG_SYNONYM, "-fg", (char *)NULL, (char *)NULL, (char *)NULL,
287 	0, 0, (ClientData)"-foreground"},
288     {BLT_CONFIG_FONT, "-font", (char *)NULL, (char *)NULL,
289 	(char *)NULL, Blt_Offset(TreeViewEntry, font),
290 	 BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT},
291     {BLT_CONFIG_BITFLAG, "-forcetree", "forceTree", "forceTree",
292 	"False", Blt_Offset(TreeViewEntry, flags),
293 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)ENTRY_IS_TREE},
294     {BLT_CONFIG_COLOR, "-foreground", "entryforeground", (char *)NULL,
295 	(char *)NULL, Blt_Offset(TreeViewEntry, color),
296 	BLT_CONFIG_NULL_OK},
297     {BLT_CONFIG_DISTANCE, "-height", (char *)NULL, (char *)NULL,
298 	(char *)NULL, Blt_Offset(TreeViewEntry, reqHeight),
299          BLT_CONFIG_DONT_SET_DEFAULT|BLT_CONFIG_NULL_OK},
300     {BLT_CONFIG_CUSTOM, "-icons", (char *)NULL, (char *)NULL,
301 	(char *)NULL, Blt_Offset(TreeViewEntry, icons),
302 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewIconsOption},
303     /*{BLT_CONFIG_BOOLEAN, "-hide", (char *)NULL, (char *)NULL,
304 	"no", Blt_Offset(TreeViewEntry, hide),
305 	0},*/
306     {BLT_CONFIG_BITFLAG, "-hide", (char *)NULL, (char *)NULL,
307 	"no", Blt_Offset(TreeViewEntry, flags),
308          BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)ENTRY_HIDDEN },
309     {BLT_CONFIG_CUSTOM, "-isopen", (char *)NULL, (char *)NULL,
310 	(char *)NULL, 0,
311 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewIsOpenOption },
312     {BLT_CONFIG_CUSTOM, "-label", (char *)NULL, (char *)NULL,
313 	(char *)NULL, Blt_Offset(TreeViewEntry, labelUid), BLT_CONFIG_DONT_SET_DEFAULT,
314 	&bltTreeViewLabelOption},
315     {BLT_CONFIG_CUSTOM, "-opencommand", (char *)NULL, (char *)NULL,
316 	(char *)NULL, Blt_Offset(TreeViewEntry, openCmd),
317 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT, &bltTreeViewUidOption },
318     {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
319 	(char *)NULL, Blt_Offset(TreeViewEntry, shadow),
320 	BLT_CONFIG_NULL_OK | BLT_CONFIG_COLOR_ONLY | BLT_CONFIG_DONT_SET_DEFAULT},
321     {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
322 	(char *)NULL, Blt_Offset(TreeViewEntry, shadow),
323 	BLT_CONFIG_NULL_OK | BLT_CONFIG_MONO_ONLY | BLT_CONFIG_DONT_SET_DEFAULT},
324     {BLT_CONFIG_STATE, "-state", "state", "State",
325 	"0", Blt_Offset(TreeViewEntry, state),
326 	BLT_CONFIG_DONT_SET_DEFAULT},
327     {BLT_CONFIG_CUSTOM, "-style", "style", "Style",
328 	(char *)NULL, Blt_Offset(TreeViewEntry, realStylePtr),
329          BLT_CONFIG_DONT_SET_DEFAULT|BLT_CONFIG_NULL_OK, &bltTreeViewStyleOption},
330     {BLT_CONFIG_STRING, "-sublabel", (char *)NULL, (char *)NULL,
331 	(char *)NULL, Blt_Offset(TreeViewEntry, subLabel),
332 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT},
333     {BLT_CONFIG_INT, "-underline", (char *)NULL, (char *)NULL,
334 	"-1", Blt_Offset(TreeViewEntry, underline),
335 	BLT_CONFIG_DONT_SET_DEFAULT},
336     {BLT_CONFIG_STRING, "-userdata", (char *)NULL, (char *)NULL,
337 	(char *)NULL, Blt_Offset(TreeViewEntry, userData),
338 	BLT_CONFIG_NULL_OK|BLT_CONFIG_DONT_SET_DEFAULT},
339     {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
340 	(char *)NULL, 0, 0}
341 };
342 
343 extern Blt_CustomOption bltTreeViewStyleOption;
344 
345 Blt_ConfigSpec bltTreeViewSpecs[] =
346 {
347     {BLT_CONFIG_CUSTOM, "-activeicons", "activeIcons", "Icons",
348 	DEF_TV_ACTIVE_ICONS, Blt_Offset(TreeView, activeIcons),
349 	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
350     {BLT_CONFIG_CUSTOM, "-activeleaficons", "activeLeafIcons", "LeafIcons",
351 	DEF_TV_ACTIVE_LEAFICONS, Blt_Offset(TreeView, activeLeafIcons),
352 	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
353     {BLT_CONFIG_BITFLAG,
354 	"-allowduplicates", "allowDuplicates", "AllowDuplicates",
355 	DEF_TV_ALLOW_DUPLICATES, Blt_Offset(TreeView, flags),
356 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_ALLOW_DUPLICATES},
357     {BLT_CONFIG_CUSTOM, "-altstyle", "altStyle", "AltStyle",
358 	(char *)NULL, Blt_Offset(TreeView, altStylePtr),
359          BLT_CONFIG_DONT_SET_DEFAULT| BLT_CONFIG_NULL_OK, &bltTreeViewStyleOption},
360     {BLT_CONFIG_BITFLAG, "-autocreate", "autoCreate", "AutoCreate",
361 	DEF_TV_MAKE_PATH, Blt_Offset(TreeView, flags),
362 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_FILL_ANCESTORS},
363     {BLT_CONFIG_BORDER, "-background", "background", "Background",
364 	DEF_TV_BACKGROUND, Blt_Offset(TreeView, border), 0},
365     {BLT_CONFIG_SYNONYM, "-bd", (char *)NULL, (char *)NULL, (char *)NULL,
366 	0, 0, (ClientData)"-borderwidth"},
367     {BLT_CONFIG_SYNONYM, "-bg", (char *)NULL, (char *)NULL, (char *)NULL,
368 	0, 0, (ClientData)"-background"},
369     {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
370 	DEF_TV_BORDERWIDTH, Blt_Offset(TreeView, borderWidth),
371 	BLT_CONFIG_DONT_SET_DEFAULT},
372     {BLT_CONFIG_CUSTOM, "-button", "button", "Button",
373 	DEF_TV_BUTTON, Blt_Offset(TreeView, buttonFlags),
374 	BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
375     {BLT_CONFIG_STRING, "-closecommand", "closeCommand", "CloseCommand",
376 	(char *)NULL, Blt_Offset(TreeView, closeCmd),
377 	BLT_CONFIG_NULL_OK},
378     {BLT_CONFIG_BOOLEAN, "-columnshowhighlight", "ColumnShowHighlight", "ColumnShowHighlight",
379 	DEF_TV_ACT_COL_SHOW, Blt_Offset(TreeView, actCol),
380 	0},
381     {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
382 	(char *)NULL, Blt_Offset(TreeView, cursor), BLT_CONFIG_NULL_OK},
383     {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes",
384 	DEF_TV_DASHES, Blt_Offset(TreeView, dashes),
385 	BLT_CONFIG_DONT_SET_DEFAULT},
386 /*    {BLT_CONFIG_BORDER, "-disabledbackground", "disabledBackground",
387         "DisabledBackground", (char *)NULL, Blt_Offset(TreeView, disabledBorder),
388         BLT_CONFIG_DONT_SET_DEFAULT| BLT_CONFIG_NULL_OK}, */
389     {BLT_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
390         "DisabledForeground", DEF_TV_TEXT_DISABLED_COLOR,
391         Blt_Offset(TreeView, disabledColor), 0 },
392     {BLT_CONFIG_CUSTOM, "-emptystyle", "emptyStyle", "EmptyStyle",
393 	(char *)NULL, Blt_Offset(TreeView, emptyStylePtr),
394          BLT_CONFIG_DONT_SET_DEFAULT| BLT_CONFIG_NULL_OK, &bltTreeViewStyleOption},
395     {BLT_CONFIG_BOOLEAN, "-entryshowhighlight", "EntryShowHightlight", "EntryShowHightlight",
396 	DEF_TV_ACT_ENTRY_SHOW, Blt_Offset(TreeView, actEntry),
397 	0},
398     {BLT_CONFIG_BITFLAG, "-exportselection", "exportSelection",
399 	"ExportSelection", DEF_TV_EXPORT_SELECTION,
400 	Blt_Offset(TreeView, flags), BLT_CONFIG_DONT_SET_DEFAULT,
401 	(Blt_CustomOption *)TV_SELECT_EXPORT},
402     {BLT_CONFIG_SYNONYM, "-fg", (char *)NULL , (char *)NULL, (char *)NULL,
403 	0, 0, (ClientData)"-foreground"},
404     {BLT_CONFIG_BITFLAG, "-fillnull", "fillNull",
405 	"FillNull", DEF_TV_FILL_NULL,
406 	Blt_Offset(TreeView, flags), BLT_CONFIG_DONT_SET_DEFAULT,
407 	(Blt_CustomOption *)TV_FILL_NULL},
408     {BLT_CONFIG_BOOLEAN, "-flat", "flat", "Flat",
409 	DEF_TV_FLAT, Blt_Offset(TreeView, flatView),
410 	BLT_CONFIG_DONT_SET_DEFAULT},
411     {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
412 	DEF_TV_FOCUS_DASHES, Blt_Offset(TreeView, focusDashes),
413 	BLT_CONFIG_NULL_OK},
414     {BLT_CONFIG_COLOR,
415 	"-focusforeground", "focusForeground", "FocusForeground",
416 	DEF_TV_FOCUS_FOREGROUND, Blt_Offset(TreeView, focusColor),
417 	BLT_CONFIG_COLOR_ONLY},
418     {BLT_CONFIG_COLOR,
419 	"-focusforeground", "focusForeground", "FocusForeground",
420 	DEF_TV_FOCUS_FG_MONO, Blt_Offset(TreeView, focusColor),
421 	BLT_CONFIG_MONO_ONLY},
422     {BLT_CONFIG_INT, "-focusheight", "focusHeight", "FocusHeight",
423 	DEF_TV_FOCUSHEIGHT, Blt_Offset(TreeView, focusHeight), 0},
424     {BLT_CONFIG_FONT, "-font", "font", "Font",
425 	DEF_TV_FONT, Blt_Offset(TreeView, font), 0},
426     {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
427 	DEF_TV_TEXT_COLOR, Blt_Offset(TreeView, fgColor),
428 	BLT_CONFIG_COLOR_ONLY},
429     {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
430 	DEF_TV_TEXT_MONO, Blt_Offset(TreeView, fgColor),
431 	BLT_CONFIG_MONO_ONLY},
432     {BLT_CONFIG_OBJCMD, "-formatcmd", "formatCmd", "FormatCmd",
433 	NULL, Blt_Offset(TreeView, formatCmd),
434         BLT_CONFIG_NULL_OK},
435     {BLT_CONFIG_DISTANCE, "-height", "height", "Height",
436 	DEF_TV_HEIGHT, Blt_Offset(TreeView, reqHeight),
437 	BLT_CONFIG_DONT_SET_DEFAULT},
438     {BLT_CONFIG_BITFLAG, "-hideicons", "hideIcons", "HideIcons",
439 	DEF_TV_HIDE_ICONS, Blt_Offset(TreeView, flags), 0,
440         (Blt_CustomOption *)TV_HIDE_ICONS},
441     {BLT_CONFIG_BITFLAG, "-hideleaves", "hideLeaves", "HideLeaves",
442 	DEF_TV_HIDE_LEAVES, Blt_Offset(TreeView, flags),
443 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_LEAVES},
444     {BLT_CONFIG_BITFLAG, "-hideroot", "hideRoot", "HideRoot",
445 	DEF_TV_HIDE_ROOT, Blt_Offset(TreeView, flags),
446 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_ROOT},
447     {BLT_CONFIG_BOOLEAN, "-hidedataicons", "hideDataIcons", "HideDataIcons",
448 	DEF_TV_HIDE_STYLE_ICONS, Blt_Offset(TreeView, hideStyleIcons), 0, },
449     {BLT_CONFIG_BOOLEAN, "-hidedatatext", "hideDataText", "HideDataText",
450 	DEF_TV_HIDE_STYLE_TEXT, Blt_Offset(TreeView, hideStyleText), 0, },
451     {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
452 	"HighlightBackground", DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND,
453         Blt_Offset(TreeView, highlightBgColor), 0},
454     {BLT_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
455 	DEF_TV_FOCUS_HIGHLIGHT_COLOR, Blt_Offset(TreeView, highlightColor), 0},
456     {BLT_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
457 	"HighlightThickness", DEF_TV_FOCUS_HIGHLIGHT_WIDTH,
458 	Blt_Offset(TreeView, highlightWidth), BLT_CONFIG_DONT_SET_DEFAULT},
459     {BLT_CONFIG_CUSTOM, "-icons", "icons", "Icons",
460 	DEF_TV_ICONS, Blt_Offset(TreeView, icons),
461 	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
462     {BLT_CONFIG_OBJCMD, "-imagecmd", "ImageCmd", "ImageCmd",
463 	NULL, Blt_Offset(TreeView, imageCmd),
464         BLT_CONFIG_NULL_OK},
465     {BLT_CONFIG_BOOLEAN, "-inlinedata", "inlineData", "InlineData",
466 	DEF_TV_INLINEIMG, Blt_Offset(TreeView, inlineImg), 0},
467     {BLT_CONFIG_INT, "-insertfirst", "insertFirst", "InsertFirst",
468 	DEF_TV_INSERTFIRST, Blt_Offset(TreeView, insertFirst), 0},
469     {BLT_CONFIG_CUSTOM, "-leaficons", "leafIcons", "LeafIcons",
470 	DEF_TV_LEAFICONS, Blt_Offset(TreeView, leafIcons),
471 	BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
472     {BLT_CONFIG_DISTANCE, "-levelpad", "levelPad", "LevelPad",
473 	"0", Blt_Offset(TreeView, levelPad), 0},
474     {BLT_CONFIG_CUSTOM, "-levelstyles", "levelStyles", "LevelStyles",
475 	(char *)NULL, Blt_Offset(TreeView, levelStyles),
476          BLT_CONFIG_DONT_SET_DEFAULT| BLT_CONFIG_NULL_OK, &bltTreeViewStylesOption},
477     {BLT_CONFIG_INT, "-nextauto", (char *)NULL, (char *)NULL,
478 	"1", Blt_Offset(TreeView, nextIdx), 0},
479     {BLT_CONFIG_INT, "-nextsubauto", (char *)NULL, (char *)NULL,
480 	"1", Blt_Offset(TreeView, nextSubIdx), 0},
481     {BLT_CONFIG_BORDER, "-nofocusselectbackground", "noFocusSelectBackground",
482 	"NoFocusSelectBackground", (char*)NULL,
483 	Blt_Offset(TreeView, selOutFocusBorder), BLT_CONFIG_NULL_OK},
484     {BLT_CONFIG_INT, "-rootnode", "rootNode", "RootNode", DEF_TV_ROOT_NODE,
485 	Blt_Offset(TreeView, rootNodeNum), 0},
486     {BLT_CONFIG_COLOR, "-nofocusselectforeground", "noFocusSelectForeground",
487 	"NoFocusSelectForeground", (char*)NULL,
488 	Blt_Offset(TreeView, selOutFocusFgColor), BLT_CONFIG_NULL_OK},
489     {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
490 	DEF_TV_VLINE_COLOR, Blt_Offset(TreeView, lineColor),
491 	BLT_CONFIG_COLOR_ONLY},
492     {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
493 	DEF_TV_VLINE_MONO, Blt_Offset(TreeView, lineColor),
494 	BLT_CONFIG_MONO_ONLY},
495     {BLT_CONFIG_DISTANCE, "-linespacing", "lineSpacing", "LineSpacing",
496 	DEF_TV_LINESPACING, Blt_Offset(TreeView, leader),
497 	BLT_CONFIG_DONT_SET_DEFAULT},
498     {BLT_CONFIG_DISTANCE, "-linewidth", "lineWidth", "LineWidth",
499 	DEF_TV_LINEWIDTH, Blt_Offset(TreeView, lineWidth),
500 	BLT_CONFIG_DONT_SET_DEFAULT},
501     {BLT_CONFIG_DISTANCE, "-minheight", "minHeight", "MinHeight",
502 	DEF_TV_MINHEIGHT, Blt_Offset(TreeView, reqMin),
503 	0},
504     {BLT_CONFIG_BITFLAG, "-newtags", "newTags", "NewTags",
505 	DEF_TV_NEW_TAGS, Blt_Offset(TreeView, flags),
506 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_NEW_TAGS},
507     {BLT_CONFIG_BITFLAG, "-noautocloseleaf", "noAutoCloseLeaf", "NoAutoCloseLeaf",
508 	DEF_TV_NOAUTO_CLOSE_LEAF, Blt_Offset(TreeView, flags),
509 	BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_NOAUTO_CLOSE_LEAF},
510     {BLT_CONFIG_ANCHOR, "-openanchor", "openAnchor", "OpenAnchor",
511 	DEF_TV_OPENANCHOR, Blt_Offset(TreeView, openAnchor), BLT_CONFIG_NULL_OK},
512     {BLT_CONFIG_STRING, "-opencommand", "openCommand", "OpenCommand",
513 	(char *)NULL, Blt_Offset(TreeView, openCmd), BLT_CONFIG_NULL_OK},
514 #if 1
515     {BLT_CONFIG_DISTANCE, "-padx", "padX", "Pad",
516 	"0", Blt_Offset(TreeView, padX), 0},
517     {BLT_CONFIG_DISTANCE, "-pady", "padY", "Pad",
518 	"0", Blt_Offset(TreeView, padY), 0},
519 #endif
520     {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
521 	DEF_TV_RELIEF, Blt_Offset(TreeView, relief), 0},
522     {BLT_CONFIG_CURSOR, "-resizecursor", "resizeCursor", "ResizeCursor",
523 	DEF_TV_RESIZE_CURSOR, Blt_Offset(TreeView, resizeCursor), 0},
524     {BLT_CONFIG_CUSTOM, "-scrollmode", "scrollMode", "ScrollMode",
525 	DEF_TV_SCROLL_MODE, Blt_Offset(TreeView, scrollMode),
526 	BLT_CONFIG_DONT_SET_DEFAULT, &scrollmodeOption},
527     {BLT_CONFIG_BOOLEAN, "-scrolltile", "scrollTile", "ScrollTile",
528 	DEF_TV_SCROLL_TILE, Tk_Offset(TreeView, scrollTile),
529 	BLT_CONFIG_DONT_SET_DEFAULT},
530     {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Background",
531 	DEF_TV_SELECT_BACKGROUND, Blt_Offset(TreeView, selInFocusBorder), 0},
532     {BLT_CONFIG_DISTANCE,
533 	"-selectborderwidth", "selectBorderWidth", "BorderWidth",
534 	DEF_TV_SELECT_BORDERWIDTH, Blt_Offset(TreeView, selBorderWidth),
535 	BLT_CONFIG_DONT_SET_DEFAULT},
536     {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
537 	(char *)NULL, Blt_Offset(TreeView, selectCmd), BLT_CONFIG_NULL_OK},
538     {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
539 	DEF_TV_SELECT_FOREGROUND, Blt_Offset(TreeView, selInFocusFgColor), 0},
540     {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
541 	DEF_TV_SELECT_MODE, Blt_Offset(TreeView, selectMode),
542 	BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
543     {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
544 	DEF_TV_SELECT_RELIEF, Blt_Offset(TreeView, selRelief),
545 	BLT_CONFIG_DONT_SET_DEFAULT},
546     {BLT_CONFIG_TILE, "-selecttile", "selectTile", "SelectTile",
547 	(char *)NULL, Tk_Offset(TreeView, selectTile), BLT_CONFIG_NULL_OK, },
548     {BLT_CONFIG_CUSTOM, "-separator", "separator", "Separator",
549 	(char *)NULL, Blt_Offset(TreeView, pathSep), BLT_CONFIG_NULL_OK,
550 	&separatorOption},
551     {BLT_CONFIG_BOOLEAN, "-showfull", "showFull", "ShowFull",
552 	"1", Tk_Offset(TreeView, showFull),
553 	0},
554     {BLT_CONFIG_BITFLAG, "-showtitles", "showTitles", "ShowTitles",
555 	DEF_TV_SHOW_TITLES, Blt_Offset(TreeView, flags), 0,
556         (Blt_CustomOption *)TV_SHOW_COLUMN_TITLES},
557     {BLT_CONFIG_BITFLAG, "-sortselection", "sortSelection", "SortSelection",
558 	DEF_TV_SORT_SELECTION, Blt_Offset(TreeView, flags),
559         BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_SELECT_SORTED},
560     {BLT_CONFIG_STRING, "-stylecommand", "styleCommand", "StyleCommand",
561 	"%W style create textbox %V", Blt_Offset(TreeView, styleCmd), BLT_CONFIG_NULL_OK},
562     {BLT_CONFIG_CUSTOM, "-substyle", "subStyle", "SubStyle",
563 	(char *)NULL, Blt_Offset(TreeView, subStylePtr),
564          BLT_CONFIG_DONT_SET_DEFAULT|BLT_CONFIG_NULL_OK, &bltTreeViewStyleOption},
565     {BLT_CONFIG_DISTANCE, "-underline", "underline", "Underline",
566 	DEF_TV_RULE_WIDTH, Blt_Offset(TreeView, ruleWidth),
567 	0},
568     {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
569 	DEF_TV_TAKE_FOCUS, Blt_Offset(TreeView, takeFocus),
570 	BLT_CONFIG_NULL_OK},
571     {BLT_CONFIG_FONT, "-titlefont", "titleFont", "TitleFont",
572 	DEF_TV_TITLEFONT, Blt_Offset(TreeView, titleFont), 0},
573     {BLT_CONFIG_DISTANCE, "-titlepad", "titlePad", "TitlePad",
574 	"0", Blt_Offset(TreeView, titlePad), 0},
575     {BLT_CONFIG_CUSTOM, "-tree", "tree", "Tree",
576 	(char *)NULL, Blt_Offset(TreeView, tree), BLT_CONFIG_NULL_OK,
577 	&bltTreeViewTreeOption},
578     {BLT_CONFIG_TILE, "-tile", "tile", "Tile",
579 	(char *)NULL, Tk_Offset(TreeView, tile), BLT_CONFIG_NULL_OK, },
580     {BLT_CONFIG_STRING, "-trim", "trim", "Trim",
581 	DEF_TV_TRIMLEFT, Blt_Offset(TreeView, trimLeft),
582 	BLT_CONFIG_NULL_OK},
583     {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
584 	DEF_TV_WIDTH, Blt_Offset(TreeView, reqWidth),
585 	BLT_CONFIG_DONT_SET_DEFAULT},
586     {BLT_CONFIG_STRING,
587 	"-xscrollcommand", "xScrollCommand", "ScrollCommand",
588 	(char *)NULL, Blt_Offset(TreeView, xScrollCmdPrefix),
589 	BLT_CONFIG_NULL_OK},
590     {BLT_CONFIG_DISTANCE,
591 	"-xscrollincrement", "xScrollIncrement", "ScrollIncrement",
592 	DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, xScrollUnits),
593 	BLT_CONFIG_DONT_SET_DEFAULT},
594     {BLT_CONFIG_STRING,
595         "-yscrollcommand", "yScrollCommand", "ScrollCommand",
596 	(char *)NULL, Blt_Offset(TreeView, yScrollCmdPrefix),
597 	BLT_CONFIG_NULL_OK},
598     {BLT_CONFIG_DISTANCE,
599 	"-yscrollincrement", "yScrollIncrement", "ScrollIncrement",
600 	DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, yScrollUnits),
601 	BLT_CONFIG_DONT_SET_DEFAULT},
602     {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
603 	(char *)NULL, 0, 0}
604 };
605 
606 /* Forward Declarations */
607 static Blt_TreeNotifyEventProc TreeEventProc;
608 static Blt_TreeTraceProc TreeTraceProc;
609 static Tcl_CmdDeleteProc WidgetInstCmdDeleteProc;
610 static Tcl_FreeProc DestroyTreeView;
611 static Tcl_IdleProc DisplayTreeView;
612 static Tk_EventProc TreeViewEventProc;
613 
614 static int ComputeVisibleEntries _ANSI_ARGS_((TreeView *tvPtr));
615 
616 EXTERN int Blt_TreeCmdGetToken _ANSI_ARGS_((Tcl_Interp *interp,
617 	CONST char *treeName, Blt_Tree *treePtr));
618 
619 extern Blt_TreeApplyProc Blt_TreeViewSortApplyProc;
620 static Blt_BindPickProc PickItem;
621 static Blt_BindTagProc GetTags;
622 static Tcl_FreeProc DestroyEntry;
623 static Tcl_ObjCmdProc TreeViewObjCmd;
624 static Tk_ImageChangedProc IconChangedProc;
625 static Tk_SelectionProc SelectionProc;
626 
627 static void widgetWorldChanged(ClientData clientData);
628 
629 static Tk_ClassProcs treeviewClass = {
630     sizeof(Tk_ClassProcs),	/* size */
631     widgetWorldChanged,		/* worldChangedProc */
632 };
633 
634 
Blt_TreeViewOptsInit(TreeView * tvPtr)635 void Blt_TreeViewOptsInit(TreeView* tvPtr) {
636     bltTreeViewIconsOption.clientData = tvPtr;
637     bltTreeViewIconOption.clientData = tvPtr;
638     bltTreeViewStyleOption.clientData = tvPtr;
639     bltTreeViewStylesOption.clientData = tvPtr;
640     bltTreeViewTreeOption.clientData = tvPtr;
641     bltTreeViewColumnOption.clientData = tvPtr;
642     bltTreeViewLabelOption.clientData = tvPtr;
643     bltTreeViewDataOption.clientData = tvPtr;
644     bltTreeViewUidOption.clientData = tvPtr;
645 }
646 
647 /*
648  *----------------------------------------------------------------------
649  *
650  * Blt_TreeViewEventuallyRedraw --
651  *
652  *	Queues a request to redraw the widget at the next idle point.
653  *
654  * Results:
655  *	None.
656  *
657  * Side effects:
658  *	Information gets redisplayed.  Right now we don't do selective
659  *	redisplays:  the whole window will be redrawn.
660  *
661  *----------------------------------------------------------------------
662  */
663 void
Blt_TreeViewEventuallyRedraw(TreeView * tvPtr)664 Blt_TreeViewEventuallyRedraw(TreeView *tvPtr)
665 {
666     if ((tvPtr->tkwin != NULL) && ((tvPtr->flags & TV_REDRAW) == 0)) {
667 	tvPtr->flags |= TV_REDRAW;
668 	Tcl_DoWhenIdle(DisplayTreeView, tvPtr);
669     }
670 }
671 
672 void
Blt_TreeViewTraceColumn(TreeView * tvPtr,TreeViewColumn * columnPtr)673 Blt_TreeViewTraceColumn(TreeView *tvPtr, TreeViewColumn *columnPtr)
674 {
675     if (columnPtr->trace != NULL) {
676         Blt_TreeDeleteTrace(columnPtr->trace);
677     }
678     columnPtr->trace = Blt_TreeCreateTrace(tvPtr->tree, NULL /* Node */,
679         columnPtr->key, NULL,
680 	TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET,
681 	TreeTraceProc, tvPtr);
682 }
683 
684 void
Blt_TreeViewUntraceColumn(TreeView * tvPtr,TreeViewColumn * columnPtr)685 Blt_TreeViewUntraceColumn(TreeView *tvPtr, TreeViewColumn *columnPtr)
686 {
687     if (columnPtr->trace != NULL) {
688         Blt_TreeDeleteTrace(columnPtr->trace);
689         columnPtr->trace = NULL;
690     }
691 }
692 
693 static void
TraceColumns(TreeView * tvPtr)694 TraceColumns(TreeView *tvPtr)
695 {
696     Blt_ChainLink *linkPtr;
697     TreeViewColumn *columnPtr;
698 
699     for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
700 	linkPtr = Blt_ChainNextLink(linkPtr)) {
701 	columnPtr = Blt_ChainGetValue(linkPtr);
702          Blt_TreeViewTraceColumn(tvPtr, columnPtr);
703     }
704 }
705 
706 static void
UntraceColumns(TreeView * tvPtr)707 UntraceColumns(TreeView *tvPtr)
708 {
709     Blt_ChainLink *linkPtr;
710     TreeViewColumn *columnPtr;
711 
712     for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
713     linkPtr = Blt_ChainNextLink(linkPtr)) {
714         columnPtr = Blt_ChainGetValue(linkPtr);
715         Blt_TreeViewUntraceColumn(tvPtr, columnPtr);
716     }
717 }
718 
719 /*
720  *----------------------------------------------------------------------
721  *
722  * ObjToTree --
723  *
724  *	Convert the string representing the name of a tree object
725  *	into a tree token.
726  *
727  * Results:
728  *	If the string is successfully converted, TCL_OK is returned.
729  *	Otherwise, TCL_ERROR is returned and an error message is left
730  *	in interpreter's result field.
731  *
732  *----------------------------------------------------------------------
733  */
734 /*ARGSUSED*/
735 static int
ObjToTree(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)736 ObjToTree(
737     ClientData clientData,	/* Not used. */
738     Tcl_Interp *interp,		/* Interpreter to send results back to */
739     Tk_Window tkwin,		/* Not used. */
740     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
741     char *widgRec,
742     int offset)
743 {
744     Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
745     Blt_Tree tree;
746     char *string;
747 
748     tree = NULL;
749     string = Tcl_GetString(objPtr);
750     if ((string[0] != '\0') &&
751 	(Blt_TreeGetToken(interp, string, &tree) != TCL_OK)) {
752 	return TCL_ERROR;
753     }
754     *treePtr = tree;
755     return TCL_OK;
756 }
757 
758 /*
759  *----------------------------------------------------------------------
760  *
761  * TreeToObj --
762  *
763  * Results:
764  *	The string representation of the button boolean is returned.
765  *
766  *----------------------------------------------------------------------
767  */
768 /*ARGSUSED*/
769 static Tcl_Obj *
TreeToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)770 TreeToObj(
771     ClientData clientData,	/* Not used. */
772     Tcl_Interp *interp,
773     Tk_Window tkwin,		/* Not used. */
774     char *widgRec,
775     int offset)
776 {
777     Blt_Tree tree = *(Blt_Tree *)(widgRec + offset);
778 
779     return Tcl_NewStringObj(tree?Blt_TreeName(tree):"", -1);
780 }
781 
782 #if 0
783 /* In case of external tree, release entries manually. */
784 static void
785 ReleaseEntries( TreeView *tvPtr )
786 {
787     Blt_HashEntry *hPtr;
788     Blt_HashSearch cursor;
789     TreeViewEntry *entryPtr;
790 
791     for (hPtr = Blt_FirstHashEntry(&tvPtr->entryTable, &cursor); hPtr != NULL;
792         hPtr = Blt_NextHashEntry(&cursor)) {
793         entryPtr = Blt_GetHashValue(hPtr);
794         if (entryPtr->node != tvPtr->rootNode) {
795             DestroyEntry((ClientData)entryPtr);
796         } else {
797             /* Release keys for root. */
798             TreeViewValue *lastPtr, *nextPtr, *valuePtr;
799 
800             lastPtr = NULL;
801             for(valuePtr = entryPtr->values; valuePtr != NULL;
802                 valuePtr = nextPtr) {
803                 nextPtr = valuePtr->nextPtr;
804                 if (valuePtr->columnPtr != &tvPtr->treeColumn) {
805                     Blt_TreeViewWindowUpdate(entryPtr, valuePtr->columnPtr);
806                     Blt_TreeViewDestroyValue(tvPtr, entryPtr, valuePtr);
807                     if (lastPtr == NULL) {
808                         entryPtr->values = nextPtr;
809                     } else {
810                         lastPtr->nextPtr = nextPtr;
811                     }
812                 }
813                 lastPtr = valuePtr;
814             }
815 
816         }
817     }
818     UntraceColumns(tvPtr);
819 
820 }
821 #endif
822 
823 
824 static
FreeTreeDo(Tcl_Interp * interp,TreeView * tvPtr,Blt_Tree treePtr)825 void FreeTreeDo (Tcl_Interp *interp, TreeView *tvPtr, Blt_Tree treePtr) {
826     Blt_TreeNode root;
827 
828     Blt_Tree newTree = tvPtr->tree;
829     if (treePtr == NULL) return;
830 
831     /*
832     * Release the current tree, removing any entry fields.
833     */
834     tvPtr->tree = treePtr;
835     root = Blt_TreeRootNode(treePtr);
836     Blt_TreeApply(root, DeleteApplyProc, tvPtr);
837     UntraceColumns(tvPtr);
838     /* TODO: release entries now if this was an external tree. */
839     /* ReleaseEntries(tvPtr); */
840     Blt_TreeViewClearSelection(tvPtr);
841     Blt_TreeReleaseToken(treePtr);
842     tvPtr->tree = newTree;
843 
844 }
845 
846 /*ARGSUSED*/
847 static int
FreeTree(ClientData clientData,Display * display,char * widgRec,int offset,char * oldPtr)848 FreeTree(
849     ClientData clientData,
850     Display *display,		/* Not used. */
851     char *widgRec,
852     int offset,
853     char *oldPtr)
854 {
855     Blt_Tree tree = (Blt_Tree)(oldPtr);
856     TreeView *tvPtr = (TreeView*)clientData;
857 
858     FreeTreeDo(tvPtr->interp, tvPtr, tree);
859     return TCL_OK;
860 }
861 
862 /*
863  *----------------------------------------------------------------------
864  *
865  * ObjToScrollmode --
866  *
867  *	Convert the string reprsenting a scroll mode, to its numeric
868  *	form.
869  *
870  * Results:
871  *	If the string is successfully converted, TCL_OK is returned.
872  *	Otherwise, TCL_ERROR is returned and an error message is left
873  *	in interpreter's result field.
874  *
875  *----------------------------------------------------------------------
876  */
877 /*ARGSUSED*/
878 static int
ObjToScrollmode(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)879 ObjToScrollmode(
880     ClientData clientData,	/* Not used. */
881     Tcl_Interp *interp,		/* Interpreter to send results back to */
882     Tk_Window tkwin,		/* Not used. */
883     Tcl_Obj *objPtr,		/* New legend position string */
884     char *widgRec,
885     int offset)
886 {
887     char *string;
888     char c;
889     int *modePtr = (int *)(widgRec + offset);
890 
891     string = Tcl_GetString(objPtr);
892     c = string[0];
893     if ((c == 'l') && (strcmp(string, "listbox") == 0)) {
894 	*modePtr = BLT_SCROLL_MODE_LISTBOX;
895     } else if ((c == 'h') && (strcmp(string, "hierbox") == 0)) {
896 	*modePtr = BLT_SCROLL_MODE_HIERBOX;
897     } else if ((c == 'c') && (strcmp(string, "canvas") == 0)) {
898 	*modePtr = BLT_SCROLL_MODE_CANVAS;
899     } else {
900 	Tcl_AppendResult(interp, "bad scroll mode \"", string,
901 	    "\": should be \"hierbox\", \"listbox\", or \"canvas\"",
902 		(char *)NULL);
903 	return TCL_ERROR;
904     }
905     return TCL_OK;
906 }
907 
908 /*
909  *----------------------------------------------------------------------
910  *
911  * ScrollmodeToObj --
912  *
913  * Results:
914  *	The string representation of the button boolean is returned.
915  *
916  *----------------------------------------------------------------------
917  */
918 /*ARGSUSED*/
919 static Tcl_Obj *
ScrollmodeToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)920 ScrollmodeToObj(
921     ClientData clientData,	/* Not used. */
922     Tcl_Interp *interp,
923     Tk_Window tkwin,		/* Not used. */
924     char *widgRec,
925     int offset)
926 {
927     int mode = *(int *)(widgRec + offset);
928 
929     switch (mode) {
930     case BLT_SCROLL_MODE_LISTBOX:
931 	return Tcl_NewStringObj("listbox", -1);
932     case BLT_SCROLL_MODE_HIERBOX:
933 	return Tcl_NewStringObj("hierbox", -1);
934     case BLT_SCROLL_MODE_CANVAS:
935 	return Tcl_NewStringObj("canvas", -1);
936     default:
937 	return Tcl_NewStringObj("unknown scroll mode", -1);
938     }
939 }
940 
941 /*
942  *----------------------------------------------------------------------
943  *
944  * ObjToSelectmode --
945  *
946  *	Convert the string reprsenting a scroll mode, to its numeric
947  *	form.
948  *
949  * Results:
950  *	If the string is successfully converted, TCL_OK is returned.
951  *	Otherwise, TCL_ERROR is returned and an error message is left
952  *	in interpreter's result field.
953  *
954  *----------------------------------------------------------------------
955  */
956 /*ARGSUSED*/
957 static int
ObjToSelectmode(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)958 ObjToSelectmode(
959     ClientData clientData,	/* Not used. */
960     Tcl_Interp *interp,		/* Interpreter to send results back to */
961     Tk_Window tkwin,		/* Not used. */
962     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
963     char *widgRec,
964     int offset)
965 {
966     char *string;
967     char c;
968     int *modePtr = (int *)(widgRec + offset);
969 
970     string = Tcl_GetString(objPtr);
971     c = string[0];
972     if ((c == 's') && (strcmp(string, "single") == 0)) {
973 	*modePtr = SELECT_MODE_SINGLE;
974     } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
975 	*modePtr = SELECT_MODE_MULTIPLE;
976     } else if ((c == 'c') && (strcmp(string, "cell") == 0)) {
977 	*modePtr = SELECT_MODE_CELL;
978     } else if ((c == 'm') && (strcmp(string, "multicell") == 0)) {
979 	*modePtr = SELECT_MODE_MCELL;
980     } else if ((c == 'n') && (strcmp(string, "none") == 0)) {
981 	*modePtr = SELECT_MODE_NONE;
982     } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
983 	*modePtr = SELECT_MODE_SINGLE;
984     } else {
985 	Tcl_AppendResult(interp, "bad select mode \"", string,
986 	    "\": should be \"single\", \"multiple\" \"cell\", \"multicell\", or \"none\"", (char *)NULL);
987 	return TCL_ERROR;
988     }
989     return TCL_OK;
990 }
991 
992 /*
993  *----------------------------------------------------------------------
994  *
995  * SelectmodeToObj --
996  *
997  * Results:
998  *	The string representation of the button boolean is returned.
999  *
1000  *----------------------------------------------------------------------
1001  */
1002 /*ARGSUSED*/
1003 static Tcl_Obj *
SelectmodeToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1004 SelectmodeToObj(
1005     ClientData clientData,	/* Not used. */
1006     Tcl_Interp *interp,
1007     Tk_Window tkwin,		/* Not used. */
1008     char *widgRec,
1009     int offset)
1010 {
1011     int mode = *(int *)(widgRec + offset);
1012 
1013     switch (mode) {
1014     case SELECT_MODE_SINGLE:
1015 	return Tcl_NewStringObj("single", -1);
1016     case SELECT_MODE_MULTIPLE:
1017 	return Tcl_NewStringObj("multiple", -1);
1018     case SELECT_MODE_CELL:
1019 	return Tcl_NewStringObj("cell", -1);
1020     case SELECT_MODE_MCELL:
1021 	return Tcl_NewStringObj("multicell", -1);
1022     case SELECT_MODE_NONE:
1023 	return Tcl_NewStringObj("none", -1);
1024     default:
1025 	return Tcl_NewStringObj("unknown scroll mode", -1);
1026     }
1027 }
1028 
1029 
1030 /*
1031  *----------------------------------------------------------------------
1032  *
1033  * ObjToButton --
1034  *
1035  *	Convert a string to one of three values.
1036  *		0 - false, no, off
1037  *		1 - true, yes, on
1038  *		2 - auto
1039  * Results:
1040  *	If the string is successfully converted, TCL_OK is returned.
1041  *	Otherwise, TCL_ERROR is returned and an error message is left in
1042  *	interpreter's result field.
1043  *
1044  *----------------------------------------------------------------------
1045  */
1046 /*ARGSUSED*/
1047 static int
ObjToButton(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)1048 ObjToButton(
1049     ClientData clientData,	/* Not used. */
1050     Tcl_Interp *interp,		/* Interpreter to send results back to */
1051     Tk_Window tkwin,		/* Not used. */
1052     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
1053     char *widgRec,
1054     int offset)
1055 {
1056     char *string;
1057     int *flagsPtr = (int *)(widgRec + offset);
1058 
1059     string = Tcl_GetString(objPtr);
1060     if ((string[0] == 'a') && (strcmp(string, "auto") == 0)) {
1061 	*flagsPtr &= ~BUTTON_MASK;
1062 	*flagsPtr |= BUTTON_AUTO;
1063     } else {
1064 	int bool;
1065 
1066 	if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
1067 	    return TCL_ERROR;
1068 	}
1069 	*flagsPtr &= ~BUTTON_MASK;
1070 	if (bool) {
1071 	    *flagsPtr |= BUTTON_SHOW;
1072 	}
1073     }
1074     return TCL_OK;
1075 }
1076 
1077 /*
1078  *----------------------------------------------------------------------
1079  *
1080  * ButtonToObj --
1081  *
1082  * Results:
1083  *	The string representation of the button boolean is returned.
1084  *
1085  *----------------------------------------------------------------------
1086  */
1087 /*ARGSUSED*/
1088 static Tcl_Obj *
ButtonToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1089 ButtonToObj(
1090     ClientData clientData,	/* Not used. */
1091     Tcl_Interp *interp,
1092     Tk_Window tkwin,		/* Not used. */
1093     char *widgRec,
1094     int offset)
1095 {
1096     int bool;
1097     unsigned int flags = *(int *)(widgRec + offset);
1098 
1099     bool = (flags & BUTTON_MASK);
1100     if (bool == BUTTON_AUTO) {
1101 	return Tcl_NewStringObj("auto", 4);
1102     } else {
1103 	return Tcl_NewBooleanObj(bool);
1104     }
1105 }
1106 
1107 /*
1108  *----------------------------------------------------------------------
1109  *
1110  * ObjToScrollmode --
1111  *
1112  *	Convert the string reprsenting a scroll mode, to its numeric
1113  *	form.
1114  *
1115  * Results:
1116  *	If the string is successfully converted, TCL_OK is returned.
1117  *	Otherwise, TCL_ERROR is returned and an error message is left
1118  *	in interpreter's result field.
1119  *
1120  *----------------------------------------------------------------------
1121  */
1122 /*ARGSUSED*/
1123 static int
ObjToSeparator(clientData,interp,tkwin,objPtr,widgRec,offset)1124 ObjToSeparator(clientData, interp, tkwin, objPtr, widgRec, offset)
1125     ClientData clientData;	/* Not used. */
1126     Tcl_Interp *interp;		/* Interpreter to send results back to */
1127     Tk_Window tkwin;		/* Not used. */
1128     Tcl_Obj *objPtr;		/* Tcl_Obj representing the new value. */
1129     char *widgRec;
1130     int offset;
1131 {
1132     char **sepPtr = (char **)(widgRec + offset);
1133     char *string;
1134 
1135     string = Tcl_GetString(objPtr);
1136     if (*string == '\0') {
1137 	*sepPtr = SEPARATOR_LIST;
1138     } else if (strcmp(string, "none") == 0) {
1139 	*sepPtr = SEPARATOR_NONE;
1140     } else {
1141 	*sepPtr = Blt_Strdup(string);
1142     }
1143     return TCL_OK;
1144 }
1145 
1146 /*
1147  *----------------------------------------------------------------------
1148  *
1149  * SeparatorToObj --
1150  *
1151  * Results:
1152  *	The string representation of the separator is returned.
1153  *
1154  *----------------------------------------------------------------------
1155  */
1156 /*ARGSUSED*/
1157 static Tcl_Obj *
SeparatorToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1158 SeparatorToObj(
1159     ClientData clientData,	/* Not used. */
1160     Tcl_Interp *interp,
1161     Tk_Window tkwin,		/* Not used. */
1162     char *widgRec,
1163     int offset)
1164 {
1165     char *separator = *(char **)(widgRec + offset);
1166 
1167     if (separator == SEPARATOR_NONE) {
1168         return Tcl_NewStringObj("", -1);
1169     } else if (separator == SEPARATOR_LIST) {
1170 	return Tcl_NewStringObj("list", -1);
1171     }  else {
1172 	return Tcl_NewStringObj(separator, -1);
1173     }
1174 }
1175 
1176 /*
1177  *----------------------------------------------------------------------
1178  *
1179  * FreeSeparator --
1180  *
1181  *	Free the UID from the widget record, setting it to NULL.
1182  *
1183  * Results:
1184  *	The UID in the widget record is set to NULL.
1185  *
1186  *----------------------------------------------------------------------
1187  */
1188 /*ARGSUSED*/
1189 static int
FreeSeparator(ClientData clientData,Display * display,char * widgRec,int offset,char * oldPtr)1190 FreeSeparator(
1191     ClientData clientData,
1192     Display *display,		/* Not used. */
1193     char *widgRec,
1194     int offset,
1195     char *oldPtr)
1196 {
1197     char *separator = oldPtr;
1198 
1199     if ((separator != SEPARATOR_LIST) && (separator != SEPARATOR_NONE)) {
1200 	Blt_Free(separator);
1201     }
1202     return TCL_OK;
1203 }
1204 
1205 /*
1206  *----------------------------------------------------------------------
1207  *
1208  * ObjToLabel --
1209  *
1210  *	Convert the string representing the label.
1211  *
1212  * Results:
1213  *	If the string is successfully converted, TCL_OK is returned.
1214  *	Otherwise, TCL_ERROR is returned and an error message is left
1215  *	in interpreter's result field.
1216  *
1217  *----------------------------------------------------------------------
1218  */
1219 /*ARGSUSED*/
1220 static int
ObjToLabel(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)1221 ObjToLabel(
1222     ClientData clientData,	/* Not used. */
1223     Tcl_Interp *interp,		/* Interpreter to send results back to */
1224     Tk_Window tkwin,		/* Not used. */
1225     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
1226     char *widgRec,
1227     int offset)
1228 {
1229     UID *labelPtr = (UID *)(widgRec + offset);
1230     char *string;
1231 
1232     string = Tcl_GetString(objPtr);
1233     if (string[0] != '\0') {
1234 	TreeView *tvPtr = clientData;
1235 
1236 	*labelPtr = Blt_TreeViewGetUid(tvPtr, string);
1237     }
1238     return TCL_OK;
1239 }
1240 
1241 /*
1242  *----------------------------------------------------------------------
1243  *
1244  * LabelToObj --
1245  *
1246  * Results:
1247  *	The string of the entry's label is returned.
1248  *
1249  *----------------------------------------------------------------------
1250  */
1251 /*ARGSUSED*/
1252 static Tcl_Obj *
LabelToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1253 LabelToObj(
1254     ClientData clientData,	/* Not used. */
1255     Tcl_Interp *interp,
1256     Tk_Window tkwin,		/* Not used. */
1257     char *widgRec,
1258     int offset)
1259 {
1260     UID labelUid = *(UID *)(widgRec + offset);
1261     char *string;
1262 
1263     if (labelUid == NULL) {
1264 	TreeViewEntry *entryPtr  = (TreeViewEntry *)widgRec;
1265 
1266 	string = Blt_TreeNodeLabel(entryPtr->node);
1267     } else {
1268 	string = labelUid;
1269     }
1270     return Tcl_NewStringObj(string, -1);
1271 }
1272 
1273 /*ARGSUSED*/
1274 static int
FreeLabel(ClientData clientData,Display * display,char * widgRec,int offset,char * oldPtr)1275 FreeLabel(
1276     ClientData clientData,
1277     Display *display,		/* Not used. */
1278     char *widgRec,
1279     int offset,
1280     char *oldPtr)
1281 {
1282     UID label = (UID)(oldPtr);
1283 
1284     if (label != NULL) {
1285 	TreeView *tvPtr = clientData;
1286 
1287 	Blt_TreeViewFreeUid(tvPtr, label);
1288     }
1289     return TCL_OK;
1290 }
1291 
1292 /*
1293  *----------------------------------------------------------------------
1294  *
1295  * Blt_TreeViewGetUid --
1296  *
1297  *	Gets or creates a unique string identifier.  Strings are
1298  *	reference counted.  The string is placed into a hashed table
1299  *	local to the treeview.
1300  *
1301  * Results:
1302  *	Returns the pointer to the hashed string.
1303  *
1304  *----------------------------------------------------------------------
1305  */
1306 UID
Blt_TreeViewGetUid(TreeView * tvPtr,CONST char * string)1307 Blt_TreeViewGetUid(TreeView *tvPtr, CONST char *string)
1308 {
1309     Blt_HashEntry *hPtr;
1310     int isNew;
1311     int refCount;
1312 
1313     hPtr = Blt_CreateHashEntry(&tvPtr->uidTable, string, &isNew);
1314     if (isNew) {
1315 	refCount = 1;
1316     } else {
1317 	refCount = (int)Blt_GetHashValue(hPtr);
1318 	refCount++;
1319     }
1320     Blt_SetHashValue(hPtr, (ClientData)refCount);
1321     return Blt_GetHashKey(&tvPtr->uidTable, hPtr);
1322 }
1323 
1324 /*
1325  *----------------------------------------------------------------------
1326  *
1327  * Blt_TreeViewFreeUid --
1328  *
1329  *	Releases the uid.  Uids are reference counted, so only when
1330  *	the reference count is zero (i.e. no one else is using the
1331  *	string) is the entry removed from the hash table.
1332  *
1333  * Results:
1334  *	None.
1335  *
1336  *----------------------------------------------------------------------
1337  */
1338 void
Blt_TreeViewFreeUid(TreeView * tvPtr,UID uid)1339 Blt_TreeViewFreeUid(TreeView *tvPtr, UID uid)
1340 {
1341     Blt_HashEntry *hPtr;
1342     int refCount;
1343 
1344     hPtr = Blt_FindHashEntry(&tvPtr->uidTable, uid);
1345     assert(hPtr != NULL);
1346     refCount = (int)Blt_GetHashValue(hPtr);
1347     refCount--;
1348     if (refCount > 0) {
1349 	Blt_SetHashValue(hPtr, (ClientData)refCount);
1350     } else {
1351 	Blt_DeleteHashEntry(&tvPtr->uidTable, hPtr);
1352     }
1353 }
1354 
1355 
1356 /*ARGSUSED*/
1357 static int
ObjToIsopen(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)1358 ObjToIsopen(
1359     ClientData clientData,	/* Not used. */
1360     Tcl_Interp *interp,		/* Interpreter to send results back to */
1361     Tk_Window tkwin,		/* Not used. */
1362     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
1363     char *widgRec,
1364     int offset)
1365 {
1366     TreeViewEntry *entryPtr = (TreeViewEntry *)(widgRec);
1367     TreeView *tvPtr = entryPtr->tvPtr;
1368     int isopen, open, result = TCL_OK;
1369 
1370     if (Tcl_GetBooleanFromObj(interp, objPtr, &isopen) != TCL_OK) {
1371         return TCL_ERROR;
1372     }
1373     open = ((entryPtr->flags&ENTRY_CLOSED) == 0);
1374     if (open != isopen) {
1375         if (isopen) {
1376             result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
1377             if (result == TCL_OK) {
1378                 entryPtr->flags &= (~ENTRY_CLOSED);
1379             }
1380         } else {
1381             result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
1382             if (result == TCL_OK) {
1383                 entryPtr->flags |= (ENTRY_CLOSED);
1384             }
1385         }
1386         tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1387         Blt_TreeViewEventuallyRedraw(tvPtr);
1388     }
1389     return result;
1390 }
1391 
1392 /*ARGSUSED*/
1393 static Tcl_Obj *
IsopenToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1394 IsopenToObj(
1395     ClientData clientData,	/* Not used. */
1396     Tcl_Interp *interp,
1397     Tk_Window tkwin,		/* Not used. */
1398     char *widgRec,
1399     int offset)
1400 {
1401     TreeViewEntry *entryPtr = (TreeViewEntry *)(widgRec);
1402     int open;
1403 
1404     open = ((entryPtr->flags&ENTRY_CLOSED) == 0);
1405     return Tcl_NewIntObj(open);
1406 }
1407 
1408 /*
1409  *----------------------------------------------------------------------
1410  *
1411  * ObjToUid --
1412  *
1413  *	Converts the string to a Uid. Uid's are hashed, reference
1414  *	counted strings.
1415  *
1416  *----------------------------------------------------------------------
1417  */
1418 /*ARGSUSED*/
1419 static int
ObjToUid(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)1420 ObjToUid(
1421     ClientData clientData,	/* Not used. */
1422     Tcl_Interp *interp,		/* Interpreter to send results back to */
1423     Tk_Window tkwin,		/* Not used. */
1424     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
1425     char *widgRec,
1426     int offset)
1427 {
1428     TreeView *tvPtr = clientData;
1429     UID *uidPtr = (UID *)(widgRec + offset);
1430     UID newId;
1431     char *string;
1432 
1433     newId = NULL;
1434     string = Tcl_GetString(objPtr);
1435     if (*string != '\0') {
1436 	newId = Blt_TreeViewGetUid(tvPtr, string);
1437     }
1438     *uidPtr = newId;
1439     return TCL_OK;
1440 }
1441 
1442 /*
1443  *----------------------------------------------------------------------
1444  *
1445  * UidToObj --
1446  *
1447  *	Returns the uid as a string.
1448  *
1449  * Results:
1450  *	The fill style string is returned.
1451  *
1452  *----------------------------------------------------------------------
1453  */
1454 /*ARGSUSED*/
1455 static Tcl_Obj *
UidToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1456 UidToObj(
1457     ClientData clientData,	/* Not used. */
1458     Tcl_Interp *interp,
1459     Tk_Window tkwin,		/* Not used. */
1460     char *widgRec,
1461     int offset)
1462 {
1463     UID uid = *(UID *)(widgRec + offset);
1464 
1465     return Tcl_NewStringObj(uid?uid:"", -1);
1466 }
1467 
1468 /*
1469  *----------------------------------------------------------------------
1470  *
1471  * FreeUid --
1472  *
1473  *	Free the UID from the widget record, setting it to NULL.
1474  *
1475  * Results:
1476  *	The UID in the widget record is set to NULL.
1477  *
1478  *----------------------------------------------------------------------
1479  */
1480 /*ARGSUSED*/
1481 static int
FreeUid(ClientData clientData,Display * display,char * widgRec,int offset,char * oldPtr)1482 FreeUid(
1483     ClientData clientData,
1484     Display *display,		/* Not used. */
1485     char *widgRec,
1486     int offset,
1487     char *oldPtr)
1488 {
1489     UID uid = (UID)(oldPtr);
1490 
1491     if (uid != NULL) {
1492 	TreeView *tvPtr = clientData;
1493 
1494 	Blt_TreeViewFreeUid(tvPtr, uid);
1495     }
1496     return TCL_OK;
1497 }
1498 
1499 void
Blt_TreeViewRelayout(TreeView * tvPtr)1500 Blt_TreeViewRelayout(TreeView *tvPtr) {
1501 
1502     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT | TV_UPDATE |TV_SCROLL|TV_DIRTYALL);
1503     Blt_TreeViewEventuallyRedraw(tvPtr);
1504 }
1505 
1506 
1507 /*
1508  *----------------------------------------------------------------------
1509  *
1510  * IconChangedProc
1511  *
1512  *
1513  * Results:
1514  *	None.
1515  *
1516  *----------------------------------------------------------------------
1517  */
1518 /* ARGSUSED */
1519 static void
IconChangedProc(ClientData clientData,int x,int y,int width,int height,int imageWidth,int imageHeight)1520 IconChangedProc(
1521     ClientData clientData,
1522     int x,			/* Not used. */
1523     int y,			/* Not used. */
1524     int width,			/* Not used. */
1525     int height,			/* Not used. */
1526     int imageWidth, 		/* Not used. */
1527     int imageHeight)		/* Not used. */
1528 {
1529     TreeViewIcon iconPtr = clientData;
1530     TreeView *tvPtr = iconPtr->tvPtr;
1531     if (iconPtr->width != width || iconPtr->height != height) {
1532     }
1533     iconPtr->width = width;
1534     iconPtr->height = height;
1535     Blt_TreeViewRelayout(tvPtr);
1536 }
1537 
1538 TreeViewIcon
Blt_TreeViewGetIcon(TreeView * tvPtr,CONST char * iconName)1539 Blt_TreeViewGetIcon(TreeView *tvPtr, CONST char *iconName)
1540 {
1541     Blt_HashEntry *hPtr;
1542     int isNew;
1543     struct TreeViewIconStruct *iconPtr;
1544 
1545     hPtr = Blt_CreateHashEntry(&tvPtr->iconTable, iconName, &isNew);
1546     if (isNew) {
1547 	Tk_Image tkImage;
1548 	int width, height;
1549 
1550          iconPtr = Blt_Calloc(1, sizeof(struct TreeViewIconStruct));
1551          tkImage = Tk_GetImage(tvPtr->interp, tvPtr->tkwin, (char *)iconName,
1552 		IconChangedProc, iconPtr);
1553 	if (tkImage == NULL) {
1554 	    Blt_DeleteHashEntry(&tvPtr->iconTable, hPtr);
1555 	    Blt_Free(iconPtr);
1556 	    return NULL;
1557 	}
1558 	Tk_SizeOfImage(tkImage, &width, &height);
1559 	iconPtr->tvPtr = tvPtr;
1560 	iconPtr->tkImage = tkImage;
1561 	iconPtr->hashPtr = hPtr;
1562 	iconPtr->refCount = 1;
1563 	iconPtr->width = width;
1564 	iconPtr->height = height;
1565 	Blt_SetHashValue(hPtr, iconPtr);
1566     } else {
1567 	iconPtr = Blt_GetHashValue(hPtr);
1568 	iconPtr->refCount++;
1569     }
1570     return iconPtr;
1571 }
1572 
1573 void
Blt_TreeViewFreeIcon(TreeView * tvPtr,struct TreeViewIconStruct * iconPtr)1574 Blt_TreeViewFreeIcon(
1575     TreeView *tvPtr,
1576     struct TreeViewIconStruct *iconPtr)
1577 {
1578     iconPtr->refCount--;
1579     if (iconPtr->refCount == 0) {
1580 	Blt_DeleteHashEntry(&tvPtr->iconTable, iconPtr->hashPtr);
1581 	Tk_FreeImage(iconPtr->tkImage);
1582 	Blt_Free(iconPtr);
1583     }
1584 }
1585 
1586 static void
DumpIconTable(TreeView * tvPtr)1587 DumpIconTable(TreeView *tvPtr)
1588 {
1589     Blt_HashEntry *hPtr;
1590     Blt_HashSearch cursor;
1591     struct TreeViewIconStruct *iconPtr;
1592 
1593     for (hPtr = Blt_FirstHashEntry(&tvPtr->iconTable, &cursor);
1594 	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1595 	iconPtr = Blt_GetHashValue(hPtr);
1596 	Tk_FreeImage(iconPtr->tkImage);
1597 	Blt_Free(iconPtr);
1598     }
1599     Blt_DeleteHashTable(&tvPtr->iconTable);
1600 }
1601 
1602 /*
1603  *----------------------------------------------------------------------
1604  *
1605  * ObjToIcons --
1606  *
1607  *	Convert a list of image names into Tk images.
1608  *
1609  * Results:
1610  *	If the string is successfully converted, TCL_OK is returned.
1611  *	Otherwise, TCL_ERROR is returned and an error message is left in
1612  *	interpreter's result field.
1613  *
1614  *----------------------------------------------------------------------
1615  */
1616 /*ARGSUSED*/
1617 static int
ObjToIcons(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * objPtr,char * widgRec,int offset)1618 ObjToIcons(
1619     ClientData clientData,	/* Not used. */
1620     Tcl_Interp *interp,		/* Interpreter to send results back to */
1621     Tk_Window tkwin,		/* Not used. */
1622     Tcl_Obj *objPtr,		/* Tcl_Obj representing the new value. */
1623     char *widgRec,
1624     int offset)
1625 {
1626     Tcl_Obj **objv;
1627     TreeView *tvPtr = clientData;
1628     TreeViewIcon **iconPtrPtr = (TreeViewIcon **)(widgRec + offset);
1629     TreeViewIcon *icons;
1630     int objc;
1631     int result;
1632 
1633     result = TCL_OK;
1634     icons = NULL;
1635     if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1636 	return TCL_ERROR;
1637     }
1638     if (objc > 2) {
1639         Tcl_AppendResult(interp, "expected 0, 1 or 2 icons", 0);
1640     }
1641     if (objc > 0) {
1642 	register int i;
1643 
1644 	icons = Blt_Calloc(3, sizeof(TreeViewIcon *));
1645 	assert(icons);
1646 	for (i = 0; i < objc && i < 2; i++) {
1647 	    icons[i] = Blt_TreeViewGetIcon(tvPtr, Tcl_GetString(objv[i]));
1648 	    if (icons[i] == NULL) {
1649 		result = TCL_ERROR;
1650 		break;
1651 	    }
1652 	}
1653 	icons[i] = NULL;
1654     }
1655     *iconPtrPtr = icons;
1656     return result;
1657 }
1658 
1659 /*
1660  *----------------------------------------------------------------------
1661  *
1662  * IconsToObj --
1663  *
1664  *	Converts the icon into its string representation (its name).
1665  *
1666  * Results:
1667  *	The name of the icon is returned.
1668  *
1669  *----------------------------------------------------------------------
1670  */
1671 /*ARGSUSED*/
1672 static Tcl_Obj *
IconsToObj(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,char * widgRec,int offset)1673 IconsToObj(
1674     ClientData clientData,	/* Not used. */
1675     Tcl_Interp *interp,
1676     Tk_Window tkwin,		/* Not used. */
1677     char *widgRec,
1678     int offset)
1679 {
1680     TreeViewIcon *icons = *(TreeViewIcon **)(widgRec + offset);
1681     Tcl_Obj *listObjPtr;
1682 
1683     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1684     if (icons != NULL) {
1685 	register TreeViewIcon *iconPtr;
1686 	Tcl_Obj *objPtr;
1687 
1688 	for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
1689 	    objPtr = Tcl_NewStringObj(Blt_NameOfImage((*iconPtr)->tkImage), -1);
1690 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1691 
1692 	}
1693     }
1694     return listObjPtr;
1695 }
1696 
1697 /*ARGSUSED*/
1698 static int
FreeIcons(ClientData clientData,Display * display,char * widgRec,int offset,char * oldPtr)1699 FreeIcons(
1700     ClientData clientData,
1701     Display *display,		/* Not used. */
1702     char *widgRec,
1703     int offset,
1704     char *oldPtr)
1705 {
1706     TreeViewIcon *icons = (TreeViewIcon *)(oldPtr);
1707 
1708     if (icons != NULL) {
1709 	register TreeViewIcon *iconPtr;
1710 	TreeView *tvPtr = clientData;
1711 
1712 	for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
1713 	    Blt_TreeViewFreeIcon(tvPtr, *iconPtr);
1714 	}
1715 	Blt_Free(icons);
1716     }
1717     return TCL_OK;
1718 }
1719 
1720 TreeViewEntry *
Blt_NodeToEntry(TreeView * tvPtr,Blt_TreeNode node)1721 Blt_NodeToEntry(TreeView *tvPtr, Blt_TreeNode node)
1722 {
1723     Blt_HashEntry *hPtr;
1724 
1725     hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
1726     if (hPtr == NULL) {
1727 	return NULL;
1728     }
1729     return Blt_GetHashValue(hPtr);
1730 }
1731 
1732 int
Blt_TreeViewApply(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewApplyProc * proc,unsigned int flags)1733 Blt_TreeViewApply(
1734     TreeView *tvPtr,
1735     TreeViewEntry *entryPtr,	/* Root entry of subtree. */
1736     TreeViewApplyProc *proc,	/* Procedure to call for each entry. */
1737     unsigned int flags)
1738 {
1739     if ((flags & ENTRY_HIDDEN) &&
1740 	(Blt_TreeViewEntryIsHidden(entryPtr))) {
1741 	return TCL_OK;		/* Hidden node. */
1742     }
1743     if ((flags & ENTRY_HIDDEN) && (entryPtr->flags & ENTRY_HIDDEN)) {
1744 	return TCL_OK;		/* Hidden node. */
1745     }
1746     if (((flags & ENTRY_CLOSED) == 0) ||
1747 	((entryPtr->flags & ENTRY_CLOSED) == 0)) {
1748 	TreeViewEntry *childPtr;
1749 	Blt_TreeNode node, next;
1750 
1751 	for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
1752 	     node = next) {
1753 	    next = Blt_TreeNextSibling(node);
1754 	    /*
1755 	     * Get the next child before calling Blt_TreeViewApply
1756 	     * recursively.  This is because the apply callback may
1757 	     * delete the node and its link.
1758 	     */
1759 	    childPtr = Blt_NodeToEntry(tvPtr, node);
1760 	    if (Blt_TreeViewApply(tvPtr, childPtr, proc, flags) != TCL_OK) {
1761 		return TCL_ERROR;
1762 	    }
1763 	}
1764     }
1765     if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
1766 	return TCL_ERROR;
1767     }
1768     return TCL_OK;
1769 }
1770 
1771 int
Blt_TreeViewIsLeaf(TreeViewEntry * entryPtr)1772 Blt_TreeViewIsLeaf(TreeViewEntry *entryPtr) {
1773     if (entryPtr->flags&ENTRY_IS_TREE) return FALSE;
1774     return Blt_TreeIsLeaf(entryPtr->node);
1775 }
1776 
1777 int
Blt_TreeViewEntryIsHidden(TreeViewEntry * entryPtr)1778 Blt_TreeViewEntryIsHidden(TreeViewEntry *entryPtr)
1779 {
1780     TreeView *tvPtr = entryPtr->tvPtr;
1781 
1782     if ((tvPtr->flags & TV_HIDE_ROOT) && (entryPtr == tvPtr->rootPtr)) {
1783         return TRUE;
1784     }
1785 
1786    /* if (entryPtr->hide) {
1787         return TRUE;
1788     } */
1789     if ((tvPtr->flags & TV_HIDE_LEAVES) && (Blt_TreeViewIsLeaf(entryPtr))) {
1790 	return TRUE;
1791     }
1792     /*if (entryPtr->stylePtr && entryPtr->stylePtr->hidden) {
1793         return TRUE;
1794     }*/
1795     return (entryPtr->flags & ENTRY_HIDDEN) ? TRUE : FALSE;
1796 }
1797 
1798 int
Blt_TreeViewEntryIsMapped(TreeViewEntry * entryPtr)1799 Blt_TreeViewEntryIsMapped(TreeViewEntry *entryPtr)
1800 {
1801     TreeView *tvPtr = entryPtr->tvPtr;
1802     int n;
1803 
1804     if (tvPtr->visibleArr == NULL) {
1805         return FALSE;
1806     }
1807     for (n = 0; n < tvPtr->nVisible; n++) {
1808         if (tvPtr->visibleArr[n] == entryPtr) {
1809             return TRUE;
1810         }
1811     }
1812     return FALSE;
1813 }
1814 
1815 TreeViewEntry *
Blt_TreeViewFirstChild(TreeViewEntry * entryPtr,unsigned int mask)1816 Blt_TreeViewFirstChild(TreeViewEntry *entryPtr, unsigned int mask)
1817 {
1818     Blt_TreeNode node;
1819     TreeView *tvPtr = entryPtr->tvPtr;
1820 #ifdef __BOUNDS_CHECKING_ON
1821     __bounds_debug_no_checking = 1;
1822 #endif
1823 
1824     for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
1825 	 node = Blt_TreeNextSibling(node)) {
1826 	entryPtr = Blt_NodeToEntry(tvPtr, node);
1827          if ((mask & ENTRY_ONLYHIDDEN)) {
1828              if (Blt_TreeViewEntryIsHidden(entryPtr)) {
1829                  goto done;
1830              }
1831          } else if (((mask & ENTRY_HIDDEN) == 0) ||
1832 	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1833 	    goto done;
1834 	}
1835     }
1836     entryPtr = NULL;
1837     done:
1838 #ifdef __BOUNDS_CHECKING_ON
1839     __bounds_debug_no_checking = 0;
1840 #endif
1841     return entryPtr;
1842 }
1843 
1844 TreeViewEntry *
Blt_TreeViewLastChild(TreeViewEntry * entryPtr,unsigned int mask)1845 Blt_TreeViewLastChild(TreeViewEntry *entryPtr, unsigned int mask)
1846 {
1847     Blt_TreeNode node;
1848     TreeView *tvPtr = entryPtr->tvPtr;
1849 
1850     for (node = Blt_TreeLastChild(entryPtr->node); node != NULL;
1851 	 node = Blt_TreePrevSibling(node)) {
1852 	entryPtr = Blt_NodeToEntry(tvPtr, node);
1853          if ((mask & ENTRY_ONLYHIDDEN)) {
1854              if (Blt_TreeViewEntryIsHidden(entryPtr)) {
1855                  return entryPtr;
1856              }
1857          } else if (((mask & ENTRY_HIDDEN) == 0) ||
1858 	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1859 	    return entryPtr;
1860 	}
1861     }
1862     return NULL;
1863 }
1864 
1865 TreeViewEntry *
Blt_TreeViewNextSibling(TreeViewEntry * entryPtr,unsigned int mask)1866 Blt_TreeViewNextSibling(TreeViewEntry *entryPtr, unsigned int mask)
1867 {
1868     Blt_TreeNode node;
1869     TreeView *tvPtr = entryPtr->tvPtr;
1870 
1871     for (node = Blt_TreeNextSibling(entryPtr->node); node != NULL;
1872 	 node = Blt_TreeNextSibling(node)) {
1873 	entryPtr = Blt_NodeToEntry(tvPtr, node);
1874          if ((mask & ENTRY_ONLYHIDDEN)) {
1875              if (Blt_TreeViewEntryIsHidden(entryPtr)) {
1876                  return entryPtr;
1877              }
1878          } else if (((mask & ENTRY_HIDDEN) == 0) ||
1879 	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1880 	    return entryPtr;
1881 	}
1882     }
1883     return NULL;
1884 }
1885 
1886 TreeViewEntry *
Blt_TreeViewPrevSibling(TreeViewEntry * entryPtr,unsigned int mask)1887 Blt_TreeViewPrevSibling(TreeViewEntry *entryPtr, unsigned int mask)
1888 {
1889     Blt_TreeNode node;
1890     TreeView *tvPtr = entryPtr->tvPtr;
1891 
1892     for (node = Blt_TreePrevSibling(entryPtr->node); node != NULL;
1893 	 node = Blt_TreePrevSibling(node)) {
1894 	entryPtr = Blt_NodeToEntry(tvPtr, node);
1895 	if ((mask & ENTRY_ONLYHIDDEN)) {
1896              if (Blt_TreeViewEntryIsHidden(entryPtr)) {
1897                  return entryPtr;
1898              }
1899 	} else if (((mask & ENTRY_HIDDEN) == 0) ||
1900 	    (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1901 	    return entryPtr;
1902 	}
1903     }
1904     return NULL;
1905 }
1906 
1907 /*
1908  *----------------------------------------------------------------------
1909  *
1910  * Blt_TreeViewPrevEntry --
1911  *
1912  *	Returns the "previous" node in the tree.  This node (in
1913  *	depth-first order) is its parent if the node has no siblings
1914  *	that are previous to it.  Otherwise it is the last descendant
1915  *	of the last sibling.  In this case, descend the sibling's
1916  *	hierarchy, using the last child at any ancestor, until we
1917  *	we find a leaf.
1918  *
1919  *----------------------------------------------------------------------
1920  */
1921 TreeViewEntry *
Blt_TreeViewPrevEntry(TreeViewEntry * entryPtr,unsigned int mask)1922 Blt_TreeViewPrevEntry(TreeViewEntry *entryPtr, unsigned int mask)
1923 {
1924     TreeView *tvPtr = entryPtr->tvPtr;
1925     TreeViewEntry *prevPtr;
1926 
1927     if (entryPtr->node == tvPtr->rootNode) {
1928 	return NULL;		/* The root is the first node. */
1929     }
1930     prevPtr = Blt_TreeViewPrevSibling(entryPtr, mask);
1931     if (prevPtr == NULL) {
1932 	/* There are no siblings previous to this one, so pick the parent. */
1933 	prevPtr = Blt_TreeViewParentEntry(entryPtr);
1934     } else {
1935 	/*
1936 	 * Traverse down the right-most thread in order to select the
1937 	 * last entry.  Stop if we find a "closed" entry or reach a leaf.
1938 	 */
1939 	entryPtr = prevPtr;
1940 	while ((entryPtr->flags & mask) == 0) {
1941 	    entryPtr = Blt_TreeViewLastChild(entryPtr, mask);
1942 	    if (entryPtr == NULL) {
1943 		break;		/* Found a leaf. */
1944 	    }
1945 	    prevPtr = entryPtr;
1946 	}
1947     }
1948     if (prevPtr == NULL) {
1949 	return NULL;
1950     }
1951     return prevPtr;
1952 }
1953 
1954 
1955 /*
1956  *----------------------------------------------------------------------
1957  *
1958  * Blt_TreeViewNextNode --
1959  *
1960  *	Returns the "next" node in relation to the given node.
1961  *	The next node (in depth-first order) is either the first
1962  *	child of the given node the next sibling if the node has
1963  *	no children (the node is a leaf).  If the given node is the
1964  *	last sibling, then try it's parent next sibling.  Continue
1965  *	until we either find a next sibling for some ancestor or
1966  *	we reach the root node.  In this case the current node is
1967  *	the last node in the tree.
1968  *
1969  *----------------------------------------------------------------------
1970  */
1971 TreeViewEntry *
Blt_TreeViewNextEntry(TreeViewEntry * entryPtr,unsigned int mask)1972 Blt_TreeViewNextEntry(TreeViewEntry *entryPtr, unsigned int mask)
1973 {
1974     TreeView *tvPtr = entryPtr->tvPtr;
1975     TreeViewEntry *nextPtr;
1976     int ignoreLeaf;
1977 
1978     ignoreLeaf = ((tvPtr->flags & TV_HIDE_LEAVES) &&
1979 		  (Blt_TreeViewIsLeaf(entryPtr)));
1980 
1981     if ((!ignoreLeaf) && ((entryPtr->flags & mask) == 0)) {
1982 	nextPtr = Blt_TreeViewFirstChild(entryPtr, mask);
1983 	if (nextPtr != NULL) {
1984 	    return nextPtr;	/* Pick the first sub-node. */
1985 	}
1986     }
1987 
1988 
1989     /*
1990      * Back up until to a level where we can pick a "next sibling".
1991      * For the last entry we'll thread our way back to the root.
1992      */
1993 
1994     while (entryPtr != NULL && entryPtr != tvPtr->rootPtr) {
1995 	nextPtr = Blt_TreeViewNextSibling(entryPtr, mask);
1996 	if (nextPtr != NULL) {
1997 	    return nextPtr;
1998 	}
1999 	entryPtr = Blt_TreeViewParentEntry(entryPtr);
2000     }
2001     return NULL;		/* At root, no next node. */
2002 }
2003 
2004 void
Blt_TreeViewConfigureButtons(TreeView * tvPtr)2005 Blt_TreeViewConfigureButtons(TreeView *tvPtr)
2006 {
2007     GC newGC;
2008     TreeViewButton *buttonPtr = &tvPtr->button;
2009     XGCValues gcValues;
2010     unsigned long gcMask;
2011 
2012     gcMask = GCForeground;
2013     gcValues.foreground = buttonPtr->fgColor->pixel;
2014     newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
2015     if (buttonPtr->normalGC != NULL) {
2016 	Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
2017     }
2018     buttonPtr->normalGC = newGC;
2019 
2020     gcMask = GCForeground;
2021     gcValues.foreground = buttonPtr->activeFgColor->pixel;
2022     newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
2023     if (buttonPtr->activeGC != NULL) {
2024 	Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
2025     }
2026     buttonPtr->activeGC = newGC;
2027 
2028     buttonPtr->width = buttonPtr->height = ODD(buttonPtr->reqSize);
2029     if (buttonPtr->icons != NULL) {
2030 	register int i;
2031 	int width, height;
2032 
2033 	for (i = 0; i < 2; i++) {
2034 	    if (buttonPtr->icons[i] == NULL) {
2035 		break;
2036 	    }
2037 	    width = TreeViewIconWidth(buttonPtr->icons[i]);
2038 	    height = TreeViewIconWidth(buttonPtr->icons[i]);
2039 	    if (buttonPtr->width < width) {
2040 		buttonPtr->width = width;
2041 	    }
2042 	    if (buttonPtr->height < height) {
2043 		buttonPtr->height = height;
2044 	    }
2045 	}
2046     }
2047     buttonPtr->width += 2 * buttonPtr->borderWidth;
2048     buttonPtr->height += 2 * buttonPtr->borderWidth;
2049 }
2050 
2051 int
Blt_TreeViewConfigureEntry(TreeView * tvPtr,TreeViewEntry * entryPtr,int objc,Tcl_Obj * CONST * objv,int flags)2052 Blt_TreeViewConfigureEntry(
2053     TreeView *tvPtr,
2054     TreeViewEntry *entryPtr,
2055     int objc,
2056     Tcl_Obj *CONST *objv,
2057     int flags)
2058 {
2059     GC newGC;
2060     Blt_ChainLink *linkPtr;
2061     TreeViewColumn *columnPtr;
2062     int isdel;
2063 
2064     Blt_TreeViewOptsInit(tvPtr);
2065     Tcl_Preserve(entryPtr);
2066     if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
2067 	bltTreeViewEntrySpecs, objc, objv, (char *)entryPtr, flags, NULL) != TCL_OK) {
2068         Tcl_Release(entryPtr);
2069 	return TCL_ERROR;
2070     }
2071     isdel = (entryPtr->flags & ENTRY_DELETED);
2072     Tcl_Release(entryPtr);
2073     if (isdel || (tvPtr->flags & TV_DELETED)) {
2074 	return TCL_ERROR;
2075     }
2076     /*
2077      * Check if there are values that need to be added
2078      */
2079     for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
2080 	linkPtr = Blt_ChainNextLink(linkPtr)) {
2081 	columnPtr = Blt_ChainGetValue(linkPtr);
2082 	Blt_TreeViewAddValue(entryPtr, columnPtr);
2083     }
2084 
2085     newGC = NULL;
2086     if ((entryPtr->font != NULL) || (entryPtr->color != NULL)) {
2087 	Tk_Font font;
2088 	XColor *colorPtr;
2089 	XGCValues gcValues;
2090 	unsigned long gcMask;
2091 
2092 	font = entryPtr->font;
2093 	if (font == NULL) {
2094              font = Blt_TreeViewGetStyleFont(tvPtr, &tvPtr->treeColumn, tvPtr->treeColumn.stylePtr);
2095 	}
2096 	colorPtr = CHOOSE(tvPtr->fgColor, entryPtr->color);
2097 	gcMask = GCForeground | GCFont;
2098 	gcValues.foreground = colorPtr->pixel;
2099 	gcValues.font = Tk_FontId(font);
2100 	newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
2101     }
2102     if (entryPtr->gc != NULL) {
2103 	Tk_FreeGC(tvPtr->display, entryPtr->gc);
2104     }
2105     /* Assume all changes require a new layout. */
2106     entryPtr->gc = newGC;
2107     entryPtr->flags |= ENTRY_LAYOUT_PENDING;
2108     if (Blt_ObjConfigModified(bltTreeViewEntrySpecs, tvPtr->interp, "-font", "-hide*","-icons", "-*style*", "-state",
2109         (char *)NULL)) {
2110         entryPtr->flags |= ENTRY_REDRAW | ENTRY_DIRTY;
2111         tvPtr->flags |= TV_UPDATE;
2112         /*Blt_TreeViewMakeStyleDirty(tvPtr); */
2113     }
2114     if (Blt_ObjConfigModified(bltTreeViewEntrySpecs, tvPtr->interp, "-style", (char *)NULL)) {
2115         if (entryPtr->stylePtr && 'W' == *entryPtr->stylePtr->classPtr->className ) {
2116             Blt_TreeViewFreeStyle(tvPtr, entryPtr->realStylePtr);
2117             entryPtr->realStylePtr = NULL;
2118             entryPtr->stylePtr = NULL;
2119             return TCL_ERROR;
2120         }
2121     }
2122     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2123     Blt_ObjConfigModified(bltTreeViewEntrySpecs, tvPtr->interp, (char *)NULL);
2124     return TCL_OK;
2125 }
2126 
2127 void
Blt_TreeViewDestroyValue(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewValue * valuePtr)2128 Blt_TreeViewDestroyValue(TreeView *tvPtr, TreeViewEntry *entryPtr, TreeViewValue *valuePtr)
2129 {
2130     if (valuePtr->stylePtr != NULL) {
2131 	Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
2132     }
2133     if (valuePtr->textPtr != NULL) {
2134 	Blt_Free(valuePtr->textPtr);
2135     }
2136     Blt_PoolFreeItem(entryPtr->tvPtr->valuePool, valuePtr);
2137 }
2138 
Blt_TreeViewDeleteValue(TreeViewEntry * entryPtr,Blt_TreeKey key)2139 void Blt_TreeViewDeleteValue(TreeViewEntry* entryPtr, Blt_TreeKey key) {
2140     TreeView *tvPtr;
2141     TreeViewValue *lastPtr, *nextPtr, *valuePtr;
2142 
2143     tvPtr = entryPtr->tvPtr;
2144     lastPtr = NULL;
2145     for(valuePtr = entryPtr->values; valuePtr != NULL;
2146         valuePtr = nextPtr) {
2147         nextPtr = valuePtr->nextPtr;
2148         if (valuePtr->columnPtr->key == key) {
2149             Blt_TreeViewWindowUpdate(entryPtr, valuePtr->columnPtr);
2150             Blt_TreeViewDestroyValue(tvPtr, entryPtr, valuePtr);
2151             if (lastPtr == NULL) {
2152                 entryPtr->values = nextPtr;
2153             } else {
2154                 lastPtr->nextPtr = nextPtr;
2155             }
2156             entryPtr->flags |= ENTRY_DIRTY;
2157             Blt_TreeViewEventuallyRedraw(tvPtr);
2158             tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2159             break;
2160         }
2161         lastPtr = valuePtr;
2162     }
2163 }
2164 
2165 static void
DestroyEntry(DestroyData data)2166 DestroyEntry(DestroyData data)
2167 {
2168     TreeViewEntry *entryPtr = (TreeViewEntry *)data;
2169     TreeView *tvPtr;
2170 
2171     tvPtr = entryPtr->tvPtr;
2172     Blt_TreeViewOptsInit(tvPtr);
2173     Blt_FreeObjOptions(tvPtr->interp,
2174         bltTreeViewEntrySpecs, (char *)entryPtr, tvPtr->display, 0);
2175     if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
2176 	/* Don't clear tags unless this client is the only one using
2177 	 * the tag table.*/
2178 	Blt_TreeClearTags(tvPtr->tree, entryPtr->node);
2179     }
2180     if (tvPtr->selAnchorPtr == entryPtr) { tvPtr->selAnchorPtr = NULL; }
2181     if (tvPtr->selMarkPtr == entryPtr) { tvPtr->selMarkPtr = NULL; }
2182     if (tvPtr->activePtr == entryPtr) { tvPtr->activePtr = NULL; }
2183     if (tvPtr->focusPtr == entryPtr) { tvPtr->focusPtr = NULL; }
2184     if (tvPtr->activeButtonPtr == entryPtr) { tvPtr->activeButtonPtr = NULL; }
2185     if (tvPtr->fromPtr == entryPtr) { tvPtr->fromPtr = NULL; }
2186     if (entryPtr->gc != NULL) {
2187 	Tk_FreeGC(tvPtr->display, entryPtr->gc);
2188         entryPtr->gc = NULL;
2189     }
2190     if (entryPtr->shadow.color != NULL) {
2191 	Tk_FreeColor(entryPtr->shadow.color);
2192 	entryPtr->shadow.color = NULL;
2193     }
2194     /* Delete the chain of data values from the entry. */
2195     if (entryPtr->values != NULL) {
2196 	TreeViewValue *valuePtr, *nextPtr;
2197 
2198 	for (valuePtr = entryPtr->values; valuePtr != NULL;
2199 	     valuePtr = nextPtr) {
2200 	    nextPtr = valuePtr->nextPtr;
2201 	    Blt_TreeViewDestroyValue(tvPtr, entryPtr, valuePtr);
2202 	}
2203 	entryPtr->values = NULL;
2204     }
2205     if (entryPtr->fullName != NULL) {
2206 	Blt_Free(entryPtr->fullName);
2207 	entryPtr->fullName = NULL;
2208     }
2209     if (entryPtr->textPtr != NULL) {
2210 	Blt_Free(entryPtr->textPtr);
2211          entryPtr->textPtr = NULL;
2212     }
2213     if (entryPtr->subTextPtr != NULL) {
2214         Blt_Free(entryPtr->subTextPtr);
2215         entryPtr->subTextPtr = NULL;
2216     }
2217     if (entryPtr->realStylePtr != NULL) {
2218         Blt_TreeViewFreeStyle(tvPtr, entryPtr->realStylePtr);
2219         entryPtr->realStylePtr = NULL;
2220     }
2221 
2222     Blt_PoolFreeItem(tvPtr->entryPool, entryPtr);
2223 }
2224 
2225 TreeViewEntry *
Blt_TreeViewParentEntry(TreeViewEntry * entryPtr)2226 Blt_TreeViewParentEntry(TreeViewEntry *entryPtr)
2227 {
2228     TreeView *tvPtr = entryPtr->tvPtr;
2229     Blt_TreeNode node;
2230 
2231     if (entryPtr->node == NULL || entryPtr->node == tvPtr->rootNode) {
2232 	return NULL;
2233     }
2234     node = Blt_TreeNodeParent(entryPtr->node);
2235     if (node == NULL) {
2236 	return NULL;
2237     }
2238     return Blt_NodeToEntry(tvPtr, node);
2239 }
2240 
2241 void
Blt_TreeViewFreeEntry(TreeView * tvPtr,TreeViewEntry * entryPtr)2242 Blt_TreeViewFreeEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
2243 {
2244     Blt_HashEntry *hPtr;
2245 
2246     if (entryPtr == NULL) return;
2247     entryPtr->flags |= ENTRY_DELETED;
2248     if (entryPtr == tvPtr->activePtr) {
2249 	tvPtr->activePtr = Blt_TreeViewParentEntry(entryPtr);
2250     }
2251     if (entryPtr == tvPtr->activeButtonPtr) {
2252 	tvPtr->activeButtonPtr = NULL;
2253     }
2254     if (entryPtr == tvPtr->focusPtr) {
2255 	tvPtr->focusPtr = Blt_TreeViewParentEntry(entryPtr);
2256 	Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
2257     }
2258     if (entryPtr == tvPtr->selAnchorPtr) {
2259 	tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
2260     }
2261     if (entryPtr->flags & ENTRY_WINDOW) {
2262         Blt_TreeViewWindowRelease(entryPtr, NULL);
2263     }
2264     Blt_TreeViewDeselectEntry(tvPtr, entryPtr, NULL);
2265     Blt_TreeViewPruneSelection(tvPtr, entryPtr);
2266     Blt_DeleteBindings(tvPtr->bindTable, entryPtr);
2267     hPtr = Blt_FindHashEntry(&tvPtr->entryTable, entryPtr->node);
2268     if (hPtr != NULL) {
2269 	Blt_DeleteHashEntry(&tvPtr->entryTable, hPtr);
2270     }
2271     entryPtr->node = NULL;
2272 
2273     Tcl_EventuallyFree(entryPtr, DestroyEntry);
2274     /*
2275      * Indicate that the screen layout of the hierarchy may have changed
2276      * because this node was deleted.  The screen positions of the nodes
2277      * in tvPtr->visibleArr are invalidated.
2278      */
2279     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2280     Blt_TreeViewEventuallyRedraw(tvPtr);
2281 }
2282 
2283 int
Blt_TreeViewEntryIsSelected(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewColumn * columnPtr)2284 Blt_TreeViewEntryIsSelected(TreeView *tvPtr, TreeViewEntry *entryPtr,
2285     TreeViewColumn *columnPtr)
2286 {
2287     Blt_HashEntry *hPtr;
2288     TreeViewValue *valuePtr;
2289     if (tvPtr->selectMode == SELECT_MODE_NONE) {
2290         return FALSE;
2291     }
2292     hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
2293     if (hPtr == NULL) return FALSE;
2294     if (tvPtr->selectMode == SELECT_MODE_SINGLE) return TRUE;
2295     if (tvPtr->selectMode == SELECT_MODE_MULTIPLE) return TRUE;
2296     if (columnPtr == NULL) return FALSE;
2297     valuePtr = (TreeViewValue *)Blt_TreeViewFindValue(entryPtr, columnPtr);
2298     if (valuePtr == NULL) return FALSE;
2299     return (valuePtr->selected);
2300 }
2301 
2302 void
Blt_TreeViewSelectEntry(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewColumn * columnPtr)2303 Blt_TreeViewSelectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr,
2304     TreeViewColumn *columnPtr)
2305 {
2306     int isNew;
2307     Blt_HashEntry *hPtr;
2308 
2309     hPtr = Blt_CreateHashEntry(&tvPtr->selectTable, (char *)entryPtr, &isNew);
2310     if (isNew) {
2311 	Blt_ChainLink *linkPtr;
2312 
2313 	linkPtr = Blt_ChainAppend(tvPtr->selChainPtr, entryPtr);
2314 	Blt_SetHashValue(hPtr, linkPtr);
2315     }
2316     if (columnPtr != NULL) {
2317         TreeViewValue *valuePtr;
2318         valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
2319         if (valuePtr != NULL) {
2320             valuePtr->selected = TRUE;
2321         }
2322     }
2323 }
2324 
2325 void
Blt_TreeViewDeselectEntry(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewColumn * columnPtr)2326 Blt_TreeViewDeselectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr,
2327     TreeViewColumn *columnPtr)
2328 {
2329     Blt_HashEntry *hPtr;
2330     Blt_ChainLink *linkPtr;
2331 
2332     hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
2333     if (columnPtr != NULL) {
2334         TreeViewValue *valuePtr;
2335 
2336         valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
2337         if (valuePtr != NULL) {
2338             valuePtr->selected = FALSE;
2339         }
2340         if (tvPtr->selectMode & SELECT_MODE_CELLMASK) {
2341             for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
2342                 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2343                 columnPtr = Blt_ChainGetValue(linkPtr);
2344                 valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
2345                 if (valuePtr != NULL && valuePtr->selected) {
2346                     return;
2347                 }
2348             }
2349         }
2350 
2351     }
2352     if (hPtr != NULL) {
2353 
2354 	linkPtr = Blt_GetHashValue(hPtr);
2355 	Blt_ChainDeleteLink(tvPtr->selChainPtr, linkPtr);
2356 	Blt_DeleteHashEntry(&tvPtr->selectTable, hPtr);
2357     }
2358 }
2359 
2360 char *
Blt_TreeViewGetFullName(TreeView * tvPtr,TreeViewEntry * entryPtr,int checkEntryLabel,Tcl_DString * resultPtr)2361 Blt_TreeViewGetFullName(
2362     TreeView *tvPtr,
2363     TreeViewEntry *entryPtr,
2364     int checkEntryLabel,
2365     Tcl_DString *resultPtr)
2366 {
2367     Blt_TreeNode node;
2368     char **names;		/* Used the stack the component names. */
2369     char *staticSpace[64];
2370     int level;
2371     register int i;
2372 
2373     if (entryPtr == NULL) {
2374         return "";
2375     }
2376     level = Blt_TreeNodeDepth(tvPtr->tree, entryPtr->node);
2377     if (tvPtr->rootPtr->labelUid == NULL && entryPtr != tvPtr->rootPtr) {
2378 	level--;
2379     }
2380     if (level > 64) {
2381 	names = Blt_Malloc((level + 2) * sizeof(char *));
2382 	assert(names);
2383     } else {
2384 	names = staticSpace;
2385     }
2386     for (i = level; i >= 0; i--) {
2387 	/* Save the name of each ancestor in the name array. */
2388 	if (checkEntryLabel) {
2389 	    names[i] = GETLABEL(entryPtr);
2390 	} else {
2391 	    names[i] = Blt_TreeNodeLabel(entryPtr->node);
2392 	}
2393 	node = Blt_TreeNodeParent(entryPtr->node);
2394 	if (node != NULL) {
2395 	    entryPtr = Blt_NodeToEntry(tvPtr, node);
2396 	}
2397     }
2398     Tcl_DStringSetLength(resultPtr, 0);
2399     if (level >= 0) {
2400 	if ((tvPtr->pathSep == SEPARATOR_LIST) ||
2401 	    (tvPtr->pathSep == SEPARATOR_NONE)) {
2402 	    for (i = 0; i <= level; i++) {
2403 		Tcl_DStringAppendElement(resultPtr, names[i]);
2404 	    }
2405 	} else {
2406 	    Tcl_DStringAppend(resultPtr, names[0], -1);
2407 	    for (i = 1; i <= level; i++) {
2408 		Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
2409 		Tcl_DStringAppend(resultPtr, names[i], -1);
2410 	    }
2411 	}
2412     } else {
2413 	if ((tvPtr->pathSep != SEPARATOR_LIST) &&
2414 	    (tvPtr->pathSep != SEPARATOR_NONE)) {
2415 	    Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
2416 	}
2417     }
2418     if (names != staticSpace) {
2419 	Blt_Free(names);
2420     }
2421     return Tcl_DStringValue(resultPtr);
2422 }
2423 
2424 
2425 int
Blt_TreeViewCloseEntry(TreeView * tvPtr,TreeViewEntry * entryPtr)2426 Blt_TreeViewCloseEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
2427 {
2428     char *cmd;
2429 
2430     int disabled = (entryPtr->state == STATE_DISABLED);
2431 
2432     if (disabled || entryPtr->flags & ENTRY_CLOSED) {
2433 	return TCL_OK;		/* Entry is already closed. */
2434     }
2435     if ((tvPtr->flags & TV_HIDE_ROOT) && (entryPtr == tvPtr->rootPtr)) {
2436         return TCL_OK;		/* Do not close root if hidden. */
2437     }
2438 
2439     entryPtr->flags |= ENTRY_CLOSED;
2440 
2441     /*
2442      * Invoke the entry's "close" command, if there is one. Otherwise
2443      * try the treeview's global "close" command.
2444      */
2445     cmd = CHOOSE(tvPtr->closeCmd, entryPtr->closeCmd);
2446     if (cmd != NULL) {
2447 	Tcl_DString dString;
2448 	int result;
2449 
2450 	Tcl_DStringInit(&dString);
2451 	Blt_TreeViewPercentSubst(tvPtr, entryPtr, NULL, cmd, "", &dString);
2452 	Tcl_Preserve(entryPtr);
2453 	result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
2454 	Tcl_Release(entryPtr);
2455 	Tcl_DStringFree(&dString);
2456 	if (result != TCL_OK) {
2457             tvPtr->flags |= TV_LAYOUT;
2458 	    return TCL_ERROR;
2459 	}
2460     }
2461     tvPtr->flags |= TV_LAYOUT;
2462     return TCL_OK;
2463 }
2464 
2465 
2466 int
Blt_TreeViewOpenEntry(TreeView * tvPtr,TreeViewEntry * entryPtr)2467 Blt_TreeViewOpenEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
2468 {
2469     char *cmd;
2470 
2471     int disabled = (entryPtr->state == STATE_DISABLED);
2472 
2473     if (disabled || (entryPtr->flags & ENTRY_CLOSED) == 0) {
2474 	return TCL_OK;		/* Entry is already open. */
2475     }
2476     if ((tvPtr->flags&TV_NOAUTO_CLOSE_LEAF)!=0 || Blt_TreeViewIsLeaf(entryPtr)==0 || entryPtr == tvPtr->rootPtr) {
2477         entryPtr->flags &= ~ENTRY_CLOSED;
2478     }
2479     /*
2480      * If there's a "open" command proc specified for the entry, use
2481      * that instead of the more general "open" proc for the entire
2482      * treeview.
2483      */
2484     cmd = CHOOSE(tvPtr->openCmd, entryPtr->openCmd);
2485     if (cmd != NULL) {
2486 	Tcl_DString dString;
2487 	int result;
2488 
2489 	Tcl_DStringInit(&dString);
2490 	Blt_TreeViewPercentSubst(tvPtr, entryPtr, NULL, cmd, "", &dString);
2491 	Tcl_Preserve(entryPtr);
2492 	result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
2493 	Tcl_Release(entryPtr);
2494 	Tcl_DStringFree(&dString);
2495 	if (result != TCL_OK) {
2496             tvPtr->flags |= TV_LAYOUT;
2497 	    return TCL_ERROR;
2498 	}
2499     }
2500     tvPtr->flags |= TV_LAYOUT;
2501     return TCL_OK;
2502 }
2503 
2504 /*
2505  *----------------------------------------------------------------------
2506  *
2507  * Blt_TreeViewCreateEntry --
2508  *
2509  *	This procedure is called by the Tree object when a node is
2510  *	created and inserted into the tree.  It adds a new treeview
2511  *	entry field to the node.
2512  *
2513  * Results:
2514  *	Returns the entry.
2515  *
2516  *----------------------------------------------------------------------
2517  */
2518 int
Blt_TreeViewCreateEntry(TreeView * tvPtr,Blt_TreeNode node,int objc,Tcl_Obj * CONST * objv,int flags)2519 Blt_TreeViewCreateEntry(
2520     TreeView *tvPtr,
2521     Blt_TreeNode node,		/* Node that has just been created. */
2522     int objc,
2523     Tcl_Obj *CONST *objv,
2524     int flags)
2525 {
2526     TreeViewEntry *entryPtr;
2527     int isNew;
2528     Blt_HashEntry *hPtr;
2529 
2530     hPtr = Blt_CreateHashEntry(&tvPtr->entryTable, (char *)node, &isNew);
2531     if (isNew) {
2532 	/* Create the entry structure */
2533 	entryPtr = Blt_PoolAllocItem(tvPtr->entryPool, sizeof(TreeViewEntry));
2534 	memset(entryPtr, 0, sizeof(TreeViewEntry));
2535 	entryPtr->flags = tvPtr->buttonFlags | ENTRY_CLOSED;
2536 	entryPtr->tvPtr = tvPtr;
2537 	entryPtr->labelUid = NULL;
2538 	entryPtr->node = node;
2539 	entryPtr->underline = -1;
2540 	Blt_SetHashValue(hPtr, entryPtr);
2541 
2542     } else {
2543 	entryPtr = Blt_GetHashValue(hPtr);
2544     }
2545     if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, objc, objv, flags)
2546 	!= TCL_OK) {
2547 	Blt_DeleteHashEntry(&tvPtr->entryTable, hPtr);
2548 	Blt_TreeViewFreeEntry(tvPtr, entryPtr);
2549 	return TCL_ERROR;	/* Error configuring the entry. */
2550     }
2551     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2552     Blt_TreeViewEventuallyRedraw(tvPtr);
2553     return TCL_OK;
2554 }
2555 
2556 
2557 /*ARGSUSED*/
2558 static int
CreateApplyProc(Blt_TreeNode node,ClientData clientData,int order)2559 CreateApplyProc(
2560     Blt_TreeNode node,		/* Node that has just been created. */
2561     ClientData clientData,
2562     int order)			/* Not used. */
2563 {
2564     TreeView *tvPtr = clientData;
2565     return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
2566 }
2567 
2568 /*ARGSUSED*/
2569 static int
DeleteApplyProc(Blt_TreeNode node,ClientData clientData,int order)2570 DeleteApplyProc(
2571     Blt_TreeNode node,
2572     ClientData clientData,
2573     int order)			/* Not used. */
2574 {
2575     TreeView *tvPtr = clientData;
2576     /*
2577      * Unsetting the tree value triggers a call back to destroy the entry
2578      * and also releases the Tcl_Obj that contains it.
2579      */
2580     return Blt_TreeUnsetValueByKey(tvPtr->interp, tvPtr->tree, node,
2581 	tvPtr->treeColumn.key);
2582 }
2583 
2584 static int
TreeEventProc(ClientData clientData,Blt_TreeNotifyEvent * eventPtr)2585 TreeEventProc(ClientData clientData, Blt_TreeNotifyEvent *eventPtr)
2586 {
2587     Blt_TreeNode node;
2588     TreeView *tvPtr = clientData;
2589 
2590     node = Blt_TreeGetNode(eventPtr->tree, eventPtr->inode);
2591     switch (eventPtr->type) {
2592   /*  case TREE_NOTIFY_ATTACH:
2593         puts("ATTACH");
2594         tvPtr->flags |= TV_ATTACH;
2595         break; */
2596     case TREE_NOTIFY_CREATE:
2597 	return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
2598     case TREE_NOTIFY_DELETE:
2599 	/*
2600 	 * Deleting the tree node triggers a call back to free the
2601 	 * treeview entry that is associated with it.
2602 	 */
2603 	if (node != NULL) {
2604 	    Blt_TreeViewFreeEntry(tvPtr, Blt_NodeToEntry(tvPtr, node));
2605 	}
2606 	break;
2607     case TREE_NOTIFY_RELABEL:
2608 	if (node != NULL) {
2609 	    TreeViewEntry *entryPtr;
2610 
2611 	    entryPtr = Blt_NodeToEntry(tvPtr, node);
2612 	    entryPtr->flags |= ENTRY_DIRTY;
2613 	}
2614 	/*FALLTHRU*/
2615     case TREE_NOTIFY_MOVE:
2616     case TREE_NOTIFY_SORT:
2617 	Blt_TreeViewEventuallyRedraw(tvPtr);
2618 	tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
2619 	break;
2620     default:
2621 	/* empty */
2622 	break;
2623     }
2624     return TCL_OK;
2625 }
2626 
2627 TreeViewValue *
Blt_TreeViewFindValue(TreeViewEntry * entryPtr,TreeViewColumn * columnPtr)2628 Blt_TreeViewFindValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
2629 {
2630     register TreeViewValue *valuePtr;
2631 
2632     for (valuePtr = entryPtr->values; valuePtr != NULL;
2633 	 valuePtr = valuePtr->nextPtr) {
2634 	if (valuePtr->columnPtr == columnPtr) {
2635 	    return valuePtr;
2636 	}
2637     }
2638     return NULL;
2639 }
2640 
2641 TreeViewValue *
Blt_TreeViewMakeValue(TreeView * tvPtr,TreeViewColumn * columnPtr,TreeViewEntry * entryPtr)2642 Blt_TreeViewMakeValue(TreeView *tvPtr, TreeViewColumn *columnPtr, TreeViewEntry *entryPtr)
2643 {
2644     /* Add a new value only if a data entry exists. */
2645     TreeViewValue *valuePtr = Blt_PoolAllocItem(tvPtr->valuePool,
2646         sizeof(TreeViewValue));
2647     valuePtr->columnPtr = columnPtr;
2648     valuePtr->nextPtr = NULL; /*entryPtr->values; */
2649     valuePtr->textPtr = NULL;
2650     valuePtr->width = valuePtr->height = 0;
2651     valuePtr->stylePtr = NULL;
2652     valuePtr->string = NULL;
2653     valuePtr->selected = 0;
2654     /*entryPtr->values = valuePtr; */
2655     return valuePtr;
2656 }
2657 
2658 void
Blt_TreeViewAddValue(TreeViewEntry * entryPtr,TreeViewColumn * columnPtr)2659 Blt_TreeViewAddValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
2660 {
2661     if (Blt_TreeViewFindValue(entryPtr, columnPtr) == NULL) {
2662         Tcl_Obj *objPtr = NULL;
2663 
2664         if (Blt_TreeViewGetData(entryPtr, columnPtr->key, &objPtr) == TCL_OK) {
2665             TreeViewValue *valuePtr;
2666 
2667             /* Add a new value only if a data entry exists. */
2668             valuePtr = Blt_PoolAllocItem(entryPtr->tvPtr->valuePool,
2669                 sizeof(TreeViewValue));
2670                 valuePtr->columnPtr = columnPtr;
2671                 valuePtr->entryPtr = entryPtr;
2672                 valuePtr->nextPtr = entryPtr->values;
2673                 valuePtr->textPtr = NULL;
2674                 valuePtr->width = valuePtr->height = 0;
2675                 valuePtr->stylePtr = NULL;
2676                 valuePtr->string = (objPtr ? Tcl_GetString(objPtr) : NULL);
2677                 valuePtr->selected = 0;
2678                 entryPtr->values = valuePtr;
2679         }
2680     }
2681     Blt_TreeViewWindowUpdate(entryPtr, columnPtr);
2682     entryPtr->tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2683     entryPtr->flags |= ENTRY_DIRTY;
2684 }
2685 
2686 /*
2687  *----------------------------------------------------------------------
2688  *
2689  * TreeTraceProc --
2690  *
2691  *	Mirrors the individual values of the tree object (they must
2692  *	also be listed in the widget's columns chain). This is because
2693  *	it must track and save the sizes of each individual data
2694  *	entry, rather than re-computing all the sizes each time the
2695  *	widget is redrawn.
2696  *
2697  *	This procedure is called by the Tree object when a node data
2698  *	value is set unset.
2699  *
2700  * Results:
2701  *	Returns TCL_OK.
2702  *
2703  *----------------------------------------------------------------------
2704  */
2705 /*ARGSUSED*/
2706 static int
TreeTraceProc(ClientData clientData,Tcl_Interp * interp,Blt_TreeNode node,Blt_TreeKey key,unsigned int flags)2707 TreeTraceProc(
2708     ClientData clientData,
2709     Tcl_Interp *interp,
2710     Blt_TreeNode node,		/* Node that has just been updated. */
2711     Blt_TreeKey key,		/* Key of value that's been updated. */
2712     unsigned int flags)
2713 {
2714     Blt_HashEntry *hPtr;
2715     TreeView *tvPtr = clientData;
2716     TreeViewColumn *columnPtr;
2717     TreeViewEntry *entryPtr;
2718 
2719     hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
2720     if (hPtr == NULL) {
2721 	return TCL_OK;		/* Not a node that we're interested in. */
2722     }
2723     entryPtr = Blt_GetHashValue(hPtr);
2724     flags &= TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_UNSET;
2725     switch (flags) {
2726     case TREE_TRACE_WRITE:
2727 	hPtr = Blt_FindHashEntry(&tvPtr->columnTable, key);
2728 	if (hPtr == NULL) {
2729 	    return TCL_OK;	/* Data value isn't used by widget. */
2730 	}
2731 	columnPtr = Blt_GetHashValue(hPtr);
2732 	if (columnPtr != &tvPtr->treeColumn) {
2733 	    Blt_TreeViewAddValue(entryPtr, columnPtr);
2734 	}
2735 	entryPtr->flags |= ENTRY_DIRTY;
2736 	Blt_TreeViewEventuallyRedraw(tvPtr);
2737 	tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2738 	break;
2739 
2740     case TREE_TRACE_UNSET:
2741         Blt_TreeViewDeleteValue(entryPtr, key);
2742 	break;
2743 
2744     default:
2745 	break;
2746     }
2747     return TCL_OK;
2748 }
2749 
2750 
2751 static void
GetValueSize(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewValue * valuePtr,TreeViewStyle * stylePtr)2752 GetValueSize(
2753     TreeView *tvPtr,
2754     TreeViewEntry *entryPtr,
2755     TreeViewValue *valuePtr,
2756     TreeViewStyle *stylePtr)
2757 {
2758     TreeViewColumn *columnPtr;
2759 
2760     columnPtr = valuePtr->columnPtr;
2761     valuePtr->width = valuePtr->height = 0;
2762     if (entryPtr->flags & ENTRY_DIRTY) { /* Reparse the data. */
2763 	char *string;
2764 	TreeViewIcon icon;
2765 
2766 	Tcl_Obj *valueObjPtr;
2767 	TreeViewStyle *newStylePtr;
2768 
2769 	icon = NULL;
2770 	newStylePtr = NULL;
2771 	if (Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key,
2772 		&valueObjPtr) != TCL_OK) {
2773 	    return;			/* No data ??? */
2774 	}
2775 	string = Tcl_GetString(valueObjPtr);
2776 	valuePtr->string = string;
2777 	if (tvPtr->inlineImg && string[0] == '@') {	/* Name of style or Tk image. */
2778 	    int objc;
2779 	    Tcl_Obj **objv;
2780 
2781 	    if ((Tcl_ListObjGetElements(tvPtr->interp, valueObjPtr, &objc,
2782 		&objv) != TCL_OK) || (objc < 2) || (objc > 2)) {
2783 		goto handleString;
2784 	    }
2785 	    if (objc > 0) {
2786 		char *name, *cmd;
2787 
2788 		cmd = tvPtr->styleCmd;
2789 		name = Tcl_GetString(objv[0]) + 1;
2790 		if (Blt_TreeViewGetStyle((Tcl_Interp *)NULL, tvPtr, name,
2791 					 &newStylePtr) != TCL_OK) {
2792                     if (cmd != NULL && strcmp(cmd, "%W style create textbox %V")) {
2793                         Tcl_DString dString;
2794                         int result, isdel;
2795 
2796                         Tcl_DStringInit(&dString);
2797                         Blt_TreeViewPercentSubst(tvPtr, entryPtr, columnPtr, cmd, name, &dString);
2798                         Tcl_Preserve(entryPtr);
2799                         Tcl_Preserve(columnPtr);
2800                         /* TODO: should save/restore intep state. */
2801                         result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
2802                         isdel = ((entryPtr->flags & ENTRY_DELETED)||(columnPtr->flags & COLUMN_DELETED));
2803                         Tcl_Release(entryPtr);
2804                         Tcl_Release(columnPtr);
2805                         Tcl_DStringFree(&dString);
2806                         if (isdel || (tvPtr->flags & TV_DELETED)) {
2807                             return;
2808                         }
2809                         if (result != TCL_OK) {
2810                             goto handleString;
2811                         }
2812                         if (Blt_TreeViewGetStyle((Tcl_Interp *)NULL, tvPtr,
2813                             name, &newStylePtr) != TCL_OK) {
2814                             goto handleString;
2815                         }
2816                         Tcl_ResetResult(tvPtr->interp);
2817                     } else {
2818                         icon = Blt_TreeViewGetIcon(tvPtr, name);
2819                         if (icon == NULL) {
2820                             goto handleString;
2821                         }
2822                         /* Create a new style by the name of the image. */
2823                         newStylePtr = Blt_TreeViewCreateStyle((Tcl_Interp *)NULL,
2824                             tvPtr, STYLE_TEXTBOX, name);
2825                         if (newStylePtr == NULL) {
2826                             goto handleString;
2827                         }
2828                         Blt_TreeViewUpdateStyleGCs(tvPtr, newStylePtr);
2829                     }
2830 		}
2831 	    }
2832 	    if (valuePtr->stylePtr != NULL) {
2833 		Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
2834 	    }
2835 	    if (icon != NULL) {
2836 		Blt_TreeViewSetStyleIcon(tvPtr, newStylePtr, icon);
2837 	    }
2838 	    valuePtr->stylePtr = newStylePtr;
2839             if (objc!=2) {
2840                 valuePtr->string =  NULL;
2841             } else {
2842                 /* valuePtr->string =  Tcl_GetString(objv[1]) */
2843                 valuePtr->string =  strstr(Tcl_GetString(valueObjPtr) + strlen(Tcl_GetString(objv[0]))+1, Tcl_GetString(objv[1]));
2844             }
2845 	}
2846         if (valuePtr->stylePtr && valuePtr->stylePtr->icon && !valuePtr->stylePtr->hidden) {
2847             icon = valuePtr->stylePtr->icon;
2848             if (icon->height > valuePtr->height) {
2849                 valuePtr->height = icon->height;
2850             }
2851         } else if (columnPtr->stylePtr && columnPtr->stylePtr->icon) {
2852             icon = columnPtr->stylePtr->icon;
2853             if (icon->height > valuePtr->height) {
2854                 valuePtr->height = icon->height;
2855             }
2856         }
2857         if (valuePtr->stylePtr && valuePtr->stylePtr->hidden) {
2858             return;
2859         }
2860     }
2861  handleString:
2862     stylePtr = CHOOSE3(tvPtr->stylePtr, columnPtr->stylePtr, valuePtr->stylePtr);
2863     /* Measure the text string. */
2864     (*stylePtr->classPtr->measProc)(tvPtr, stylePtr, valuePtr);
2865 }
2866 
2867 static void
GetRowExtents(TreeView * tvPtr,TreeViewEntry * entryPtr,int * widthPtr,int * heightPtr)2868 GetRowExtents(
2869     TreeView *tvPtr,
2870     TreeViewEntry *entryPtr,
2871     int *widthPtr,
2872     int *heightPtr)
2873 {
2874     TreeViewValue *valuePtr;
2875     int valueWidth;		/* Width of individual value.  */
2876     int width, height;		/* Compute dimensions of row. */
2877     TreeViewStyle *stylePtr;
2878     Blt_ChainLink *linkPtr;
2879     TreeViewColumn *columnPtr;
2880 
2881     width = height = 0;
2882 #if 1
2883     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
2884         linkPtr = Blt_ChainNextLink(linkPtr)) {
2885         columnPtr = Blt_ChainGetValue(linkPtr);
2886 
2887         if (columnPtr == &tvPtr->treeColumn || columnPtr->hidden) continue;
2888         for (valuePtr = entryPtr->values; valuePtr != NULL;
2889             valuePtr = valuePtr->nextPtr) {
2890             if (valuePtr->columnPtr == columnPtr) break;
2891         }
2892         if (valuePtr == NULL) {
2893             if (!(tvPtr->flags & TV_FILL_NULL)) continue;
2894             valuePtr = columnPtr->defValue;
2895             stylePtr = CHOOSE(tvPtr->stylePtr, columnPtr->stylePtr);
2896             if (stylePtr && stylePtr->icon) {
2897                 if (stylePtr->icon->height > height) {
2898                     height = stylePtr->icon->height;
2899                 }
2900                 width += valueWidth;
2901             }
2902             continue;
2903         }
2904         stylePtr = valuePtr->stylePtr;
2905         if (stylePtr && stylePtr->hidden) continue;
2906         if (stylePtr == NULL ) {
2907             stylePtr = CHOOSE(tvPtr->stylePtr, columnPtr->stylePtr);
2908         }
2909         if ((entryPtr->flags & ENTRY_DIRTY) ||
2910         (stylePtr->flags & STYLE_DIRTY)) {
2911             GetValueSize(tvPtr, entryPtr, valuePtr, stylePtr);
2912         }
2913         if (valuePtr->height > height) {
2914             height = valuePtr->height;
2915         }
2916         valueWidth = valuePtr->width;
2917         width += valueWidth;
2918     }
2919 
2920 #else
2921     /* This fails to ignore hidden columns or account for sizes in unset values */
2922     /* which is a problem when a column style has set an icon. */
2923     for (valuePtr = entryPtr->values; valuePtr != NULL;
2924 	valuePtr = valuePtr->nextPtr) {
2925 	stylePtr = valuePtr->stylePtr;
2926 	if (stylePtr == NULL) {
2927             stylePtr = CHOOSE(tvPtr->stylePtr, valuePtr->columnPtr->stylePtr);
2928 	}
2929 	if ((entryPtr->flags & ENTRY_DIRTY) ||
2930 	    (stylePtr->flags & STYLE_DIRTY)) {
2931 	    GetValueSize(tvPtr, entryPtr, valuePtr, stylePtr);
2932 	}
2933 	if (valuePtr->height > height) {
2934 	    height = valuePtr->height;
2935 	}
2936 	valueWidth = valuePtr->width;
2937 	width += valueWidth;
2938     }
2939 #endif
2940     *widthPtr = width;
2941     *heightPtr = height;
2942 }
2943 
2944 /*
2945  *----------------------------------------------------------------------
2946  *
2947  * Blt_TreeViewNearestEntry --
2948  *
2949  *	Finds the entry closest to the given screen X-Y coordinates
2950  *	in the viewport.
2951  *
2952  * Results:
2953  *	Returns the pointer to the closest node.  If no node is
2954  *	visible (nodes may be hidden), NULL is returned.
2955  *
2956  *----------------------------------------------------------------------
2957  */
2958 /*ARGSUSED*/
2959 TreeViewEntry *
Blt_TreeViewNearestEntry(TreeView * tvPtr,int x,int y,int selectOne)2960 Blt_TreeViewNearestEntry(TreeView *tvPtr, int x, int y, int selectOne)
2961 {
2962     TreeViewEntry *lastPtr, *entryPtr;
2963     register TreeViewEntry **p;
2964 
2965     /*
2966      * We implicitly can pick only visible entries.  So make sure that
2967      * the tree exists.
2968      */
2969     if (tvPtr->nVisible == 0) {
2970 	return NULL;
2971     }
2972     if (y < tvPtr->titleHeight) {
2973 	return (selectOne) ? tvPtr->visibleArr[0] : NULL;
2974     }
2975     /*
2976      * Since the entry positions were previously computed in world
2977      * coordinates, convert Y-coordinate from screen to world
2978      * coordinates too.
2979      */
2980     y = WORLDY(tvPtr, y);
2981     lastPtr = tvPtr->visibleArr[0];
2982     for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++) {
2983 	entryPtr = *p;
2984 	/*
2985 	 * If the start of the next entry starts beyond the point,
2986 	 * use the last entry.
2987 	 */
2988 	if (entryPtr->worldY > y) {
2989 	    return (selectOne) ? entryPtr : NULL;
2990 	}
2991 	if (y < (entryPtr->worldY + entryPtr->height)) {
2992 	    return entryPtr;	/* Found it. */
2993 	}
2994 	lastPtr = entryPtr;
2995     }
2996     return (selectOne) ? lastPtr : NULL;
2997 }
2998 
2999 
3000 ClientData
Blt_TreeViewEntryTag(TreeView * tvPtr,CONST char * string)3001 Blt_TreeViewEntryTag(TreeView *tvPtr, CONST char *string)
3002 {
3003     Blt_HashEntry *hPtr;
3004     int isNew;			/* Not used. */
3005 
3006     hPtr = Blt_CreateHashEntry(&tvPtr->entryTagTable, string, &isNew);
3007     if (hPtr == NULL) {
3008         return NULL;
3009     }
3010     return Blt_GetHashKey(&tvPtr->entryTagTable, hPtr);
3011 }
3012 
3013 ClientData
Blt_TreeViewButtonTag(TreeView * tvPtr,CONST char * string)3014 Blt_TreeViewButtonTag(TreeView *tvPtr, CONST char *string)
3015 {
3016     Blt_HashEntry *hPtr;
3017     int isNew;			/* Not used. */
3018 
3019     hPtr = Blt_CreateHashEntry(&tvPtr->buttonTagTable, string, &isNew);
3020     if (hPtr == NULL) {
3021         return NULL;
3022     }
3023     return Blt_GetHashKey(&tvPtr->buttonTagTable, hPtr);
3024 }
3025 
3026 ClientData
Blt_TreeViewColumnTag(TreeView * tvPtr,CONST char * string)3027 Blt_TreeViewColumnTag(TreeView *tvPtr, CONST char *string)
3028 {
3029     Blt_HashEntry *hPtr;
3030     int isNew;			/* Not used. */
3031 
3032     hPtr = Blt_CreateHashEntry(&tvPtr->columnTagTable, string, &isNew);
3033     if (hPtr == NULL) {
3034         return NULL;
3035     }
3036     return Blt_GetHashKey(&tvPtr->columnTagTable, hPtr);
3037 }
3038 
3039 ClientData
Blt_TreeViewStyleTag(TreeView * tvPtr,CONST char * string)3040 Blt_TreeViewStyleTag(TreeView *tvPtr, CONST char *string)
3041 {
3042     Blt_HashEntry *hPtr;
3043     int isNew;			/* Not used. */
3044 
3045     hPtr = Blt_CreateHashEntry(&tvPtr->styleTagTable, string, &isNew);
3046     if (hPtr == NULL) {
3047         return NULL;
3048     }
3049     return Blt_GetHashKey(&tvPtr->styleTagTable, hPtr);
3050 }
3051 
3052 static void
GetTags(Blt_BindTable table,ClientData object,ClientData context,Blt_List ids)3053 GetTags(
3054     Blt_BindTable table,
3055     ClientData object,		/* Object picked. */
3056     ClientData context,		/* Context of object. */
3057     Blt_List ids)		/* (out) List of binding ids to be
3058 				 * applied for this object. */
3059 {
3060     TreeView *tvPtr;
3061     int nNames;
3062     char **names;
3063     register char **p;
3064 
3065     tvPtr = Blt_GetBindingData(table);
3066     if (context == (ClientData)ITEM_ENTRY_BUTTON) {
3067 	TreeViewEntry *entryPtr = object;
3068 
3069 	Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Button"), 0);
3070 	if (entryPtr->tagsUid != NULL) {
3071 	    if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
3072 			      &names) == TCL_OK) {
3073 		for (p = names; *p != NULL; p++) {
3074 		    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, *p), 0);
3075 		}
3076 		Blt_Free(names);
3077 	    }
3078 	} else {
3079 	    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Entry"), 0);
3080 	    Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "all"), 0);
3081 	}
3082     } else if (context == (ClientData)ITEM_COLUMN_TITLE) {
3083 	TreeViewColumn *columnPtr = object;
3084 
3085 	Blt_ListAppend(ids, (char *)columnPtr, 0);
3086 	if (columnPtr->tagsUid != NULL) {
3087 	    if (Tcl_SplitList((Tcl_Interp *)NULL, columnPtr->tagsUid, &nNames,
3088 		      &names) == TCL_OK) {
3089 		for (p = names; *p != NULL; p++) {
3090 		    Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, *p), 0);
3091 		}
3092 		Blt_Free(names);
3093 	    }
3094 	}
3095     } else if (context == ITEM_COLUMN_RULE) {
3096 	Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, "Rule"), 0);
3097     } else {
3098 	TreeViewEntry *entryPtr = object;
3099 
3100 	Blt_ListAppend(ids, (char *)entryPtr, 0);
3101 	if (entryPtr->tagsUid != NULL) {
3102 	    if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
3103 		      &names) == TCL_OK) {
3104 		for (p = names; *p != NULL; p++) {
3105 		    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, *p), 0);
3106 		}
3107 		Blt_Free(names);
3108 	    }
3109 	} else if (context == ITEM_ENTRY){
3110 	    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
3111 	    Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
3112 	} else {
3113 	    TreeViewValue *valuePtr = context;
3114 
3115 	    if (valuePtr != NULL) {
3116 		TreeViewStyle *stylePtr = valuePtr->stylePtr;
3117 
3118 		if (stylePtr == NULL) {
3119                     stylePtr = CHOOSE(tvPtr->stylePtr, valuePtr->columnPtr->stylePtr);
3120 		}
3121 		Blt_ListAppend(ids,
3122 	            Blt_TreeViewEntryTag(tvPtr, stylePtr->name), 0);
3123 		Blt_ListAppend(ids,
3124 		    Blt_TreeViewEntryTag(tvPtr, valuePtr->columnPtr->key), 0);
3125 		Blt_ListAppend(ids,
3126 		    Blt_TreeViewEntryTag(tvPtr, stylePtr->classPtr->className),
3127 		    0);
3128 #ifndef notdef
3129 		Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
3130 		Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
3131 #endif
3132 	    }
3133 	}
3134     }
3135 }
3136 
3137 /*ARGSUSED*/
3138 static ClientData
PickItem(ClientData clientData,int x,int y,ClientData * contextPtr)3139 PickItem(
3140     ClientData clientData,
3141     int x,
3142     int y,			/* Screen coordinates of the test point. */
3143     ClientData *contextPtr)	/* (out) Context of item selected: should
3144 				 * be ITEM_ENTRY, ITEM_ENTRY_BUTTON,
3145 				 * ITEM_COLUMN_TITLE,
3146 				 * ITEM_COLUMN_RULE, or
3147 				 * ITEM_STYLE. */
3148 {
3149     TreeView *tvPtr = clientData;
3150     TreeViewEntry *entryPtr;
3151     TreeViewColumn *columnPtr;
3152 
3153     if (Tcl_InterpDeleted(tvPtr->interp)) {
3154         return NULL;
3155     }
3156     if (contextPtr != NULL) {
3157 	*contextPtr = NULL;
3158     }
3159     if (tvPtr->flags & TV_DIRTY && (!(tvPtr->flags & TV_PICKING))) {
3160 	/* Can't trust the selected entry if nodes have been added or
3161 	 * deleted. So recompute the layout. */
3162         tvPtr->flags |= TV_PICKING;
3163 	if (tvPtr->flags & TV_LAYOUT) {
3164 	    if (Blt_TreeViewComputeLayout(tvPtr) != TCL_OK) { return NULL; }
3165 	}
3166 	if (ComputeVisibleEntries(tvPtr) != TCL_OK) { return NULL; }
3167         tvPtr->flags &= ~TV_PICKING;
3168     }
3169     columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, contextPtr);
3170     if ((*contextPtr != NULL) && (tvPtr->flags & TV_SHOW_COLUMN_TITLES)) {
3171 	return columnPtr;
3172     }
3173     if (tvPtr->nVisible == 0) {
3174 	return NULL;
3175     }
3176     entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
3177     if (entryPtr == NULL) {
3178 	return NULL;
3179     }
3180     x = WORLDX(tvPtr, x);
3181     y = WORLDY(tvPtr, y);
3182     if (contextPtr != NULL) {
3183 	*contextPtr = ITEM_ENTRY;
3184 	if (columnPtr != NULL) {
3185 	    TreeViewValue *valuePtr;
3186 
3187 	    valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
3188 	    if (valuePtr != NULL) {
3189 		TreeViewStyle *stylePtr;
3190 
3191 		stylePtr = valuePtr->stylePtr;
3192 		if (stylePtr == NULL) {
3193                     stylePtr = CHOOSE(tvPtr->stylePtr, valuePtr->columnPtr->stylePtr);
3194 		}
3195 		if ((stylePtr->classPtr->pickProc == NULL) ||
3196 		    ((*stylePtr->classPtr->pickProc)(entryPtr, valuePtr,
3197 			stylePtr, x, y))) {
3198 		    *contextPtr = valuePtr;
3199 		}
3200 	    }
3201 	}
3202 	if (entryPtr->flags & ENTRY_HAS_BUTTON) {
3203 	    TreeViewButton *buttonPtr = &tvPtr->button;
3204 	    int left, right, top, bottom;
3205 
3206 	    left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
3207 	    right = left + buttonPtr->width + 2 * BUTTON_PAD;
3208 	    top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
3209 	    bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
3210 	    if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
3211 		*contextPtr = (ClientData)ITEM_ENTRY_BUTTON;
3212 	    }
3213 	}
3214     }
3215     return entryPtr;
3216 }
3217 
3218 static void
SetEntryStyle(TreeView * tvPtr,TreeViewEntry * entryPtr)3219 SetEntryStyle(TreeView *tvPtr, TreeViewEntry *entryPtr)
3220 {
3221     int level;
3222     level = Blt_TreeNodeDepth(tvPtr->tree, entryPtr->node);
3223     entryPtr->stylePtr = entryPtr->realStylePtr;
3224     if (entryPtr->stylePtr == NULL && tvPtr->levelStyles != NULL) {
3225         int m;
3226         for (m=0; tvPtr->levelStyles[m] != NULL; m++) {
3227             if ((m+1)==level) {
3228                 entryPtr->stylePtr = tvPtr->levelStyles[m];
3229                 break;
3230             }
3231         }
3232     }
3233 }
3234 
3235 static int
GetEntryExtents(TreeView * tvPtr,TreeViewEntry * entryPtr)3236 GetEntryExtents(TreeView *tvPtr, TreeViewEntry *entryPtr)
3237 {
3238     Tk_Font font;
3239     TreeViewIcon *icons, sIcons[2];
3240     char *label;
3241     int entryWidth, entryHeight;
3242     int width, height;
3243 
3244     /*
3245      * FIXME: Use of DIRTY flag inconsistent.  When does it
3246      *	      mean "dirty entry"? When does it mean "dirty column"?
3247      *	      Does it matter? probably
3248      */
3249     if ((entryPtr->flags & ENTRY_DIRTY) || (tvPtr->flags & TV_UPDATE)) {
3250 	Tk_FontMetrics fontMetrics;
3251 
3252         SetEntryStyle(tvPtr, entryPtr);
3253 	entryPtr->iconWidth = entryPtr->iconHeight = 0;
3254 	if (entryPtr->icons) {
3255              icons = entryPtr->icons;
3256          } else if (entryPtr->stylePtr && entryPtr->stylePtr->icon) {
3257             icons = sIcons;
3258             icons[0] = entryPtr->stylePtr->icon;
3259             icons[1] = NULL;
3260         } else {
3261 	   icons = tvPtr->icons;
3262 	}
3263 	if (icons != NULL) {
3264 	    register int i;
3265 
3266 	    for (i = 0; i < 2; i++) {
3267 		if (icons[i] == NULL) {
3268 		    break;
3269 		}
3270 		if (entryPtr->iconWidth < TreeViewIconWidth(icons[i])) {
3271 		    entryPtr->iconWidth = TreeViewIconWidth(icons[i]);
3272 		}
3273                 if (tvPtr->flags & TV_HIDE_ICONS && tvPtr->flatView) {
3274                     continue;
3275                 }
3276 
3277 		if (entryPtr->iconHeight < TreeViewIconHeight(icons[i])) {
3278 		    entryPtr->iconHeight = TreeViewIconHeight(icons[i]);
3279 		}
3280 	    }
3281 	}
3282 	if ((icons == NULL) || (icons[0] == NULL)) {
3283 	    entryPtr->iconWidth = DEF_ICON_WIDTH;
3284 	    entryPtr->iconHeight = DEF_ICON_HEIGHT;
3285 	}
3286 	entryPtr->iconWidth += 2 * ICON_PADX;
3287 	entryPtr->iconHeight += 2 * ICON_PADY;
3288 	entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
3289 	if (entryPtr->stylePtr && entryPtr->stylePtr->font) {
3290 	    font = entryPtr->stylePtr->font;
3291          } else {
3292             font = entryPtr->font;
3293 	}
3294 	if (font == NULL) {
3295              font = Blt_TreeViewGetStyleFont(tvPtr, &tvPtr->treeColumn, tvPtr->treeColumn.stylePtr);
3296 	}
3297 	if (entryPtr->fullName != NULL) {
3298 	    Blt_Free(entryPtr->fullName);
3299 	    entryPtr->fullName = NULL;
3300 	}
3301 	if (entryPtr->textPtr != NULL) {
3302 	    Blt_Free(entryPtr->textPtr);
3303 	    entryPtr->textPtr = NULL;
3304 	}
3305 
3306 	Tk_GetFontMetrics(font, &fontMetrics);
3307 	entryPtr->lineHeight = fontMetrics.linespace;
3308 	entryPtr->lineHeight += 2 * (tvPtr->focusHeight + LABEL_PADY +
3309 				    tvPtr->selBorderWidth) + tvPtr->leader;
3310 	label = GETLABEL(entryPtr);
3311 	if (label[0] == '\0') {
3312 	    width = height = entryPtr->lineHeight;
3313 	} else {
3314 	    TextStyle ts;
3315 
3316 	    Blt_InitTextStyle(&ts);
3317 	    ts.shadow.offset = entryPtr->shadow.offset;
3318 	    ts.font = font;
3319 
3320 	    if (tvPtr->flatView && tvPtr->showFull) {
3321 		Tcl_DString dString;
3322 
3323 	        Tcl_DStringInit(&dString);
3324 		Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
3325 		entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
3326 		Tcl_DStringFree(&dString);
3327 		entryPtr->textPtr = Blt_GetTextLayout(entryPtr->fullName, &ts);
3328 
3329 	    } else {
3330                  Tcl_Obj *fmtObj;
3331 
3332 #define NotNullObj(p) ((p != NULL && strlen(Tcl_GetString(p))) ? p : NULL)
3333 
3334                  fmtObj = NotNullObj(tvPtr->treeColumn.formatCmd);
3335                  if (fmtObj == NULL) {
3336                      fmtObj = NotNullObj(tvPtr->formatCmd);
3337                  }
3338 
3339 	        if (fmtObj == NULL) {
3340                      entryPtr->textPtr = Blt_GetTextLayout(label, &ts);
3341                  } else {
3342                      Tcl_DString cmdString;
3343                      char *string;
3344                      int result;
3345                      Tcl_Interp *interp;
3346                      int isdel;
3347 
3348                      interp = tvPtr->interp;
3349                      Tcl_Preserve(entryPtr);
3350                      Blt_TreeViewPercentSubst(tvPtr, entryPtr, &tvPtr->treeColumn, Tcl_GetString(fmtObj), label, &cmdString);
3351                      result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
3352                      Blt_TreeViewOptsInit(tvPtr);
3353                      Tcl_DStringFree(&cmdString);
3354                      isdel = ((entryPtr->flags & ENTRY_DELETED));
3355                      Tcl_Release(entryPtr);
3356                      if (isdel || (tvPtr->flags & TV_DELETED)) {
3357                          return TCL_ERROR;
3358                      }
3359 
3360                      if (result == TCL_OK) {
3361                          string = Tcl_GetStringResult(interp);
3362                          entryPtr->textPtr = Blt_GetTextLayoutStr(string, &ts);
3363                      } else {
3364                          entryPtr->textPtr = Blt_GetTextLayout(label, &ts);
3365                      }
3366                  }
3367 	    }
3368 	    width = entryPtr->textPtr->width;
3369 	    height = entryPtr->textPtr->height;
3370             if (entryPtr->subLabel != NULL) {
3371                 if (tvPtr->subStylePtr && tvPtr->subStylePtr->hidden) {
3372                 } else {
3373                     if (tvPtr->subStylePtr && tvPtr->subStylePtr->font) {
3374                         ts.font = tvPtr->subStylePtr->font;
3375                     }
3376                     entryPtr->subTextPtr = Blt_GetTextLayout(entryPtr->subLabel, &ts);
3377                     width += entryPtr->subTextPtr->width;
3378                 }
3379             }
3380         }
3381 	width += 2 * (FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth);
3382 	height += 2 * (tvPtr->focusHeight + LABEL_PADY + tvPtr->selBorderWidth);
3383 	width = ODD(width);
3384 	if (entryPtr->reqHeight > height) {
3385 	    height = entryPtr->reqHeight;
3386 	}
3387 	height = ODD(height);
3388 	entryWidth = width;
3389 	if (entryHeight < height) {
3390 	    entryHeight = height;
3391 	}
3392 	entryPtr->labelWidth = width;
3393 	entryPtr->labelHeight = height;
3394     } else {
3395 	entryHeight = entryPtr->labelHeight;
3396 	entryWidth = entryPtr->labelWidth;
3397     }
3398     /*
3399      * Find the maximum height of the data value entries. This also has
3400      * the side effect of contributing the maximum width of the column.
3401      */
3402     GetRowExtents(tvPtr, entryPtr, &width, &height);
3403     if (entryHeight < height) {
3404 	entryHeight = height;
3405     }
3406     entryPtr->width = entryWidth + tvPtr->levelPad + COLUMN_PAD;
3407     entryPtr->height = entryHeight + tvPtr->leader;
3408     if (entryPtr->height<tvPtr->reqMin) {
3409         entryPtr->height = tvPtr->reqMin;
3410     }
3411     /*
3412      * Force the height of the entry to an even number. This is to
3413      * make the dots or the vertical line segments coincide with the
3414      * start of the horizontal lines.
3415      */
3416     if (entryPtr->height & 0x01) {
3417 	entryPtr->height++;
3418     }
3419     entryPtr->flags &= ~ENTRY_DIRTY;
3420     return TCL_OK;
3421 }
3422 
3423 /*
3424  * TreeView Procedures
3425  */
3426 static void
widgetWorldChanged(ClientData clientData)3427 widgetWorldChanged(ClientData clientData)
3428 {
3429     TreeView *tvPtr = (TreeView *)clientData;
3430     Blt_TreeViewRelayout(tvPtr);
3431 }
3432 
3433 
3434 /*
3435  * ----------------------------------------------------------------------
3436  *
3437  * CreateTreeView --
3438  *
3439  * ----------------------------------------------------------------------
3440  */
3441 static TreeView *
CreateTreeView(Tcl_Interp * interp,Tcl_Obj * objPtr,CONST char * className)3442 CreateTreeView(
3443     Tcl_Interp *interp,
3444     Tcl_Obj *objPtr,		/* Name of the new widget. */
3445     CONST char *className)
3446 {
3447     Tk_Window tkwin;
3448     TreeView *tvPtr;
3449     char *name;
3450     Tcl_DString dString;
3451     int result;
3452 
3453     name = Tcl_GetString(objPtr);
3454     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), name,
3455 	(char *)NULL);
3456     if (tkwin == NULL) {
3457 	return NULL;
3458     }
3459     Tk_SetClass(tkwin, (char *)className);
3460 
3461     tvPtr = Blt_Calloc(1, sizeof(TreeView));
3462     assert(tvPtr);
3463     tvPtr->tkwin = tkwin;
3464     tvPtr->display = Tk_Display(tkwin);
3465     tvPtr->interp = interp;
3466     tvPtr->flags = (TV_HIDE_ROOT | TV_SHOW_COLUMN_TITLES | TV_FILL_NULL |
3467 		    TV_DIRTY | TV_LAYOUT | TV_RESORT);
3468     tvPtr->leader = 0;
3469     tvPtr->dashes = 1;
3470     tvPtr->highlightWidth = 0;
3471     tvPtr->selBorderWidth = 1;
3472     tvPtr->borderWidth = 0;
3473     tvPtr->relief = TK_RELIEF_SUNKEN;
3474     tvPtr->selRelief = TK_RELIEF_FLAT;
3475     tvPtr->scrollMode = BLT_SCROLL_MODE_HIERBOX;
3476     tvPtr->selectMode = SELECT_MODE_SINGLE;
3477     tvPtr->button.closeRelief = tvPtr->button.openRelief = TK_RELIEF_SOLID;
3478     tvPtr->reqWidth = 200;
3479     tvPtr->reqHeight = 200;
3480     tvPtr->xScrollUnits = tvPtr->yScrollUnits = 20;
3481     tvPtr->lineWidth = 1;
3482     tvPtr->button.borderWidth = 1;
3483     tvPtr->colChainPtr = Blt_ChainCreate();
3484     tvPtr->buttonFlags = BUTTON_AUTO;
3485     tvPtr->selChainPtr = Blt_ChainCreate();
3486     tvPtr->tile = NULL;
3487     tvPtr->selectTile = NULL;
3488     tvPtr->scrollTile = 0;
3489     tvPtr->nextIdx = 1;
3490     tvPtr->nextSubIdx = 1;
3491     Blt_InitHashTableWithPool(&tvPtr->entryTable, BLT_ONE_WORD_KEYS);
3492     Blt_InitHashTable(&tvPtr->columnTable, BLT_STRING_KEYS);
3493     /*Blt_InitHashTable(&tvPtr->columnTable, BLT_ONE_WORD_KEYS); */
3494     Blt_InitHashTable(&tvPtr->iconTable, BLT_STRING_KEYS);
3495     Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
3496     Blt_InitHashTable(&tvPtr->uidTable, BLT_STRING_KEYS);
3497     Blt_InitHashTable(&tvPtr->styleTable, BLT_STRING_KEYS);
3498     tvPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr, PickItem,
3499 	GetTags);
3500     Blt_InitHashTable(&tvPtr->entryTagTable, BLT_STRING_KEYS);
3501     Blt_InitHashTable(&tvPtr->columnTagTable, BLT_STRING_KEYS);
3502     Blt_InitHashTable(&tvPtr->buttonTagTable, BLT_STRING_KEYS);
3503     Blt_InitHashTable(&tvPtr->styleTagTable, BLT_STRING_KEYS);
3504     Blt_InitHashTable(&tvPtr->winTable, BLT_STRING_KEYS);
3505     Blt_InitHashTable(&tvPtr->winCellTable, BLT_STRING_KEYS);
3506 
3507     tvPtr->entryPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
3508     tvPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
3509 #if (TK_MAJOR_VERSION > 4)
3510     Blt_SetWindowInstanceData(tkwin, tvPtr);
3511 #endif
3512     tvPtr->cmdToken = Tcl_CreateObjCommand(interp, Tk_PathName(tvPtr->tkwin),
3513 	Blt_TreeViewWidgetInstCmd, tvPtr, WidgetInstCmdDeleteProc);
3514 
3515 #ifdef ITCL_NAMESPACES
3516     Itk_SetWidgetCommand(tvPtr->tkwin, tvPtr->cmdToken);
3517 #endif
3518     Tk_CreateSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING, SelectionProc,
3519 	tvPtr, XA_STRING);
3520     Tk_CreateEventHandler(tvPtr->tkwin, ExposureMask | StructureNotifyMask |
3521 	FocusChangeMask, TreeViewEventProc, tvPtr);
3522     /*
3523      * Create a default style. This must exist before we can create
3524      * the treeview column.
3525      */
3526     if ((tvPtr->stylePtr = Blt_TreeViewCreateStyle(interp, tvPtr, STYLE_TEXTBOX,
3527 	"text")) == NULL) {
3528 	return NULL;
3529     }
3530     /* Create a default column to display the view of the tree. */
3531     Tcl_DStringInit(&dString);
3532     Tcl_DStringAppend(&dString, "#0", -1);
3533     /*Tcl_DStringAppend(&dString, "BLT TreeView ", -1);
3534     Tcl_DStringAppend(&dString, Tk_PathName(tvPtr->tkwin), -1);*/
3535     result = Blt_TreeViewCreateColumn(tvPtr, &tvPtr->treeColumn,
3536 				      Tcl_DStringValue(&dString), "");
3537     Tcl_DStringFree(&dString);
3538     if (result != TCL_OK) {
3539 	return NULL;
3540     }
3541     Blt_ChainAppend(tvPtr->colChainPtr, &tvPtr->treeColumn);
3542     tvPtr->treeColumn.linkPtr = tvPtr->colChainPtr->headPtr;
3543     Tk_SetClassProcs(tkwin, &treeviewClass, (ClientData)tvPtr);
3544 
3545     return tvPtr;
3546 }
3547 
3548 /*
3549  * ----------------------------------------------------------------------
3550  *
3551  * DestroyTreeView --
3552  *
3553  * 	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
3554  *	to clean up the internal structure of a TreeView at a safe time
3555  *	(when no-one is using it anymore).
3556  *
3557  * Results:
3558  *	None.
3559  *
3560  * Side effects:
3561  *	Everything associated with the widget is freed up.
3562  *
3563  * ----------------------------------------------------------------------
3564  */
3565 static void
DestroyTreeView(DestroyData dataPtr)3566 DestroyTreeView(DestroyData dataPtr)	/* Pointer to the widget record. */
3567 {
3568     Blt_HashEntry *hPtr;
3569     Blt_HashSearch cursor;
3570     TreeView *tvPtr = (TreeView *)dataPtr;
3571     TreeViewButton *buttonPtr;
3572     TreeViewEntry *entryPtr;
3573     TreeViewStyle *stylePtr;
3574 
3575     if (tvPtr->treePath != NULL) {
3576         Blt_Free( tvPtr->treePath );
3577     }
3578     Blt_TreeViewDestroyColumns(tvPtr);
3579     Blt_TreeDeleteEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc,
3580 	   tvPtr);
3581     for (hPtr = Blt_FirstHashEntry(&tvPtr->entryTable, &cursor); hPtr != NULL;
3582 	 hPtr = Blt_NextHashEntry(&cursor)) {
3583 	entryPtr = Blt_GetHashValue(hPtr);
3584 	DestroyEntry((ClientData)entryPtr);
3585     }
3586     Blt_TreeViewOptsInit(tvPtr);
3587     Blt_FreeObjOptions(tvPtr->interp,
3588         bltTreeViewSpecs, (char *)tvPtr, tvPtr->display, 0);
3589     Blt_FreeObjOptions(tvPtr->interp,
3590         bltTreeViewButtonSpecs, (char *)tvPtr, tvPtr->display, 0);
3591     if (tvPtr->tkwin != NULL) {
3592 	Tk_DeleteSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING);
3593 	tvPtr->tkwin = NULL;
3594     }
3595     if (tvPtr->lineGC != NULL) {
3596 	Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
3597 	tvPtr->lineGC = NULL;
3598     }
3599     if (tvPtr->solidGC != NULL) {
3600         Tk_FreeGC(tvPtr->display, tvPtr->solidGC);
3601         tvPtr->solidGC = NULL;
3602     }
3603     if (tvPtr->focusGC != NULL) {
3604 	Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
3605          tvPtr->focusGC = NULL;
3606     }
3607     if (tvPtr->visibleArr != NULL) {
3608 	Blt_Free(tvPtr->visibleArr);
3609          tvPtr->visibleArr = NULL;
3610     }
3611     if (tvPtr->flatArr != NULL) {
3612 	Blt_Free(tvPtr->flatArr);
3613          tvPtr->flatArr = NULL;
3614     }
3615     if (tvPtr->levelInfo != NULL) {
3616 	Blt_Free(tvPtr->levelInfo);
3617 	tvPtr->levelInfo = NULL;
3618     }
3619     buttonPtr = &tvPtr->button;
3620     if (buttonPtr->activeGC != NULL) {
3621 	Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
3622     }
3623     if (buttonPtr->normalGC != NULL) {
3624 	Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
3625 	buttonPtr->normalGC = NULL;
3626     }
3627     if (tvPtr->stylePtr != NULL) {
3628         tvPtr->stylePtr->refCount = 1;
3629         Blt_TreeViewFreeStyle(tvPtr, tvPtr->stylePtr);
3630 	tvPtr->stylePtr = NULL;
3631     }
3632     Blt_DestroyBindingTable(tvPtr->bindTable);
3633     tvPtr->bindTable = NULL;
3634     Blt_ChainDestroy(tvPtr->selChainPtr);
3635     tvPtr->selChainPtr = NULL;
3636     Blt_DeleteHashTable(&tvPtr->entryTagTable);
3637     Blt_DeleteHashTable(&tvPtr->columnTagTable);
3638     Blt_DeleteHashTable(&tvPtr->buttonTagTable);
3639     Blt_DeleteHashTable(&tvPtr->styleTagTable);
3640 
3641     for (hPtr = Blt_FirstHashEntry(&tvPtr->styleTable, &cursor); hPtr != NULL;
3642 	 hPtr = Blt_NextHashEntry(&cursor)) {
3643 	stylePtr = Blt_GetHashValue(hPtr);
3644 	/* stylePtr->refCount = 0; */
3645 	stylePtr->flags &= ~STYLE_USER;
3646 	stylePtr->refCount = 1;
3647 	Blt_TreeViewFreeStyle(tvPtr, stylePtr);
3648     }
3649     if (tvPtr->comboWin != NULL) {
3650 	Tk_DestroyWindow(tvPtr->comboWin);
3651          tvPtr->comboWin = NULL;
3652     }
3653     Blt_DeleteHashTable(&tvPtr->styleTable);
3654     Blt_TreeViewFreeWindows(tvPtr);
3655     Blt_DeleteHashTable(&tvPtr->winTable);
3656     Blt_DeleteHashTable(&tvPtr->winCellTable);
3657 
3658     Blt_DeleteHashTable(&tvPtr->selectTable);
3659     Blt_DeleteHashTable(&tvPtr->uidTable);
3660     Blt_DeleteHashTable(&tvPtr->entryTable);
3661 
3662     Blt_PoolDestroy(tvPtr->entryPool);
3663     tvPtr->entryPool = NULL;
3664     Blt_PoolDestroy(tvPtr->valuePool);
3665     tvPtr->valuePool = NULL;
3666     DumpIconTable(tvPtr);
3667     Blt_Free(tvPtr);
3668 }
3669 
3670 /*
3671 *----------------------------------------------------------------------
3672 *
3673 * Blt_TreeViewTileChangedProc
3674 *
3675 *	Stub for image change notifications.  Since we immediately draw
3676 *	the image into a pixmap, we don't care about image changes.
3677 *
3678 *	It would be better if Tk checked for NULL proc pointers.
3679 *
3680 * Results:
3681 *	None.
3682 *
3683 *----------------------------------------------------------------------
3684 */
3685 /*ARGSUSED*/
3686 void
Blt_TreeViewTileChangedProc(clientData,tile)3687 Blt_TreeViewTileChangedProc(clientData, tile)
3688 ClientData clientData;
3689 Blt_Tile tile;		/* Not used. */
3690 {
3691     TreeView *tvPtr = clientData;
3692 
3693     if (tvPtr->tkwin != NULL) {
3694         Blt_TreeViewEventuallyRedraw(tvPtr);
3695     }
3696 }
3697 
3698 /*
3699  * --------------------------------------------------------------
3700  *
3701  * TreeViewEventProc --
3702  *
3703  * 	This procedure is invoked by the Tk dispatcher for various
3704  * 	events on treeview widgets.
3705  *
3706  * Results:
3707  *	None.
3708  *
3709  * Side effects:
3710  *	When the window gets deleted, internal structures get
3711  *	cleaned up.  When it gets exposed, it is redisplayed.
3712  *
3713  * --------------------------------------------------------------
3714  */
3715 static void
TreeViewEventProc(ClientData clientData,XEvent * eventPtr)3716 TreeViewEventProc(
3717     ClientData clientData,	/* Information about window. */
3718     XEvent *eventPtr)		/* Information about event. */
3719 {
3720     TreeView *tvPtr = clientData;
3721 
3722     if (Tcl_InterpDeleted(tvPtr->interp)) {
3723         return;
3724     }
3725     if (eventPtr->type == Expose) {
3726 	if (eventPtr->xexpose.count == 0) {
3727 	    Blt_TreeViewEventuallyRedraw(tvPtr);
3728 	    Blt_PickCurrentItem(tvPtr->bindTable);
3729 	}
3730     } else if (eventPtr->type == ConfigureNotify) {
3731 	tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
3732 	Blt_TreeViewEventuallyRedraw(tvPtr);
3733     } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
3734 	if (eventPtr->xfocus.detail != NotifyInferior) {
3735 	    if (eventPtr->type == FocusIn) {
3736 		tvPtr->flags |= TV_FOCUS;
3737 	    } else {
3738 		tvPtr->flags &= ~TV_FOCUS;
3739 	    }
3740 	    Blt_TreeViewEventuallyRedraw(tvPtr);
3741 	}
3742     } else if (eventPtr->type == DestroyNotify) {
3743 	tvPtr->flags |= TV_DELETED;
3744 	if (tvPtr->cmdToken != NULL) {
3745 	    /* tvPtr->tkwin = NULL; */
3746 	    Tcl_DeleteCommandFromToken(tvPtr->interp, tvPtr->cmdToken);
3747             tvPtr->cmdToken = NULL;
3748 	}
3749 	if (tvPtr->flags & TV_REDRAW) {
3750 	    Tcl_CancelIdleCall(DisplayTreeView, tvPtr);
3751 	}
3752 	if (tvPtr->flags & TV_SELECT_PENDING) {
3753 	    Tcl_CancelIdleCall(Blt_TreeViewSelectCmdProc, tvPtr);
3754 	}
3755 	Tcl_EventuallyFree(tvPtr, DestroyTreeView);
3756     }
3757 }
3758 
3759 /* Selection Procedures */
3760 /*
3761  *----------------------------------------------------------------------
3762  *
3763  * SelectionProc --
3764  *
3765  *	This procedure is called back by Tk when the selection is
3766  *	requested by someone.  It returns part or all of the selection
3767  *	in a buffer provided by the caller.
3768  *
3769  * Results:
3770  *	The return value is the number of non-NULL bytes stored at
3771  *	buffer.  Buffer is filled (or partially filled) with a
3772  *	NUL-terminated string containing part or all of the
3773  *	selection, as given by offset and maxBytes.
3774  *
3775  * Side effects:
3776  *	None.
3777  *
3778  *----------------------------------------------------------------------
3779  */
3780 static int
SelectionProc(ClientData clientData,int offset,char * buffer,int maxBytes)3781 SelectionProc(
3782     ClientData clientData,	/* Information about the widget. */
3783     int offset,			/* Offset within selection of first
3784 				 * character to be returned. */
3785     char *buffer,		/* Location in which to place
3786 				 * selection. */
3787     int maxBytes)		/* Maximum number of bytes to place
3788 				 * at buffer, not including terminating
3789 				 * NULL character. */
3790 {
3791     Tcl_DString dString;
3792     TreeView *tvPtr = clientData;
3793     TreeViewEntry *entryPtr;
3794     int size;
3795 
3796     if (Tcl_InterpDeleted(tvPtr->interp)) {
3797         return -1;
3798     }
3799     if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
3800 	return -1;
3801     }
3802     /*
3803      * Retrieve the names of the selected entries.
3804      */
3805     Tcl_DStringInit(&dString);
3806     if (tvPtr->flags & TV_SELECT_SORTED) {
3807 	Blt_ChainLink *linkPtr;
3808 
3809 	for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr);
3810 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3811 	    entryPtr = Blt_ChainGetValue(linkPtr);
3812 	    Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
3813 	    Tcl_DStringAppend(&dString, "\n", -1);
3814 	}
3815     } else {
3816 	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3817 	     entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3818 	    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, NULL)) {
3819 		Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
3820 		Tcl_DStringAppend(&dString, "\n", -1);
3821 	    }
3822 	}
3823     }
3824     size = Tcl_DStringLength(&dString) - offset;
3825     strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
3826     Tcl_DStringFree(&dString);
3827     buffer[maxBytes] = '\0';
3828     return (size > maxBytes) ? maxBytes : size;
3829 }
3830 
3831 /*
3832  *----------------------------------------------------------------------
3833  *
3834  * WidgetInstCmdDeleteProc --
3835  *
3836  *	This procedure is invoked when a widget command is deleted.  If
3837  *	the widget isn't already in the process of being destroyed,
3838  *	this command destroys it.
3839  *
3840  * Results:
3841  *	None.
3842  *
3843  * Side effects:
3844  *	The widget is destroyed.
3845  *
3846  *----------------------------------------------------------------------
3847  */
3848 static void
WidgetInstCmdDeleteProc(ClientData clientData)3849 WidgetInstCmdDeleteProc(ClientData clientData)
3850 {
3851     TreeView *tvPtr = clientData;
3852 
3853     /*
3854      * This procedure could be invoked either because the window was
3855      * destroyed and the command was then deleted (in which case tkwin
3856      * is NULL) or because the command was deleted, and then this
3857      * procedure destroys the widget.
3858      */
3859     if (tvPtr->tkwin != NULL) {
3860 	Tk_Window tkwin;
3861 
3862 	tkwin = tvPtr->tkwin;
3863 	tvPtr->tkwin = NULL;
3864 	Tk_DestroyWindow(tkwin);
3865 #ifdef ITCL_NAMESPACES
3866 	Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
3867 #endif /* ITCL_NAMESPACES */
3868     }
3869 }
3870 
Blt_TreeViewMakeStyleDirty(tvPtr)3871 void Blt_TreeViewMakeStyleDirty(tvPtr)
3872 TreeView *tvPtr;
3873 {
3874     TreeViewColumn *columnPtr;
3875     Blt_ChainLink *linkPtr;
3876     TreeViewEntry *entryPtr;
3877     tvPtr->flags |= (TV_LAYOUT | TV_SCROLL |TV_DIRTY);
3878     Blt_TreeViewUpdateStyles(tvPtr);
3879     for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3880     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
3881         entryPtr->flags |= ENTRY_DIRTY;
3882     }
3883 
3884     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
3885         linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3886         columnPtr = Blt_ChainGetValue(linkPtr);
3887         if (columnPtr->stylePtr) {
3888             columnPtr->stylePtr->flags |= STYLE_DIRTY;
3889         }
3890         Blt_TreeViewUpdateColumnGCs(tvPtr, columnPtr);
3891     }
3892 }
3893 
3894 static int
SetupTree(Tcl_Interp * interp,TreeView * tvPtr)3895 SetupTree(Tcl_Interp *interp,  TreeView *tvPtr)
3896 {
3897     Blt_TreeNode root = NULL;
3898 
3899     Blt_TreeViewColumnRekey(tvPtr);
3900     if (tvPtr->treePath != NULL) {
3901         Blt_Free( tvPtr->treePath );
3902     }
3903     tvPtr->treePath = Blt_Strdup(Blt_TreeName(tvPtr->tree));
3904     Blt_TreeCreateEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc,
3905         tvPtr);
3906     TraceColumns(tvPtr);
3907     if (tvPtr->rootNodeNum == 0 ||
3908         (root=Blt_TreeGetNode(tvPtr->tree, tvPtr->rootNodeNum)) == NULL) {
3909         root = Blt_TreeRootNode(tvPtr->tree);
3910     }
3911     tvPtr->rootNode = root;
3912 
3913     /* Automatically add view-entry values to the new tree. */
3914     Blt_TreeApply(root, CreateApplyProc, tvPtr);
3915     tvPtr->focusPtr = tvPtr->rootPtr = Blt_NodeToEntry(tvPtr, root);
3916     tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
3917     Blt_SetFocusItem(tvPtr->bindTable, tvPtr->rootPtr, ITEM_ENTRY);
3918 
3919     /* Automatically open the root node. */
3920     if (Blt_TreeViewOpenEntry(tvPtr, tvPtr->rootPtr) != TCL_OK) {
3921         return TCL_ERROR;
3922     }
3923     if (!(tvPtr->flags & TV_NEW_TAGS)) {
3924         Blt_Tree tree;
3925 
3926         if (Blt_TreeCmdGetToken(interp, Blt_TreeName(tvPtr->tree),
3927             &tree) == TCL_OK) {
3928                 Blt_TreeShareTagTable(tree, tvPtr->tree);
3929         } else {
3930             Tcl_ResetResult(interp);
3931         }
3932     }
3933     return TCL_OK;
3934 }
3935 
Blt_TreeViewChanged(TreeView * tvPtr)3936 void Blt_TreeViewChanged(TreeView *tvPtr) {
3937     Blt_TreeNode node;
3938 
3939     if ((tvPtr->flags & TV_ATTACH) == 0) return;
3940     node = Blt_TreeRootNode(tvPtr->tree);
3941     Blt_TreeApply(node, DeleteApplyProc, tvPtr);
3942     Blt_TreeViewClearSelection(tvPtr);
3943     Blt_TreeReleaseToken(tvPtr->tree);
3944     tvPtr->tree = NULL;
3945     if (Blt_TreeGetToken(tvPtr->interp, tvPtr->treePath, &tvPtr->tree) != TCL_OK) {
3946         return;
3947     }
3948     tvPtr->flags &=  ~TV_ATTACH;
3949     SetupTree(tvPtr->interp, tvPtr);
3950 }
3951 
3952 
3953 /*
3954  * ----------------------------------------------------------------------
3955  *
3956  * Blt_TreeViewUpdateWidget --
3957  *
3958  *	Updates the GCs and other information associated with the
3959  *	treeview widget.
3960  *
3961  * Results:
3962  *	The return value is a standard Tcl result.  If TCL_ERROR is
3963  * 	returned, then interp->result contains an error message.
3964  *
3965  * Side effects:
3966  *	Configuration information, such as text string, colors, font,
3967  *	etc. get set for tvPtr; old resources get freed, if there
3968  *	were any.  The widget is redisplayed.
3969  *
3970  * ----------------------------------------------------------------------
3971  */
3972 static int treeIdx = 0;
3973 int
Blt_TreeViewUpdateWidget(Tcl_Interp * interp,TreeView * tvPtr)3974 Blt_TreeViewUpdateWidget(Tcl_Interp *interp, TreeView *tvPtr)
3975 {
3976     GC newGC;
3977     XGCValues gcValues;
3978     int setupTree;
3979     unsigned long gcMask;
3980 
3981     /*
3982      * GC for dotted vertical line.
3983      */
3984     gcMask = (GCForeground | GCLineWidth);
3985     gcValues.foreground = tvPtr->lineColor->pixel;
3986     gcValues.line_width = tvPtr->lineWidth;
3987     if (tvPtr->dashes > 0) {
3988 	gcMask |= (GCLineStyle | GCDashList);
3989 	gcValues.line_style = LineOnOffDash;
3990 	gcValues.dashes = tvPtr->dashes;
3991     }
3992     newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
3993     if (tvPtr->lineGC != NULL) {
3994 	Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
3995     }
3996     tvPtr->lineGC = newGC;
3997 
3998     /*
3999     * GC for solid line.
4000     */
4001     gcMask = (GCForeground | GCLineWidth);
4002     gcValues.foreground = tvPtr->lineColor->pixel;
4003     gcValues.line_width = tvPtr->lineWidth;
4004     newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
4005     if (tvPtr->solidGC != NULL) {
4006         Tk_FreeGC(tvPtr->display, tvPtr->solidGC);
4007     }
4008     tvPtr->solidGC = newGC;
4009     /*
4010      * GC for active label. Dashed outline.
4011      */
4012     gcMask = GCForeground | GCLineStyle;
4013     gcValues.foreground = tvPtr->focusColor->pixel;
4014     gcValues.line_style = (LineIsDashed(tvPtr->focusDashes))
4015 	? LineOnOffDash : LineSolid;
4016     newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
4017     if (LineIsDashed(tvPtr->focusDashes)) {
4018 	tvPtr->focusDashes.offset = 2;
4019 	Blt_SetDashes(tvPtr->display, newGC, &tvPtr->focusDashes);
4020     }
4021     if (tvPtr->focusGC != NULL) {
4022 	Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
4023     }
4024     tvPtr->focusGC = newGC;
4025 
4026     Blt_TreeViewConfigureButtons(tvPtr);
4027     tvPtr->insetX = tvPtr->highlightWidth + tvPtr->borderWidth + tvPtr->padX;
4028     tvPtr->insetY = tvPtr->highlightWidth + tvPtr->borderWidth + tvPtr->padY;
4029 
4030     setupTree = FALSE;
4031 
4032     /*
4033      * If no tree object was named, allocate a new one.
4034      * BUG: using col 0 coltitle width grows as path...
4035      */
4036     if (tvPtr->tree == NULL) {
4037 	Blt_Tree token;
4038 	char *string, buf[100];
4039 
4040 	/*string = Tk_PathName(tvPtr->tkwin);*/
4041         while (1) {
4042             sprintf(buf, "::blt::_tree%d", treeIdx++);
4043             string = buf;
4044             if (Blt_TreeCreate(interp, string, &token) == TCL_OK) {
4045                 break;
4046             }
4047         }
4048 	tvPtr->tree = token;
4049         Blt_TreeViewColumnRekey(tvPtr);
4050         setupTree = TRUE;
4051     }
4052 
4053     /*
4054      * If the tree object was changed, we need to setup the new one.
4055      */
4056      if (Blt_ObjConfigModified(bltTreeViewSpecs, interp, "-tree",
4057          (char *)NULL)) {
4058              Blt_TreeViewColumnRekey(tvPtr);
4059              setupTree = TRUE;
4060       }
4061      if (setupTree == FALSE && Blt_ObjConfigModified(bltTreeViewSpecs, interp,
4062         "-rootnode", (char *)NULL)) {
4063         Blt_TreeViewColumnRekey(tvPtr);
4064 	setupTree = TRUE;
4065     }
4066 
4067     /*
4068      * These options change the layout of the box.  Mark the widget for update.
4069      */
4070     if (setupTree == FALSE && Blt_ObjConfigModified(bltTreeViewSpecs, interp,
4071         "-font", "-title*", "-pad*",
4072 	"-linespacing", "-*width", "-height", "-hide*", "-flat",
4073 	"-show*", "-icons", "-activeicons", "-leaficons", "-minheight",
4074 	"-*style", "-levelstyles", "-fillnull", "-levelpad", "-formatcmd",
4075 	(char *)NULL)) {
4076 	Blt_TreeViewMakeStyleDirty(tvPtr);
4077     }
4078     /*
4079      * If the tree view was changed, mark all the nodes dirty (we'll
4080      * be switching back to either the full path name or the label)
4081      * and free the array representing the flattened view of the tree.
4082      */
4083     if (Blt_ObjConfigModified(bltTreeViewSpecs, interp, "-hide*", "-flat",
4084         (char *)NULL)) {
4085 	TreeViewEntry *entryPtr;
4086 
4087 	tvPtr->flags |= (TV_DIRTY | TV_RESORT);
4088 	/* Mark all entries dirty. */
4089 	for (entryPtr = tvPtr->rootPtr; setupTree == FALSE && entryPtr != NULL;
4090 	     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
4091 	    entryPtr->flags |= ENTRY_DIRTY;
4092 	}
4093 	if ((!tvPtr->flatView) && (tvPtr->flatArr != NULL)) {
4094 	    Blt_Free(tvPtr->flatArr);
4095 	    tvPtr->flatArr = NULL;
4096 	}
4097     }
4098     if ((tvPtr->reqHeight != Tk_ReqHeight(tvPtr->tkwin)) ||
4099 	(tvPtr->reqWidth != Tk_ReqWidth(tvPtr->tkwin))) {
4100 	Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
4101     }
4102 
4103     if (setupTree) {
4104         if (SetupTree(interp, tvPtr) != TCL_OK) {
4105             return TCL_ERROR;
4106         }
4107     }
4108 
4109     if (Blt_ObjConfigModified(bltTreeViewSpecs, interp, "-font", "-color",
4110 	(char *)NULL)) {
4111 	Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
4112     }
4113     Blt_ObjConfigModified(bltTreeViewSpecs, interp, (char *)NULL);
4114     Blt_TreeViewEventuallyRedraw(tvPtr);
4115     return TCL_OK;
4116 }
4117 
4118 /*
4119  * ----------------------------------------------------------------------
4120  *
4121  * ResetCoordinates --
4122  *
4123  *	Determines the maximum height of all visible entries.
4124  *
4125  *	1. Sets the worldY coordinate for all mapped/open entries.
4126  *	2. Determines if entry needs a button.
4127  *	3. Collects the minimum height of open/mapped entries. (Do for all
4128  *	   entries upon insert).
4129  *	4. Figures out horizontal extent of each entry (will be width of
4130  *	   tree view column).
4131  *	5. Collects maximum icon size for each level.
4132  *	6. The height of its vertical line
4133  *
4134  * Results:
4135  *	Returns 1 if beyond the last visible entry, 0 otherwise.
4136  *
4137  * Side effects:
4138  *	The array of visible nodes is filled.
4139  *
4140  * ----------------------------------------------------------------------
4141  */
4142 static void
ResetCoordinates(TreeView * tvPtr,TreeViewEntry * entryPtr,int * yPtr)4143 ResetCoordinates(
4144     TreeView *tvPtr,
4145     TreeViewEntry *entryPtr,
4146     int *yPtr)
4147 {
4148     int depth;
4149 
4150     entryPtr->worldY = -1;
4151     entryPtr->vertLineLength = -1;
4152     if ((entryPtr != tvPtr->rootPtr) &&
4153 	(Blt_TreeViewEntryIsHidden(entryPtr))) {
4154 	return;     /* If the entry is hidden, then do nothing. */
4155     }
4156     entryPtr->worldY = *yPtr;
4157     entryPtr->vertLineLength = -(*yPtr);
4158     *yPtr += entryPtr->height;
4159 
4160     depth = DEPTH(tvPtr, entryPtr->node) + 1;
4161     if ((tvPtr->flags & TV_HIDE_ROOT) && (entryPtr == tvPtr->rootPtr)) {
4162         /* TODO: adjust size for non-display of root. */
4163         tvPtr->levelInfo[depth].labelWidth = 0;
4164     } else {
4165         if (tvPtr->levelInfo[depth].labelWidth < entryPtr->labelWidth) {
4166             tvPtr->levelInfo[depth].labelWidth = entryPtr->labelWidth;
4167         }
4168     }
4169     if (tvPtr->levelInfo[depth].iconWidth < entryPtr->iconWidth) {
4170         tvPtr->levelInfo[depth].iconWidth = entryPtr->iconWidth;
4171     }
4172     tvPtr->levelInfo[depth].iconWidth |= 0x01;
4173 
4174     if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
4175 	TreeViewEntry *bottomPtr, *childPtr;
4176 
4177 	bottomPtr = entryPtr;
4178 	for (childPtr = Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN);
4179 	     childPtr != NULL;
4180 	     childPtr = Blt_TreeViewNextSibling(childPtr, ENTRY_HIDDEN)){
4181 	    ResetCoordinates(tvPtr, childPtr, yPtr);
4182 	    bottomPtr = childPtr;
4183 	}
4184 	entryPtr->vertLineLength += bottomPtr->worldY;
4185     }
4186 }
4187 
4188 static void
AdjustColumns(TreeView * tvPtr)4189 AdjustColumns(TreeView *tvPtr)
4190 {
4191     Blt_ChainLink *linkPtr;
4192     TreeViewColumn *columnPtr;
4193     double weight;
4194     int nOpen;
4195     int size, avail, ration, growth;
4196 
4197     growth = VPORTWIDTH(tvPtr) - tvPtr->worldWidth;
4198     nOpen = 0;
4199     weight = 0.0;
4200     /* Find out how many columns still have space available */
4201     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
4202 	 linkPtr = Blt_ChainNextLink(linkPtr)) {
4203 	columnPtr = Blt_ChainGetValue(linkPtr);
4204 	if ((columnPtr->hidden) ||
4205 	    (columnPtr->weight == 0.0) ||
4206 	    (columnPtr->width >= columnPtr->max) ||
4207 	    (columnPtr->reqWidth > 0)) {
4208 	    continue;
4209 	}
4210 	nOpen++;
4211 	weight += columnPtr->weight;
4212     }
4213 
4214     while ((nOpen > 0) && (weight > 0.0) && (growth > 0)) {
4215 	ration = (int)(growth / weight);
4216 	if (ration == 0) {
4217 	    ration = 1;
4218 	}
4219 	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4220 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4221 	    columnPtr = Blt_ChainGetValue(linkPtr);
4222 	    if ((columnPtr->hidden) ||
4223 		(columnPtr->weight == 0.0) ||
4224 		(columnPtr->width >= columnPtr->max) ||
4225 		(columnPtr->reqWidth > 0)) {
4226 		continue;
4227 	    }
4228 	    size = (int)(ration * columnPtr->weight);
4229 	    if (size > growth) {
4230 		size = growth;
4231 	    }
4232 	    avail = columnPtr->max - columnPtr->width;
4233 	    if (size > avail) {
4234 		size = avail;
4235 		nOpen--;
4236 		weight -= columnPtr->weight;
4237 	    }
4238 	    growth -= size;
4239 	    columnPtr->width += size;
4240 	}
4241     }
4242 }
4243 
4244 /*
4245  * ----------------------------------------------------------------------
4246  *
4247  * ComputeFlatLayout --
4248  *
4249  *	Recompute the layout when entries are opened/closed,
4250  *	inserted/deleted, or when text attributes change (such as
4251  *	font, linespacing).
4252  *
4253  * Results:
4254  *	None.
4255  *
4256  * Side effects:
4257  *	The world coordinates are set for all the opened entries.
4258  *
4259  * ----------------------------------------------------------------------
4260  */
4261 static int
ComputeFlatLayout(TreeView * tvPtr)4262 ComputeFlatLayout(TreeView *tvPtr)
4263 {
4264     Blt_ChainLink *linkPtr;
4265     TreeViewColumn *columnPtr;
4266     TreeViewEntry **p;
4267     TreeViewEntry *entryPtr;
4268     int count;
4269     int maxX;
4270     int y;
4271 
4272     /*
4273      * Pass 1:	Reinitialize column sizes and loop through all nodes.
4274      *
4275      *		1. Recalculate the size of each entry as needed.
4276      *		2. The maximum depth of the tree.
4277      *		3. Minimum height of an entry.  Dividing this by the
4278      *		   height of the widget gives a rough estimate of the
4279      *		   maximum number of visible entries.
4280      *		4. Build an array to hold level information to be filled
4281      *		   in on pass 2.
4282      */
4283     if (tvPtr->flags & (TV_DIRTY | TV_UPDATE)) {
4284 	int position;
4285 
4286 	/* Reset the positions of all the columns and initialize the
4287 	 * column used to track the widest value. */
4288 	position = 1;
4289 	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4290 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4291 	    columnPtr = Blt_ChainGetValue(linkPtr);
4292 	    columnPtr->maxWidth = 0;
4293 	    columnPtr->max = SHRT_MAX;
4294 	    if (columnPtr->reqMax > 0) {
4295 		columnPtr->max = columnPtr->reqMax;
4296 	    }
4297 	    columnPtr->position = position;
4298 	    position++;
4299 	}
4300 
4301 	/* If the view needs to be resorted, free the old view. */
4302 	if ((tvPtr->flags & TV_RESORT) && (tvPtr->flatArr != NULL)) {
4303 	    Blt_Free(tvPtr->flatArr);
4304 	    tvPtr->flatArr = NULL;
4305 	}
4306 
4307 	/* Recreate the flat view of all the open and not-hidden entries. */
4308 	if (tvPtr->flatArr == NULL) {
4309 	    count = 0;
4310 	    /* Count the number of open entries to allocate for the array. */
4311 	    for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
4312 		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
4313 		if ((tvPtr->flags & TV_HIDE_ROOT) &&
4314 		    (entryPtr == tvPtr->rootPtr)) {
4315 		    continue;
4316 		}
4317 		count++;
4318 	    }
4319 	    tvPtr->nEntries = count;
4320 
4321 	    /* Allocate an array for the flat view. */
4322 	    tvPtr->flatArr = Blt_Calloc((count + 1), sizeof(TreeViewEntry *));
4323 	    assert(tvPtr->flatArr);
4324 
4325 	    /* Fill the array with open and not-hidden entries */
4326 	    p = tvPtr->flatArr;
4327 	    for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
4328 		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
4329 		if ((tvPtr->flags & TV_HIDE_ROOT) &&
4330 		    (entryPtr == tvPtr->rootPtr)) {
4331 		    continue;
4332 		}
4333 		*p++ = entryPtr;
4334 	    }
4335 	    *p = NULL;
4336 	    tvPtr->flags &= ~TV_SORTED;	/* Indicate the view isn't sorted. */
4337 	}
4338 
4339 	/* Collect the extents of the entries in the flat view. */
4340 	tvPtr->depth = 0;
4341 	tvPtr->minHeight = SHRT_MAX;
4342 	for (p = tvPtr->flatArr; p != NULL && *p != NULL; p++) {
4343 	    entryPtr = *p;
4344 	    if (GetEntryExtents(tvPtr, entryPtr) != TCL_OK) { return TCL_ERROR; }
4345 	    if (tvPtr->minHeight > entryPtr->height) {
4346 		tvPtr->minHeight = entryPtr->height;
4347 	    }
4348 	    entryPtr->flags &= ~ENTRY_HAS_BUTTON;
4349 	}
4350 	if (tvPtr->levelInfo != NULL) {
4351 	    Blt_Free(tvPtr->levelInfo);
4352 	}
4353 	tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
4354 	assert(tvPtr->levelInfo);
4355 	tvPtr->flags &= ~(TV_DIRTY | TV_UPDATE | TV_RESORT);
4356 	if (tvPtr->flags & TV_SORT_AUTO) {
4357 	    /* If we're auto-sorting, schedule the view to be resorted. */
4358 	    tvPtr->flags |= TV_SORT_PENDING;
4359 	}
4360     }
4361 
4362     if (tvPtr->flags & TV_SORT_PENDING) {
4363 	Blt_TreeViewSortFlatView(tvPtr);
4364     }
4365 
4366     tvPtr->levelInfo[0].labelWidth = tvPtr->levelInfo[0].x =
4367 	    tvPtr->levelInfo[0].iconWidth = 0;
4368     /*
4369      * Pass 2:	Loop through all open/mapped nodes.
4370      *
4371      *		1. Set world y-coordinates for entries. We must defer
4372      *		   setting the x-coordinates until we know the maximum
4373      *		   icon sizes at each level.
4374      *		2. Compute the maximum depth of the tree.
4375      *		3. Build an array to hold level information.
4376      */
4377     y = 0;
4378     count = 0;
4379     if (tvPtr->flags & TV_HIDE_ICONS) {
4380 	tvPtr->levelInfo[0].iconWidth = 5;
4381     }
4382     for(p = tvPtr->flatArr; p != NULL && *p != NULL; p++) {
4383 	entryPtr = *p;
4384 	entryPtr->flatIndex = count++;
4385 	entryPtr->worldY = y;
4386 	entryPtr->vertLineLength = 0;
4387 	y += entryPtr->height;
4388 	if (tvPtr->levelInfo[0].labelWidth < entryPtr->labelWidth) {
4389 	    tvPtr->levelInfo[0].labelWidth = entryPtr->labelWidth;
4390 	}
4391         if (tvPtr->flags & TV_HIDE_ICONS) {
4392             continue;
4393         }
4394 	if (tvPtr->levelInfo[0].iconWidth < entryPtr->iconWidth) {
4395 	    tvPtr->levelInfo[0].iconWidth = entryPtr->iconWidth;
4396 	}
4397     }
4398     tvPtr->levelInfo[0].iconWidth |= 0x01;
4399     tvPtr->worldHeight = y;	/* Set the scroll height of the hierarchy. */
4400     if (tvPtr->worldHeight < 1) {
4401 	tvPtr->worldHeight = 1;
4402     }
4403     maxX = tvPtr->levelInfo[0].iconWidth + tvPtr->levelInfo[0].labelWidth;
4404     tvPtr->treeColumn.maxWidth = maxX;
4405     tvPtr->treeWidth = maxX;
4406     tvPtr->flags |= TV_VIEWPORT;
4407     return TCL_OK;
4408 }
4409 
4410 /*
4411  * ----------------------------------------------------------------------
4412  *
4413  * ComputeTreeLayout --
4414  *
4415  *	Recompute the layout when entries are opened/closed,
4416  *	inserted/deleted, or when text attributes change (such as
4417  *	font, linespacing).
4418  *
4419  * Results:
4420  *	None.
4421  *
4422  * Side effects:
4423  *	The world coordinates are set for all the opened entries.
4424  *
4425  * ----------------------------------------------------------------------
4426  */
4427 static int
ComputeTreeLayout(TreeView * tvPtr)4428 ComputeTreeLayout(TreeView *tvPtr)
4429 {
4430     Blt_ChainLink *linkPtr;
4431     TreeViewColumn *columnPtr;
4432     TreeViewEntry *entryPtr;
4433     int maxX, x, y;
4434     int sum;
4435     register int i;
4436     int hr;
4437 
4438     hr = ((tvPtr->flags & TV_HIDE_ROOT) ? 1 : 0);
4439 
4440     /*
4441      * Pass 1:	Reinitialize column sizes and loop through all nodes.
4442      *
4443      *		1. Recalculate the size of each entry as needed.
4444      *		2. The maximum depth of the tree.
4445      *		3. Minimum height of an entry.  Dividing this by the
4446      *		   height of the widget gives a rough estimate of the
4447      *		   maximum number of visible entries.
4448      *		4. Build an array to hold level information to be filled
4449      *		   in on pass 2.
4450      */
4451     if (tvPtr->flags & TV_DIRTY) {
4452 	int position;
4453 
4454 	position = 1;
4455 	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4456 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4457 	    columnPtr = Blt_ChainGetValue(linkPtr);
4458 	    columnPtr->maxWidth = 0;
4459 	    columnPtr->max = SHRT_MAX;
4460 	    if (columnPtr->reqMax > 0) {
4461 		columnPtr->max = columnPtr->reqMax;
4462 	    }
4463 	    columnPtr->position = position;
4464 	    position++;
4465 	}
4466 	tvPtr->minHeight = SHRT_MAX;
4467 	tvPtr->depth = 0;
4468 	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
4469 	     entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
4470 	    if (GetEntryExtents(tvPtr, entryPtr) != TCL_OK) { return TCL_ERROR; }
4471 	    if (tvPtr->minHeight > entryPtr->height) {
4472 		tvPtr->minHeight = entryPtr->height;
4473 	    }
4474 	    /*
4475 	     * Determine if the entry should display a button
4476 	     * (indicating that it has children) and mark the
4477 	     * entry accordingly.
4478 	     */
4479 	    entryPtr->flags &= ~ENTRY_HAS_BUTTON;
4480 	    if (entryPtr->flags & BUTTON_SHOW) {
4481 		entryPtr->flags |= ENTRY_HAS_BUTTON;
4482 	    } else if (entryPtr->flags & BUTTON_AUTO) {
4483 		if (tvPtr->flags & TV_HIDE_LEAVES) {
4484 		    /* Check that a non-leaf child exists */
4485 		    if (Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN)
4486 			!= NULL) {
4487 			entryPtr->flags |= ENTRY_HAS_BUTTON;
4488 		    }
4489 		} else if (!Blt_TreeViewIsLeaf(entryPtr)) {
4490 		    entryPtr->flags |= ENTRY_HAS_BUTTON;
4491 		}
4492 	    }
4493 	    /* Determine the depth of the tree. */
4494 	    if (tvPtr->depth < DEPTH(tvPtr, entryPtr->node)) {
4495 		tvPtr->depth = DEPTH(tvPtr, entryPtr->node);
4496 	    }
4497 	}
4498 	if (tvPtr->flags & TV_SORT_PENDING) {
4499 	    Blt_TreeViewSortTreeView(tvPtr);
4500 	}
4501 	if (tvPtr->levelInfo != NULL) {
4502 	    Blt_Free(tvPtr->levelInfo);
4503 	}
4504 	tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
4505 	assert(tvPtr->levelInfo);
4506 	tvPtr->flags &= ~(TV_DIRTY | TV_RESORT);
4507     }
4508     for (i = 0; i <= (tvPtr->depth + 1); i++) {
4509 	tvPtr->levelInfo[i].labelWidth = tvPtr->levelInfo[i].x =
4510 	    tvPtr->levelInfo[i].iconWidth = 0;
4511     }
4512     /*
4513      * Pass 2:	Loop through all open/mapped nodes.
4514      *
4515      *		1. Set world y-coordinates for entries. We must defer
4516      *		   setting the x-coordinates until we know the maximum
4517      *		   icon sizes at each level.
4518      *		2. Compute the maximum depth of the tree.
4519      *		3. Build an array to hold level information.
4520      */
4521     y = 0;
4522     if (tvPtr->flags & TV_HIDE_ROOT) {
4523 	/* If the root entry is to be hidden, cheat by offsetting
4524 	 * the y-coordinates by the height of the entry. */
4525 	y = -(tvPtr->rootPtr->height);
4526     }
4527     ResetCoordinates(tvPtr, tvPtr->rootPtr, &y);
4528     tvPtr->worldHeight = y;	/* Set the scroll height of the hierarchy. */
4529     if (tvPtr->worldHeight < 1) {
4530 	tvPtr->worldHeight = 1;
4531     }
4532     sum = maxX = 0;
4533     for (i = 0; i <= (tvPtr->depth + 1); i++) {
4534 	sum += tvPtr->levelInfo[i].iconWidth + (i==0?0:tvPtr->levelPad);
4535 	if (i <= tvPtr->depth) {
4536 	    tvPtr->levelInfo[i + 1].x = sum;
4537 	}
4538          if (tvPtr->lineWidth>0 || tvPtr->button.reqSize>0 || i>hr) {
4539              x = sum + tvPtr->levelInfo[i].labelWidth;
4540          } else {
4541              tvPtr->levelInfo[i + 1].x = sum = x = BUTTON_PAD;
4542          }
4543 	if (x > maxX) {
4544 	    maxX = x;
4545 	}
4546     }
4547     tvPtr->treeColumn.maxWidth = maxX;
4548     tvPtr->treeWidth = maxX;
4549     return TCL_OK;
4550 }
4551 
4552 
4553 static void
LayoutColumns(TreeView * tvPtr)4554 LayoutColumns(TreeView *tvPtr)
4555 {
4556     Blt_ChainLink *linkPtr;
4557     TreeViewColumn *columnPtr;
4558     int sum, reqWid;
4559 
4560     /* The width of the widget (in world coordinates) is the sum
4561      * of the column widths. */
4562 
4563     tvPtr->worldWidth = tvPtr->titleHeight = 0;
4564     sum = 0;
4565     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
4566 	 linkPtr = Blt_ChainNextLink(linkPtr)) {
4567 	columnPtr = Blt_ChainGetValue(linkPtr);
4568 	columnPtr->width = 0;
4569 	if (!columnPtr->hidden) {
4570 	    if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) &&
4571 		(tvPtr->titleHeight < columnPtr->titleHeight)) {
4572 		tvPtr->titleHeight = columnPtr->titleHeight;
4573 	    }
4574             reqWid = columnPtr->reqWidth;
4575 	    if (reqWid <= 0 && columnPtr->autoWidth>0) {
4576 	        int mw;
4577                 mw = MAX(columnPtr->titleWidth, columnPtr->maxWidth);
4578                 if (mw > columnPtr->autoWidth) {
4579                     reqWid = columnPtr->autoWidth;
4580                 }
4581              }
4582 	    if (reqWid > 0) {
4583 		columnPtr->width = reqWid;
4584 	    } else {
4585 		/* The computed width of a column is the maximum of
4586 		 * the title width and the widest entry. */
4587 		columnPtr->width = MAX(columnPtr->titleWidth,
4588 				       columnPtr->maxWidth);
4589 		/* Check that the width stays within any constraints that
4590 		 * have been set. */
4591 		if ((columnPtr->reqMin > 0) &&
4592 		    (columnPtr->reqMin > columnPtr->width)) {
4593 		    columnPtr->width = columnPtr->reqMin;
4594 		}
4595 		if ((columnPtr->reqMax > 0) &&
4596 		    (columnPtr->reqMax < columnPtr->width)) {
4597 		    columnPtr->width = columnPtr->reqMax;
4598 		}
4599 	    }
4600 	    columnPtr->width +=
4601 		PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth;
4602 	}
4603 	columnPtr->worldX = sum;
4604 	sum += columnPtr->width;
4605     }
4606     tvPtr->worldWidth = sum;
4607     if (VPORTWIDTH(tvPtr) > sum) {
4608 	AdjustColumns(tvPtr);
4609     }
4610     sum = 0;
4611     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
4612 	 linkPtr = Blt_ChainNextLink(linkPtr)) {
4613 	columnPtr = Blt_ChainGetValue(linkPtr);
4614 	columnPtr->worldX = sum;
4615 	sum += columnPtr->width;
4616     }
4617     if (tvPtr->titleHeight > 0) {
4618 	/* If any headings are displayed, add some extra padding to
4619 	 * the height. */
4620 	tvPtr->titleHeight += 4;
4621     }
4622     /* tvPtr->worldWidth += 10; */
4623     if (tvPtr->yScrollUnits < 1) {
4624 	tvPtr->yScrollUnits = 1;
4625     }
4626     if (tvPtr->xScrollUnits < 1) {
4627 	tvPtr->xScrollUnits = 1;
4628     }
4629     if (tvPtr->worldWidth < 1) {
4630 	tvPtr->worldWidth = 1;
4631     }
4632     tvPtr->flags &= ~TV_LAYOUT;
4633     tvPtr->flags |= TV_SCROLL;
4634 }
4635 
4636 /*
4637  * ----------------------------------------------------------------------
4638  *
4639  * Blt_TreeViewComputeLayout --
4640  *
4641  *	Recompute the layout when entries are opened/closed,
4642  *	inserted/deleted, or when text attributes change (such as
4643  *	font, linespacing).
4644  *
4645  * Results:
4646  *	None.
4647  *
4648  * Side effects:
4649  *	The world coordinates are set for all the opened entries.
4650  *
4651  * ----------------------------------------------------------------------
4652  */
4653 int
Blt_TreeViewComputeLayout(TreeView * tvPtr)4654 Blt_TreeViewComputeLayout(TreeView *tvPtr)
4655 {
4656     Blt_ChainLink *linkPtr;
4657     TreeViewColumn *columnPtr;
4658     TreeViewEntry *entryPtr;
4659     TreeViewValue *valuePtr;
4660 
4661     if (tvPtr->flatView) {
4662 	if (ComputeFlatLayout(tvPtr) != TCL_OK) { return TCL_ERROR; }
4663     } else {
4664         if (ComputeTreeLayout(tvPtr) != TCL_OK) { return TCL_ERROR; }
4665     }
4666     /*
4667      * Determine the width of each column based upon the entries
4668      * that as open (not hidden).  The widest entry in a column
4669      * determines the width of that column.
4670      */
4671 
4672     /* Initialize the columns. */
4673     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4674 	 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4675 	columnPtr = Blt_ChainGetValue(linkPtr);
4676 	columnPtr->maxWidth = 0;
4677 	columnPtr->max = SHRT_MAX;
4678 	if (columnPtr->reqMax > 0) {
4679 	    columnPtr->max = columnPtr->reqMax;
4680 	}
4681     }
4682     /* The treeview column width was computed earlier. */
4683     tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
4684 
4685     /*
4686      * Look at all open entries and their values.  Determine the column
4687      * widths by tracking the maximum width value in each column.
4688      */
4689     for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
4690 	 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
4691 	for (valuePtr = entryPtr->values; valuePtr != NULL;
4692 	     valuePtr = valuePtr->nextPtr) {
4693 	    if (valuePtr->columnPtr->maxWidth < valuePtr->width) {
4694 		valuePtr->columnPtr->maxWidth = valuePtr->width;
4695 	    }
4696 	}
4697     }
4698     /* Now layout the columns with the proper sizes. */
4699     LayoutColumns(tvPtr);
4700     return TCL_OK;
4701 }
4702 
4703 static int
ComputeFillLabel(TreeView * tvPtr,TreeViewEntry * entryPtr)4704 ComputeFillLabel(TreeView *tvPtr, TreeViewEntry *entryPtr)
4705 {
4706     TreeViewColumn *columnPtr;
4707     Tcl_Interp *interp = tvPtr->interp;
4708     int result, objc;
4709     char *string;
4710     Tcl_Obj **objv, *objPtr;
4711 
4712 
4713     columnPtr = &tvPtr->treeColumn;
4714     if (columnPtr->fillCmd == NULL || entryPtr->labelUid != NULL) {
4715         return TCL_OK;
4716     }
4717     string = Blt_TreeNodeLabel(entryPtr->node);
4718     objPtr = Tcl_DuplicateObj(columnPtr->fillCmd);
4719 
4720     Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(string,-1));
4721     Tcl_IncrRefCount(objPtr);
4722     if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) == TCL_OK) {
4723 
4724         Tcl_Preserve(entryPtr);
4725         result = Tcl_EvalObjv(interp, objc, objv, TCL_EVAL_GLOBAL);
4726         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
4727             Tcl_DecrRefCount(objPtr);
4728             Tcl_Release(entryPtr);
4729             return TCL_ERROR;
4730         }
4731         string = Tcl_GetStringResult(interp);
4732         if (result != TCL_ERROR && string[0]) {
4733             entryPtr->labelUid = Blt_TreeViewGetUid(tvPtr, string);
4734         }
4735         Tcl_Release(entryPtr);
4736     }
4737     Tcl_DecrRefCount(objPtr);
4738     return TCL_OK;
4739 }
4740 
4741 
4742 /*
4743  * ----------------------------------------------------------------------
4744  *
4745  * ComputeVisibleEntries --
4746  *
4747  *	The entries visible in the viewport (the widget's window) are
4748  *	inserted into the array of visible nodes.
4749  *
4750  * Results:
4751  *	Returns 1 if beyond the last visible entry, 0 otherwise.
4752  *
4753  * Side effects:
4754  *	The array of visible nodes is filled.
4755  *
4756  * ----------------------------------------------------------------------
4757  */
4758 static int
ComputeVisibleEntries(TreeView * tvPtr)4759 ComputeVisibleEntries(TreeView *tvPtr)
4760 {
4761     int height;
4762     int level;
4763     int nSlots;
4764     int x, maxX, nAbove;
4765     int xOffset, yOffset;
4766 
4767     xOffset = Blt_AdjustViewport(tvPtr->xOffset, tvPtr->worldWidth,
4768 	VPORTWIDTH(tvPtr), tvPtr->xScrollUnits, tvPtr->scrollMode);
4769     yOffset = Blt_AdjustViewport(tvPtr->yOffset,
4770 	tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
4771 	tvPtr->scrollMode);
4772 
4773     if ((xOffset != tvPtr->xOffset) || (yOffset != tvPtr->yOffset)) {
4774 	tvPtr->yOffset = yOffset;
4775 	tvPtr->xOffset = xOffset;
4776 	tvPtr->flags |= TV_VIEWPORT;
4777     }
4778 
4779     tvPtr->nVisible = 0;
4780     tvPtr->nAbove = 0;
4781     nAbove = 0;
4782     height = VPORTHEIGHT(tvPtr) - tvPtr->insetY;
4783     if (height<=1) return TCL_OK;
4784 
4785     /* Allocate worst case number of slots for entry array. */
4786     nSlots = (height / tvPtr->minHeight) + 3;
4787     if (nSlots != tvPtr->nVisible) {
4788 	if (tvPtr->visibleArr != NULL) {
4789 	    Blt_Free(tvPtr->visibleArr);
4790 	}
4791 	tvPtr->visibleArr = Blt_Calloc(nSlots+1, sizeof(TreeViewEntry *));
4792 	assert(tvPtr->visibleArr);
4793     }
4794     if (tvPtr->visibleArr) {
4795         tvPtr->visibleArr[0] = NULL;
4796     }
4797 
4798     if (tvPtr->rootPtr->flags & ENTRY_HIDDEN) {
4799 	return TCL_OK;		/* Root node is hidden. */
4800     }
4801     /* Find the node where the view port starts. */
4802     if (tvPtr->flatView) {
4803 	register TreeViewEntry **p, *entryPtr;
4804 
4805 	/* Find the starting entry visible in the viewport. It can't
4806 	 * be hidden or any of it's ancestors closed. */
4807     again:
4808 	for (p = tvPtr->flatArr; p != NULL && *p != NULL; p++) {
4809 	    entryPtr = *p;
4810 	    if ((entryPtr->worldY + entryPtr->height) > tvPtr->yOffset) {
4811 		break;
4812 	    }
4813 	    nAbove++;
4814 	}
4815 	/*
4816 	 * If we can't find the starting node, then the view must be
4817 	 * scrolled down, but some nodes were deleted.  Reset the view
4818 	 * back to the top and try again.
4819 	 */
4820 	if (p != NULL && *p == NULL) {
4821 	    if (tvPtr->yOffset == 0) {
4822 		return TCL_OK;	/* All entries are hidden. */
4823 	    }
4824 	    tvPtr->yOffset = 0;
4825 	    goto again;
4826 	}
4827 
4828 	maxX = 0;
4829 	height += tvPtr->yOffset;
4830 	for (/* empty */; p != NULL && *p != NULL; p++) {
4831 	    entryPtr = *p;
4832 	    if (ComputeFillLabel(tvPtr, entryPtr) != TCL_OK) {
4833 	        return TCL_ERROR;
4834 	    }
4835 	    entryPtr->worldX = LEVELX(0) + tvPtr->treeColumn.worldX;
4836 	    x = entryPtr->worldX + ICONWIDTH(0) + entryPtr->width;
4837 	    if (x > maxX) {
4838 		maxX = x;
4839 	    }
4840 	    if (entryPtr->worldY >= height) {
4841 		break;
4842 	    }
4843 	    entryPtr->stylePtr = entryPtr->realStylePtr;
4844 	    tvPtr->visibleArr[tvPtr->nVisible] = entryPtr;
4845 	    tvPtr->nVisible++;
4846 	}
4847 	tvPtr->visibleArr[tvPtr->nVisible] = NULL;
4848     } else {
4849 	TreeViewEntry *entryPtr;
4850 
4851 	entryPtr = tvPtr->rootPtr;
4852 	while (entryPtr && (entryPtr->worldY + entryPtr->height) <= tvPtr->yOffset) {
4853 	    for (entryPtr = Blt_TreeViewLastChild(entryPtr, ENTRY_HIDDEN);
4854 		 entryPtr != NULL;
4855 		 entryPtr = Blt_TreeViewPrevSibling(entryPtr, ENTRY_HIDDEN)) {
4856 
4857 		if (entryPtr->worldY <= tvPtr->yOffset) {
4858                       if (entryPtr->height >= Tk_Height(tvPtr->tkwin)) {
4859                           nAbove++;
4860                       }
4861                       break;
4862 		}
4863 		/* Alternate or odd row style start index. */
4864                 nAbove++;
4865             }
4866 	    /*
4867 	     * If we can't find the starting node, then the view must be
4868 	     * scrolled down, but some nodes were deleted.  Reset the view
4869 	     * back to the top and try again.
4870 	     */
4871 	    if (entryPtr == NULL) {
4872 		if (tvPtr->yOffset == 0) {
4873 		    return TCL_OK;	/* All entries are hidden. */
4874 		}
4875 		tvPtr->yOffset = 0;
4876 		continue;
4877 	    }
4878          }
4879 
4880 
4881 	height += tvPtr->yOffset;
4882 	maxX = 0;
4883 	tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
4884 
4885 	for (/* empty */; entryPtr != NULL;
4886 		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
4887 	    /*
4888 	     * Compute and save the entry's X-coordinate now that we know
4889 	     * the maximum level offset for the entire widget.
4890 	     */
4891             if (ComputeFillLabel(tvPtr, entryPtr) != TCL_OK) {
4892                 return TCL_ERROR;
4893             }
4894 	    level = DEPTH(tvPtr, entryPtr->node);
4895 	    entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
4896 
4897 	    x = entryPtr->worldX + ICONWIDTH(level) + ICONWIDTH(level + 1) +
4898 		entryPtr->width;
4899 	    if (x > maxX) {
4900 		maxX = x;
4901 	    }
4902 	    if (entryPtr->worldY >= height) {
4903 		break;
4904 	    }
4905 	    SetEntryStyle(tvPtr, entryPtr);
4906             tvPtr->visibleArr[tvPtr->nVisible] = entryPtr;
4907             tvPtr->nVisible++;
4908          }
4909          if (tvPtr->visibleArr) {
4910              tvPtr->visibleArr[tvPtr->nVisible] = NULL;
4911          }
4912     }
4913     /*
4914      * -------------------------------------------------------------------
4915      *
4916      * Note:	It's assumed that the view port always starts at or
4917      *		over an entry.  Check that a change in the hierarchy
4918      *		(e.g. closing a node) hasn't left the viewport beyond
4919      *		the last entry.  If so, adjust the viewport to start
4920      *		on the last entry.
4921      *
4922      * -------------------------------------------------------------------
4923      */
4924     if (tvPtr->xOffset > (tvPtr->worldWidth - tvPtr->xScrollUnits)) {
4925 	tvPtr->xOffset = tvPtr->worldWidth - tvPtr->xScrollUnits;
4926     }
4927     if (tvPtr->yOffset > (tvPtr->worldHeight - tvPtr->yScrollUnits)) {
4928 	tvPtr->yOffset = tvPtr->worldHeight - tvPtr->yScrollUnits;
4929     }
4930     tvPtr->xOffset = Blt_AdjustViewport(tvPtr->xOffset,
4931 	tvPtr->worldWidth, VPORTWIDTH(tvPtr), tvPtr->xScrollUnits,
4932 	tvPtr->scrollMode);
4933     tvPtr->yOffset = Blt_AdjustViewport(tvPtr->yOffset,
4934 	tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
4935 	tvPtr->scrollMode);
4936 
4937     Blt_PickCurrentItem(tvPtr->bindTable);
4938     tvPtr->flags &= ~TV_DIRTY;
4939     tvPtr->nAbove = nAbove;
4940     return TCL_OK;
4941 }
4942 
4943 
4944 /*
4945  * ---------------------------------------------------------------------------
4946  *
4947  * DrawVerticals --
4948  *
4949  * 	Draws vertical lines for the ancestor nodes.  While the entry
4950  *	of the ancestor may not be visible, its vertical line segment
4951  *	does extent into the viewport.  So walk back up the hierarchy
4952  *	drawing lines until we get to the root.
4953  *
4954  * Results:
4955  *	None.
4956  *
4957  * Side Effects:
4958  *	Vertical lines are drawn for the ancestor nodes.
4959  *
4960  * ---------------------------------------------------------------------------
4961  */
4962 static void
DrawVerticals(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable)4963 DrawVerticals(
4964     TreeView *tvPtr,		/* Widget record containing the attribute
4965 				 * information for buttons. */
4966     TreeViewEntry *entryPtr,	/* Entry to be drawn. */
4967     Drawable drawable)		/* Pixmap or window to draw into. */
4968 {
4969     int height, level;
4970     int x, y;
4971     int x1, y1a, x2, y2;
4972 
4973     while (entryPtr != tvPtr->rootPtr) {
4974 	entryPtr = Blt_TreeViewParentEntry(entryPtr);
4975 	if (entryPtr == NULL) {
4976 	    break;
4977 	}
4978 	level = DEPTH(tvPtr, entryPtr->node);
4979 	/*
4980 	 * World X-coordinates aren't computed only for entries that are
4981 	 * outside the view port.  So for each off-screen ancestor node
4982 	 * compute it here too.
4983 	 */
4984 	entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
4985 	x = SCREENX(tvPtr, entryPtr->worldX);
4986 	y = SCREENY(tvPtr, entryPtr->worldY);
4987 	height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
4988 		tvPtr->button.height);
4989 	y += (height - tvPtr->button.height) / 2;
4990 	x1 = x2 = x + ICONWIDTH(level) + ICONWIDTH(level + 1) / 2;
4991 	y1a = y + tvPtr->button.height / 2;
4992 	y2 = y1a + entryPtr->vertLineLength;
4993 	if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
4994 	    y1a += entryPtr->height;
4995 	}
4996 	/*
4997 	 * Clip the line's Y-coordinates at the viewport borders.
4998 	 */
4999 	if (y1a <= tvPtr->insetY) {
5000 	    y1a = tvPtr->insetY+1;
5001 	}
5002 	if (y1a < 0) {
5003 	    y1a = (y1a & 0x1);	/* Make sure the dotted line starts on
5004 				 * the same even/odd pixel. */
5005 	}
5006 	if (y2 > (Tk_Height(tvPtr->tkwin)-tvPtr->insetY)) {
5007              y2 = (Tk_Height(tvPtr->tkwin)-tvPtr->insetY);
5008 	}
5009 	if ((y1a < Tk_Height(tvPtr->tkwin)) && (y2 > 0)) {
5010 	    XDrawLine(tvPtr->display, drawable, tvPtr->lineGC,
5011 	      x1, y1a, x2, y2);
5012 
5013 	}
5014     }
5015 }
5016 
5017 void
Blt_TreeViewDrawRule(TreeView * tvPtr,TreeViewColumn * columnPtr,Drawable drawable)5018 Blt_TreeViewDrawRule(
5019     TreeView *tvPtr,		/* Widget record containing the
5020 				 * attribute information for rules. */
5021     TreeViewColumn *columnPtr,
5022     Drawable drawable)		/* Pixmap or window to draw into. */
5023 {
5024     int x, y1a, y2;
5025 
5026     x = SCREENX(tvPtr, columnPtr->worldX) +
5027 	columnPtr->width + tvPtr->ruleMark - tvPtr->ruleAnchor - 1;
5028 
5029     y1a = tvPtr->titleHeight + tvPtr->insetY;
5030     y2 = Tk_Height(tvPtr->tkwin) - tvPtr->insetY*2;
5031     XDrawLine(tvPtr->display, drawable, columnPtr->ruleGC, x, y1a, x, y2);
5032     tvPtr->flags = TOGGLE(tvPtr->flags, TV_RULE_ACTIVE);
5033 }
5034 
Blt_TreeViewRedrawIcon(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewColumn * columnPtr,TreeViewIcon icon,int imageX,int imageY,int width,int height,Drawable drawable,int drawableX,int drawableY)5035 int Blt_TreeViewRedrawIcon(TreeView *tvPtr, TreeViewEntry *entryPtr,
5036     TreeViewColumn *columnPtr, TreeViewIcon icon, int imageX,
5037     int imageY, int width, int height, Drawable drawable,
5038     int drawableX, int drawableY)
5039 {
5040     icon->count++;
5041     if (icon->count == 1 && tvPtr->imageCmd != NULL
5042         && strlen(Tcl_GetString(tvPtr->imageCmd))) {
5043         Tcl_DString cmdString;
5044         char *string;
5045         int result, rcnt;
5046         Tcl_Interp *interp = tvPtr->interp;
5047 
5048         string = Blt_GetHashKey(&tvPtr->iconTable, icon->hashPtr);
5049         if (string == NULL) string = "";
5050 
5051         icon->refCount++;
5052         if (entryPtr) Tcl_Preserve(entryPtr);
5053         if (columnPtr) Tcl_Preserve(columnPtr);
5054         Blt_TreeViewPercentSubst(tvPtr, entryPtr, columnPtr, Tcl_GetString(tvPtr->imageCmd), string, &cmdString);
5055         result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
5056         rcnt = icon->refCount;
5057         Blt_TreeViewFreeIcon(tvPtr, icon);
5058         if ((tvPtr->flags & TV_DELETED)
5059             || (entryPtr && (entryPtr->flags & ENTRY_DELETED))
5060             || (columnPtr && (columnPtr->flags & COLUMN_DELETED))
5061             || rcnt<=1) {
5062             if (entryPtr) Tcl_Release(entryPtr);
5063             if (columnPtr) Tcl_Release(columnPtr);
5064             return TCL_ERROR;
5065         }
5066         if (columnPtr) Tcl_Release(columnPtr);
5067         if (entryPtr) Tcl_Release(entryPtr);
5068         Blt_TreeViewOptsInit(tvPtr);
5069         Tcl_DStringFree(&cmdString);
5070     }
5071     Tk_RedrawImage(TreeViewIconBits(icon), imageX, imageY, width, height, drawable, drawableX, drawableY);
5072     return TCL_OK;
5073 
5074 }
5075 
5076 
5077 /*
5078  * ---------------------------------------------------------------------------
5079  *
5080  * Blt_TreeViewDrawButton --
5081  *
5082  * 	Draws a button for the given entry. The button is drawn
5083  * 	centered in the region immediately to the left of the origin
5084  * 	of the entry (computed in the layout routines). The height
5085  * 	and width of the button were previously calculated from the
5086  * 	average row height.
5087  *
5088  *		button height = entry height - (2 * some arbitrary padding).
5089  *		button width = button height.
5090  *
5091  *	The button may have a border.  The symbol (either a plus or
5092  *	minus) is slight smaller than the width or height minus the
5093  *	border.
5094  *
5095  *	    x,y origin of entry
5096  *
5097  *              +---+
5098  *              | + | icon label
5099  *              +---+
5100  *             closed
5101  *
5102  *           |----|----| horizontal offset
5103  *
5104  *              +---+
5105  *              | - | icon label
5106  *              +---+
5107  *              open
5108  *
5109  * Results:
5110  *	None.
5111  *
5112  * Side Effects:
5113  *	A button is drawn for the entry.
5114  *      TODO: handle button images > BUTTON_SIZE properly.
5115  *
5116  * ---------------------------------------------------------------------------
5117  */
5118 int
Blt_TreeViewDrawButton(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable,int x,int y)5119 Blt_TreeViewDrawButton(
5120     TreeView *tvPtr,		/* Widget record containing the
5121 				 * attribute information for
5122 				 * buttons. */
5123     TreeViewEntry *entryPtr,	/* Entry. */
5124     Drawable drawable,		/* Pixmap or window to draw into. */
5125     int x,
5126     int y)
5127 {
5128     Tk_3DBorder border;
5129     TreeViewButton *buttonPtr = &tvPtr->button;
5130     TreeViewIcon icon;
5131     TreeViewIcon *icons;
5132     int relief;
5133     int width, height;
5134     int altRow;
5135     int selected;
5136 
5137     if (buttonPtr->reqSize <= 0) {
5138         return TCL_OK;
5139     }
5140     if (entryPtr == tvPtr->activeButtonPtr) {
5141         icons = CHOOSE(buttonPtr->icons,buttonPtr->activeicons);
5142     } else {
5143         icons = buttonPtr->icons;
5144     }
5145     if (icons == NULL) {
5146         if (entryPtr == tvPtr->activeButtonPtr) {
5147             border = CHOOSE(tvPtr->border, buttonPtr->activeBorder);
5148         } else {
5149             border = CHOOSE(tvPtr->border, buttonPtr->border);
5150         }
5151     } else {
5152         altRow = (entryPtr->flags & ENTRY_ALTROW);
5153         selected = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, NULL);
5154         if (entryPtr == tvPtr->activeButtonPtr && buttonPtr->activeBorder) {
5155             border = buttonPtr->activeBorder;
5156         } else if (selected) {
5157             border = SELECT_BORDER(tvPtr);
5158         } else if (buttonPtr->border != NULL) {
5159             border = buttonPtr->border;
5160         } else if (entryPtr->stylePtr && entryPtr->stylePtr->border != NULL) {
5161             border = entryPtr->stylePtr->border;
5162         } else if (entryPtr->border != NULL) {
5163             border = entryPtr->border;
5164         } else if (altRow && tvPtr->altStylePtr && tvPtr->altStylePtr->border) {
5165             /*stylePtr = tvPtr->altStylePtr;
5166             if (tvPtr->treeColumn.stylePtr->priority > stylePtr->priority) {
5167                 stylePtr = tvPtr->treeColumn.stylePtr;
5168             }*/
5169             border = tvPtr->altStylePtr->border;
5170         } else {
5171             border = tvPtr->border;
5172         }
5173     }
5174     if (entryPtr->flags & ENTRY_CLOSED) {
5175 	relief = buttonPtr->closeRelief;
5176     } else {
5177 	relief = buttonPtr->openRelief;
5178     }
5179     if (relief == TK_RELIEF_SOLID) {
5180 	relief = TK_RELIEF_FLAT;
5181     }
5182     Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
5183 	buttonPtr->width, buttonPtr->height, buttonPtr->borderWidth, relief);
5184 
5185     x += buttonPtr->borderWidth;
5186     y += buttonPtr->borderWidth;
5187     width = buttonPtr->width - (2 * buttonPtr->borderWidth);
5188     height = buttonPtr->height - (2 * buttonPtr->borderWidth);
5189 
5190     icon = NULL;
5191     if (icons != NULL) {  /* Open or close button icon? */
5192 	icon = icons[0];
5193 	if (((entryPtr->flags & ENTRY_CLOSED) == 0) &&
5194 	    (icons[1] != NULL)) {
5195 	    icon = icons[1];
5196 	}
5197     }
5198     if (icon != NULL) {	/* Icon or rectangle? */
5199         if (Blt_TreeViewRedrawIcon(tvPtr, entryPtr, NULL, icon, 0, 0, width, height, drawable, x, y) != TCL_OK) { return TCL_ERROR; }
5200     } else {
5201 	int top, bottom, left, right;
5202 	XSegment segments[6];
5203 	int count;
5204 	GC gc;
5205 
5206 	gc = (entryPtr == tvPtr->activeButtonPtr)
5207 	    ? buttonPtr->activeGC : buttonPtr->normalGC;
5208 	if (relief == TK_RELIEF_FLAT) {
5209 	    /* Draw the box outline */
5210 
5211 	    left = x - buttonPtr->borderWidth;
5212 	    top = y - buttonPtr->borderWidth;
5213 	    right = left + buttonPtr->width - 1;
5214 	    bottom = top + buttonPtr->height - 1;
5215 
5216 	    segments[0].x1 = left;
5217 	    segments[0].x2 = right;
5218 	    segments[0].y2 = segments[0].y1 = top;
5219 	    segments[1].x2 = segments[1].x1 = right;
5220 	    segments[1].y1 = top;
5221 	    segments[1].y2 = bottom;
5222 	    segments[2].x2 = segments[2].x1 = left;
5223 	    segments[2].y1 = top;
5224 	    segments[2].y2 = bottom;
5225 #ifdef WIN32
5226 	    segments[2].y2++;
5227 #endif
5228 	    segments[3].x1 = left;
5229 	    segments[3].x2 = right;
5230 	    segments[3].y2 = segments[3].y1 = bottom;
5231 #ifdef WIN32
5232 	    segments[3].x2++;
5233 #endif
5234 	}
5235 	top = y + height / 2;
5236 	left = x + BUTTON_IPAD;
5237 	right = x + width - BUTTON_IPAD;
5238 
5239 	segments[4].y1 = segments[4].y2 = top;
5240 	segments[4].x1 = left;
5241 	segments[4].x2 = right - 1;
5242 #ifdef WIN32
5243 	segments[4].x2++;
5244 #endif
5245 
5246 	count = 5;
5247 	if (entryPtr->flags & ENTRY_CLOSED) { /* Draw the vertical
5248 					       * line for the plus. */
5249 	    top = y + BUTTON_IPAD;
5250 	    bottom = y + height - BUTTON_IPAD;
5251 	    segments[5].y1 = top;
5252 	    segments[5].y2 = bottom - 1;
5253 	    segments[5].x1 = segments[5].x2 = x + width / 2;
5254 #ifdef WIN32
5255 	    segments[5].y2++;
5256 #endif
5257 	    count = 6;
5258 	}
5259 	XDrawSegments(tvPtr->display, drawable, gc, segments, count);
5260     }
5261     return TCL_OK;
5262 }
5263 
5264 
5265 /*
5266  * ---------------------------------------------------------------------------
5267  *
5268  * Blt_TreeViewGetEntryIcon --
5269  *
5270  * 	Selects the correct image for the entry's icon depending upon
5271  *	the current state of the entry: active/inactive normal/selected.
5272  *
5273  *		active - normal
5274  *		active - selected
5275  *		inactive - normal
5276  *		inactive - selected
5277  *
5278  * Results:
5279  *	Returns the image for the icon.
5280  *
5281  * ---------------------------------------------------------------------------
5282  */
5283 TreeViewIcon
Blt_TreeViewGetEntryIcon(TreeView * tvPtr,TreeViewEntry * entryPtr)5284 Blt_TreeViewGetEntryIcon(TreeView *tvPtr, TreeViewEntry *entryPtr)
5285 {
5286     TreeViewIcon *icons;
5287     TreeViewIcon icon;
5288 
5289     int isActive, hasFocus;
5290 
5291     isActive = (entryPtr == tvPtr->activePtr);
5292     hasFocus = (entryPtr == tvPtr->focusPtr);
5293     icons = NULL;
5294     if (tvPtr->flags & TV_HIDE_ICONS) {
5295         return NULL;
5296     }
5297     if (entryPtr->stylePtr && entryPtr->stylePtr->icon && entryPtr->icons == NULL) {
5298         return entryPtr->stylePtr->icon;
5299     }
5300     if (isActive) {
5301         if (tvPtr->activeLeafIcons != NULL && entryPtr->icons == NULL &&
5302         Blt_TreeViewIsLeaf(entryPtr)) {
5303             icons = tvPtr->activeLeafIcons;
5304         } else {
5305 	   icons = CHOOSE(tvPtr->activeIcons, entryPtr->activeIcons);
5306         }
5307     }
5308     if (icons == NULL) {
5309         if (tvPtr->leafIcons != NULL && entryPtr->icons == NULL &&
5310         Blt_TreeViewIsLeaf(entryPtr)) {
5311             icons = tvPtr->leafIcons;
5312         } else {
5313             icons = CHOOSE(tvPtr->icons, entryPtr->icons);
5314         }
5315     }
5316 
5317     icon = NULL;
5318     if (icons != NULL) {	/* Selected or normal icon? */
5319 	icon = icons[0];
5320          if ((/*hasFocus ||*/ (!(entryPtr->flags &ENTRY_CLOSED))) && (icons[1] != NULL)) {
5321 	    icon = icons[1];
5322 	}
5323     }
5324     return icon;
5325 }
5326 
5327 
5328 int
Blt_TreeViewDrawIcon(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable,int x,int y,int clear)5329 Blt_TreeViewDrawIcon(
5330     TreeView *tvPtr,		/* Widget record containing the attribute
5331 				 * information for buttons. */
5332     TreeViewEntry *entryPtr,	/* Entry to display. */
5333     Drawable drawable,		/* Pixmap or window to draw into. */
5334     int x,
5335     int y,
5336     int clear)
5337 {
5338     TreeViewIcon icon;
5339 
5340     icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
5341 
5342     if (icon != NULL) {	/* Icon or default icon bitmap? */
5343 	int entryHeight;
5344 	int level;
5345 	int maxY;
5346 	int top, bottom, left;
5347 	int topInset, botInset;
5348 	int width, height;
5349 	int cend;
5350 
5351 	level = DEPTH(tvPtr, entryPtr->node);
5352 	entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
5353 		tvPtr->button.height);
5354 	height = TreeViewIconHeight(icon);
5355 	width = TreeViewIconWidth(icon);
5356 	if (tvPtr->flatView) {
5357 	    x += (ICONWIDTH(0) - width) / 2;
5358 	} else {
5359 	    x += (ICONWIDTH(level + 1) - width) / 2;
5360 	}
5361 	y += (entryHeight - height + tvPtr->leader) / 2;
5362 	botInset = tvPtr->insetY;
5363 	topInset = tvPtr->titleHeight + tvPtr->insetY;
5364 	maxY = Tk_Height(tvPtr->tkwin) - botInset;
5365 	left = 0;
5366 	top = 0;
5367 	bottom = y + height;
5368 	if (y < topInset) {
5369 	    height += y - topInset;
5370 	    top = -y + topInset;
5371 	    y = topInset;
5372 	} else if (bottom >= maxY) {
5373 	    height = maxY - y;
5374 	}
5375 	if (x<tvPtr->insetX) {
5376 	    int dif=(tvPtr->insetX-x);
5377 	    x = tvPtr->insetX;
5378 	    left += dif;
5379 	    width -= dif;
5380 	}
5381          cend = tvPtr->treeColumn.worldX + tvPtr->treeColumn.width - tvPtr->xOffset - tvPtr->treeColumn.borderWidth + tvPtr->insetX;
5382          if ((x+width)>cend) {
5383              if (x>cend) {
5384                  return (icon != NULL);
5385              }
5386              width -= (x+width-cend);
5387         }
5388         if (clear && 0) {
5389              /* TODO: If using activate, need to clear background
5390                in case last icon had transparency. */
5391            Tk_3DBorder border;
5392            border = Blt_TreeViewGetStyleBorder(tvPtr, tvPtr->treeColumn.stylePtr);
5393            if (border) {
5394                 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
5395                 width, height, 0, TK_RELIEF_FLAT);
5396             }
5397         }
5398         if (Blt_TreeViewRedrawIcon(tvPtr, entryPtr, &tvPtr->treeColumn, icon, left, top, width, height, drawable, x, y) != TCL_OK) {
5399             return -1;
5400         }
5401     }
5402     return (icon != NULL);
5403 }
5404 
5405 static int
DrawLabel(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable,int x,int y)5406 DrawLabel(
5407     TreeView *tvPtr,		/* Widget record. */
5408     TreeViewEntry *entryPtr,	/* Entry attribute information. */
5409     Drawable drawable,		/* Pixmap or window to draw into. */
5410     int x,
5411     int y)
5412 {
5413     char *label;
5414     int entryHeight;
5415     int isFocused;
5416     int width, height;		/* Width and height of label. */
5417     int selected, disabled;
5418     /*int altRow = (tvPtr->flatView && (entryPtr->flags & ENTRY_ALTROW)); */
5419     int altRow = ((entryPtr->flags & ENTRY_ALTROW));
5420     Shadow *shadowPtr;
5421 
5422     disabled = (entryPtr->state == STATE_DISABLED);
5423     entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
5424        tvPtr->button.height);
5425     isFocused = ((entryPtr == tvPtr->focusPtr) &&
5426 		 (tvPtr->flags & TV_FOCUS));
5427     selected = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, NULL);
5428 
5429     /* Includes padding, selection 3-D border, and focus outline. */
5430     width = entryPtr->labelWidth;
5431     height = entryPtr->labelHeight;
5432 
5433     /* Center the label, if necessary, vertically along the entry row. */
5434     if (height < entryHeight) {
5435 	y += (entryHeight - height) / 2;
5436     }
5437     if (Blt_TreeViewGetEntryIcon(tvPtr, entryPtr) != NULL) {
5438         y += tvPtr->leader/2;
5439     }
5440     if (isFocused) {		/* Focus outline */
5441 	if (selected) {
5442 	    XColor *color;
5443 
5444 	    color = SELECT_FG(tvPtr);
5445 	    XSetForeground(tvPtr->display, tvPtr->focusGC, color->pixel);
5446 	}
5447 	XDrawRectangle(tvPtr->display, drawable, tvPtr->focusGC, x, y,
5448 		       width - 1, height - 1);
5449 	if (selected) {
5450 	    XSetForeground(tvPtr->display, tvPtr->focusGC,
5451 		tvPtr->focusColor->pixel);
5452 	}
5453     }
5454     x += FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth;
5455     y += tvPtr->focusHeight + LABEL_PADY + tvPtr->selBorderWidth;
5456 
5457     label = GETLABEL(entryPtr);
5458     if (label[0] != '\0') {
5459         GC gc;
5460 	TreeViewStyle *stylePtr = NULL;
5461         TreeViewColumn *columnPtr = &tvPtr->treeColumn;
5462 	TextStyle ts;
5463 	Tk_Font font;
5464 	XColor *normalColor, *activeColor;
5465 #if 1
5466         TreeViewStyle sRec;
5467         int flags = 0;
5468         if (altRow) {
5469             stylePtr = tvPtr->altStylePtr;
5470             /* flags |= STYLEFLAG_ALTSTYLE; */
5471         }
5472         Blt_GetPriorityStyle(&sRec, tvPtr, columnPtr, entryPtr, NULL, stylePtr, flags);
5473         font = sRec.font;
5474         normalColor = sRec.fgColor;
5475         gc = sRec.gc;
5476         shadowPtr = &sRec.shadow;
5477         if (disabled) {
5478             normalColor = tvPtr->disabledColor;
5479             activeColor = tvPtr->disabledColor;
5480         } else {
5481             if (normalColor == NULL) {
5482                 normalColor = Blt_TreeViewGetStyleFg(tvPtr, columnPtr, &sRec);
5483             }
5484             activeColor = (selected) ? SELECT_FG(tvPtr) : normalColor;
5485         }
5486 #else
5487 	TreeViewStyle *nStylePtr = NULL;
5488         if (altRow && tvPtr->altStylePtr) {
5489             stylePtr = tvPtr->altStylePtr;
5490             if (tvPtr->treeColumn.stylePtr->priority > stylePtr->priority) {
5491                 stylePtr = tvPtr->treeColumn.stylePtr;
5492             }
5493         } else if (entryPtr->stylePtr) {
5494             stylePtr = entryPtr->stylePtr;
5495         } else {
5496             stylePtr = tvPtr->treeColumn.stylePtr;
5497         }
5498         nStylePtr = stylePtr;
5499         if (entryPtr->stylePtr && entryPtr->stylePtr->fgColor) {
5500             nStylePtr = entryPtr->stylePtr;
5501 	}
5502 	font = entryPtr->font;
5503 	if (disabled) {
5504              normalColor = tvPtr->disabledColor;
5505              activeColor = tvPtr->disabledColor;
5506         } else {
5507              normalColor = entryPtr->color;
5508              if (normalColor == NULL) {
5509                  normalColor = Blt_TreeViewGetStyleFg(tvPtr, columnPtr, nStylePtr);
5510              }
5511              activeColor = (selected) ? SELECT_FG(tvPtr) : normalColor;
5512          }
5513 	gc = entryPtr->gc;
5514         if (stylePtr && stylePtr->shadow.color) {
5515             shadowPtr = &stylePtr->shadow;
5516         } else {
5517             shadowPtr = ((entryPtr->shadow.color || tvPtr->treeColumn.stylePtr==NULL)? &entryPtr->shadow : &tvPtr->treeColumn.stylePtr->shadow);
5518         }
5519 #endif
5520 	if (font == NULL) {
5521              font = Blt_TreeViewGetStyleFont(tvPtr, &tvPtr->treeColumn, stylePtr);
5522 	}
5523 	if (gc == NULL) {
5524 	    gc = Blt_TreeViewGetStyleGC(tvPtr, stylePtr);
5525 	}
5526         Blt_SetDrawTextStyle(&ts, font, gc, normalColor, activeColor,
5527 		shadowPtr->color, 0.0, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0,
5528 		shadowPtr->offset);
5529 	ts.state = (selected || (entryPtr->gc == NULL)) ? STATE_ACTIVE : 0;
5530 	ts.underline = entryPtr->underline;
5531 	Blt_DrawTextLayout(tvPtr->tkwin, drawable, entryPtr->textPtr,
5532 		&ts, x, y);
5533         if (entryPtr->subLabel != NULL && (tvPtr->subStylePtr == NULL || tvPtr->subStylePtr->hidden == 0)) {
5534             if (tvPtr->subStylePtr) {
5535                 gc = Blt_TreeViewGetStyleGC(tvPtr, tvPtr->subStylePtr);
5536                  if (tvPtr->subStylePtr->font) {
5537                     font = tvPtr->subStylePtr->font;
5538                 }
5539                 if (!disabled) {
5540                     if (!selected) {
5541                         normalColor = Blt_TreeViewGetStyleFg(tvPtr, columnPtr, tvPtr->subStylePtr);
5542                     } else {
5543                         normalColor = SELECT_FG(tvPtr);
5544                     }
5545                 }
5546                 activeColor = normalColor;
5547                 if (tvPtr->subStylePtr->shadow.color) {
5548                     shadowPtr = &tvPtr->subStylePtr->shadow;
5549                 }
5550             }
5551             Blt_SetDrawTextStyle(&ts, font, gc, normalColor, activeColor,
5552 		shadowPtr->color, 0.0, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0,
5553 		shadowPtr->offset);
5554 	    ts.state = (selected || (entryPtr->gc == NULL)) ? STATE_ACTIVE : 0;
5555             ts.underline = entryPtr->underline;
5556             Blt_DrawTextLayout(tvPtr->tkwin, drawable, entryPtr->subTextPtr,
5557                 &ts, x + entryPtr->textPtr->width, y);
5558         }
5559     }
5560     return entryHeight;
5561 }
5562 
5563 /*
5564  * Draw the rule underline for an entry.
5565  */
DrawEntryRule(tvPtr,entryPtr,columnPtr,drawable,x,y)5566 void DrawEntryRule( tvPtr, entryPtr, columnPtr, drawable, x, y)
5567     TreeView *tvPtr;
5568     TreeViewEntry *entryPtr;
5569     TreeViewColumn *columnPtr;
5570     Pixmap drawable;
5571     int x;
5572     int y;
5573 {
5574     int x2 =0, y2, ri, rw = tvPtr->ruleWidth;
5575     if (columnPtr == NULL) {
5576         columnPtr = &tvPtr->treeColumn;
5577         x = columnPtr->worldX-tvPtr->xOffset;
5578         x2+=2;
5579     }
5580     x2 += x + columnPtr->width + 2;
5581     y2 = y + entryPtr->height-rw;
5582     if (tvPtr->ruleWidth<0) {
5583         Blt_Draw3DRectangle(tvPtr->tkwin, drawable, tvPtr->border, x, y+1, x2, y+entryPtr->height-1,columnPtr->borderWidth, columnPtr->relief);
5584     } else {
5585         for (ri=0; ri<rw; ri++, y2++) {
5586             XDrawLine(tvPtr->display, drawable, tvPtr->solidGC, x, y2, x2, y2);
5587         }
5588     }
5589 }
5590 
5591 /*
5592  * ---------------------------------------------------------------------------
5593  *
5594  * DrawFlatEntry --
5595  *
5596  * 	Draws a button for the given entry.  Note that buttons should only
5597  *	be drawn if the entry has sub-entries to be opened or closed.  It's
5598  *	the responsibility of the calling routine to ensure this.
5599  *
5600  *	The button is drawn centered in the region immediately to the left
5601  *	of the origin of the entry (computed in the layout routines). The
5602  *	height and width of the button were previously calculated from the
5603  *	average row height.
5604  *
5605  *		button height = entry height - (2 * some arbitrary padding).
5606  *		button width = button height.
5607  *
5608  *	The button has a border.  The symbol (either a plus or minus) is
5609  *	slight smaller than the width or height minus the border.
5610  *
5611  *	    x,y origin of entry
5612  *
5613  *              +---+
5614  *              | + | icon label
5615  *              +---+
5616  *             closed
5617  *
5618  *           |----|----| horizontal offset
5619  *
5620  *              +---+
5621  *              | - | icon label
5622  *              +---+
5623  *              open
5624  *
5625  * Results:
5626  *	None.
5627  *
5628  * Side Effects:
5629  *	A button is drawn for the entry.
5630  *
5631  * ---------------------------------------------------------------------------
5632  */
5633 static void
DrawFlatEntry(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable)5634 DrawFlatEntry(
5635     TreeView *tvPtr,		/* Widget record containing the attribute
5636 				 * information for buttons. */
5637     TreeViewEntry *entryPtr,	/* Entry to be drawn. */
5638     Drawable drawable)		/* Pixmap or window to draw into. */
5639 {
5640     int level;
5641     int x, y, di ;
5642 
5643     entryPtr->flags &= ~ENTRY_REDRAW;
5644 
5645     x = SCREENX(tvPtr, entryPtr->worldX);
5646     y = SCREENY(tvPtr, entryPtr->worldY);
5647     di = Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y, 0);
5648     if (di == -1) return;
5649     if (!di) {
5650 	x -= (DEF_ICON_WIDTH * 2) / 3;
5651     }
5652     if (tvPtr->flags & TV_DELETED) return;
5653     level = 0;
5654     x += ICONWIDTH(level);
5655     /* Entry label. */
5656     DrawLabel(tvPtr, entryPtr, drawable, x, y);
5657     if (tvPtr->ruleWidth) {
5658         DrawEntryRule( tvPtr, entryPtr, NULL, drawable, x, y);
5659     }
5660 }
5661 
5662 /*
5663  * ---------------------------------------------------------------------------
5664  *
5665  * DrawTreeEntry --
5666  *
5667  * 	Draws a button for the given entry.  Note that buttons should only
5668  *	be drawn if the entry has sub-entries to be opened or closed.  It's
5669  *	the responsibility of the calling routine to ensure this.
5670  *
5671  *	The button is drawn centered in the region immediately to the left
5672  *	of the origin of the entry (computed in the layout routines). The
5673  *	height and width of the button were previously calculated from the
5674  *	average row height.
5675  *
5676  *		button height = entry height - (2 * some arbitrary padding).
5677  *		button width = button height.
5678  *
5679  *	The button has a border.  The symbol (either a plus or minus) is
5680  *	slight smaller than the width or height minus the border.
5681  *
5682  *	    x,y origin of entry
5683  *
5684  *              +---+
5685  *              | + | icon label
5686  *              +---+
5687  *             closed
5688  *
5689  *           |----|----| horizontal offset
5690  *
5691  *              +---+
5692  *              | - | icon label
5693  *              +---+
5694  *              open
5695  *
5696  * Results:
5697  *	None.
5698  *
5699  * Side Effects:
5700  *	A button is drawn for the entry.
5701  *
5702  * ---------------------------------------------------------------------------
5703  */
5704 static void
DrawTreeEntry(TreeView * tvPtr,TreeViewEntry * entryPtr,Drawable drawable)5705 DrawTreeEntry(
5706     TreeView *tvPtr,		/* Widget record. */
5707     TreeViewEntry *entryPtr,	/* Entry to be drawn. */
5708     Drawable drawable)		/* Pixmap or window to draw into. */
5709 {
5710     TreeViewButton *buttonPtr = &tvPtr->button;
5711     int buttonY;
5712     int level;
5713     int width, height;
5714     int x, y, di, hr;
5715     int x1, y1a, x2, y2;
5716 
5717     entryPtr->flags &= ~ENTRY_REDRAW;
5718 
5719     x = SCREENX(tvPtr, entryPtr->worldX);
5720     y = SCREENY(tvPtr, entryPtr->worldY);
5721 
5722     level = DEPTH(tvPtr, entryPtr->node);
5723     width = ICONWIDTH(level);
5724     height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
5725 	buttonPtr->height);
5726 
5727     entryPtr->buttonX = (width - buttonPtr->width) / 2;
5728     if (entryPtr->buttonX < 0) entryPtr->buttonX = 0;
5729     entryPtr->buttonY = (height - buttonPtr->height) / 2;
5730     if (entryPtr->buttonY < 0) entryPtr->buttonY = 0;
5731 
5732     buttonY = y + entryPtr->buttonY;
5733 
5734     x1 = x + (width / 2);
5735     y1a = y2 = buttonY + (buttonPtr->height / 2);
5736     x2 = x1 + (ICONWIDTH(level) + ICONWIDTH(level + 1)) / 2;
5737 
5738     if ((Blt_TreeNodeParent(entryPtr->node) != NULL) &&
5739 	(tvPtr->lineWidth > 0)) {
5740 	/*
5741 	 * For every node except root, draw a horizontal line from
5742 	 * the vertical bar to the middle of the icon.
5743 	 */
5744 	XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x1-tvPtr->levelPad, y1a, x2, y2);
5745      }
5746     if (((entryPtr->flags & ENTRY_CLOSED) == 0) && (tvPtr->lineWidth > 0) &&
5747         Blt_TreeViewIsLeaf(entryPtr)!=TRUE) {
5748 	/*
5749 	 * Entry is open, draw vertical line.
5750 	 */
5751 	y2 = y1a + entryPtr->vertLineLength;
5752 	if (y1a < tvPtr->insetY) {
5753 	    y1a = tvPtr->insetY;
5754 	}
5755 	if (y2 > Tk_Height(tvPtr->tkwin)) {
5756 	    y2 = Tk_Height(tvPtr->tkwin); /* Clip line at window border. */
5757 	}
5758 	XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x2, y1a, x2, y2);
5759      }
5760     if ((entryPtr->flags & ENTRY_HAS_BUTTON) && (entryPtr != tvPtr->rootPtr) &&
5761         (tvPtr->buttonFlags & BUTTON_AUTO)) {
5762 
5763 	/*
5764 	 * Except for the root, draw a button for every entry that
5765 	 * needs one.  The displayed button can be either an icon (Tk
5766 	 * image) or a line drawing (rectangle with plus or minus
5767 	 * sign).
5768 	 */
5769 	if (Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, x + entryPtr->buttonX,
5770 		y + entryPtr->buttonY) != TCL_OK) return;
5771         if (tvPtr->flags & TV_DELETED) return;
5772     }
5773     if (tvPtr->button.icons && tvPtr->button.width > (24)) {
5774         /* TODO: hack. */
5775         x += tvPtr->button.width;
5776     }
5777     hr = ((tvPtr->flags & TV_HIDE_ROOT) ? 1 : 0);
5778     if (tvPtr->lineWidth>0 || tvPtr->button.reqSize>0 || level>hr) {
5779         x += ICONWIDTH(level);
5780     } else {
5781         x = BUTTON_PAD;
5782     }
5783     di = Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y, 0);
5784     if (di == -1) return;
5785     if (!di) {
5786 	x -= (DEF_ICON_WIDTH * 2) / 3;
5787     }
5788     if (tvPtr->flags & TV_DELETED) return;
5789     x += ICONWIDTH(level + 1) + 2;
5790     /* Entry label. */
5791     DrawLabel(tvPtr, entryPtr, drawable, x, y);
5792     if (tvPtr->ruleWidth) {
5793         DrawEntryRule( tvPtr, entryPtr, NULL, drawable, x, y);
5794     }
5795 }
5796 
5797 /*
5798  * ---------------------------------------------------------------------------
5799  *
5800  * Blt_TreeViewDrawValue --
5801  *
5802  * 	Draws a column value for the given entry.
5803  *
5804  * Results:
5805  *	None.
5806  *
5807  * Side Effects:
5808  *	A button is drawn for the entry.
5809  *
5810  * ---------------------------------------------------------------------------
5811  */
5812 void
Blt_TreeViewDrawValue(TreeView * tvPtr,TreeViewEntry * entryPtr,TreeViewValue * valuePtr,Drawable drawable,int x,int y,int altRow,int ishid)5813 Blt_TreeViewDrawValue(
5814     TreeView *tvPtr,		/* Widget record. */
5815     TreeViewEntry *entryPtr,	/* Node of entry to be drawn. */
5816     TreeViewValue *valuePtr,
5817     Drawable drawable,		/* Pixmap or window to draw into. */
5818     int x,
5819     int y,
5820     int altRow,
5821     int ishid)
5822 {
5823     TreeViewStyle *stylePtr, *csPtr;
5824     TreeViewColumn *columnPtr;
5825     TreeViewIcon icon = NULL;
5826 
5827     columnPtr = valuePtr->columnPtr;
5828     csPtr = CHOOSE(tvPtr->stylePtr, columnPtr->stylePtr);
5829 
5830     if (altRow && tvPtr->altStylePtr && valuePtr->stylePtr == NULL &&
5831         csPtr == tvPtr->stylePtr) {
5832         stylePtr = tvPtr->altStylePtr;
5833         if (csPtr->priority > stylePtr->priority) {
5834             stylePtr = columnPtr->stylePtr;
5835         }
5836     } else if (entryPtr->stylePtr && valuePtr->stylePtr == NULL) {
5837         stylePtr = entryPtr->stylePtr;
5838     } else {
5839         stylePtr = CHOOSE(csPtr, valuePtr->stylePtr);
5840     }
5841     if (stylePtr == NULL) {
5842         /* fprintf(stderr, "FATAL: EMPTY STYLE VALUE\n"); */
5843         return;
5844     }
5845     if (stylePtr != csPtr && columnPtr->stylePtr) {
5846         /* If style !Window and type != col style type, reset to column style. */
5847         if (0 && stylePtr->classPtr->className[0] != 'W' &&
5848             strcmp(stylePtr->classPtr->className,
5849             columnPtr->stylePtr->classPtr->className)) {
5850             stylePtr = columnPtr->stylePtr;
5851         }
5852     }
5853     if (tvPtr->hideStyleIcons) {
5854         icon = NULL;
5855     } else if (ishid) {
5856         icon = NULL;
5857     } else if (valuePtr->stylePtr && valuePtr->stylePtr->icon) {
5858         icon = valuePtr->stylePtr->icon;
5859     } else if (columnPtr->stylePtr && columnPtr->stylePtr->icon) {
5860         icon = columnPtr->stylePtr->icon;
5861     }
5862 
5863     (*stylePtr->classPtr->drawProc)(tvPtr, drawable, entryPtr, valuePtr,
5864 		stylePtr, icon, x, y + tvPtr->leader/2);
5865 }
5866 
5867 static void
DrawTitle(TreeView * tvPtr,TreeViewColumn * columnPtr,Drawable drawable,int x)5868 DrawTitle(
5869     TreeView *tvPtr,
5870     TreeViewColumn *columnPtr,
5871     Drawable drawable,
5872     int x)
5873 {
5874     GC gc;
5875     Tk_3DBorder border;
5876     XColor *fgColor;
5877     int columnWidth;
5878     int width;
5879     int x0, cx, xOffset;
5880     TreeViewStyle *sPtr;
5881     TreeViewIcon icon;
5882     int mw, mh;
5883     mw = Tk_Width(tvPtr->tkwin) - tvPtr->padX;
5884     mh = Tk_Height(tvPtr->tkwin) - tvPtr->padY;
5885 
5886     if (tvPtr->titleHeight < 1) {
5887 	return;
5888     }
5889     sPtr = columnPtr->titleStylePtr;
5890     icon = (sPtr && sPtr->icon? sPtr->icon : columnPtr->titleIcon);
5891     columnWidth = columnPtr->width;
5892     cx = x;
5893     if (columnPtr->position == Blt_ChainGetLength(tvPtr->colChainPtr)) {
5894 	/* If there's any room left over, let the last column take it. */
5895 	columnWidth = Tk_Width(tvPtr->tkwin) - x - tvPtr->padX;
5896     } else if (columnPtr->position == 1) {
5897 	columnWidth += x;
5898 	cx = tvPtr->padX;
5899     }
5900     if ((x + columnWidth) > mw) {
5901         columnWidth = (mw - x);
5902     }
5903     if (columnWidth<2) {
5904         columnWidth = 2;
5905     }
5906     x0 = x + columnPtr->borderWidth;
5907 
5908     if (columnPtr == tvPtr->activeTitleColumnPtr) {
5909         if (sPtr && sPtr->activeBorder) {
5910             border = sPtr->activeBorder;
5911             gc = sPtr->activeGC;
5912         } else {
5913             border = columnPtr->activeTitleBorder;
5914             gc = columnPtr->activeTitleGC;
5915         }
5916         fgColor = (sPtr && sPtr->activeFgColor)?sPtr->activeFgColor:columnPtr->activeTitleFgColor;
5917      } else {
5918         if (sPtr && sPtr->border) {
5919             border = sPtr->border;
5920             gc = sPtr->gc;
5921         } else {
5922             border = columnPtr->titleBorder;
5923             gc = columnPtr->titleGC;
5924         }
5925 	fgColor = (sPtr && sPtr->fgColor)?sPtr->fgColor:columnPtr->titleFgColor;
5926     }
5927     Blt_TreeViewFill3DTile(tvPtr, drawable, border, cx + 1,
5928 	tvPtr->insetY + 1, columnWidth - 2, tvPtr->titleHeight - 2, 0,
5929          TK_RELIEF_FLAT, sPtr ? sPtr->tile : NULL, 0, 1);
5930     if (Blt_HasTile(tvPtr->tile) && !columnPtr->hasttlbg) {
5931         if (tvPtr->scrollTile) {
5932             Blt_SetTSOrigin(tvPtr->tkwin, tvPtr->tile, -tvPtr->xOffset,
5933                 -tvPtr->yOffset);
5934         } else {
5935             Blt_SetTileOrigin(tvPtr->tkwin, tvPtr->tile, 0, 0);
5936         }
5937         Blt_TileRectangle(tvPtr->tkwin, drawable, tvPtr->tile, cx + 1,
5938 	   tvPtr->insetY + 1, columnWidth - 2, tvPtr->titleHeight - 2);
5939     }
5940     width = columnPtr->width;
5941     xOffset = x0 + columnPtr->pad.side1 + 1;
5942 
5943 #if 0
5944     if (width > columnPtr->titleWidth) {
5945 	x += (width - columnPtr->titleWidth) / 2;
5946     }
5947 #endif
5948     if (width > columnPtr->titleWidth) {
5949         switch(columnPtr->titleJustify) {
5950             case TK_JUSTIFY_RIGHT:
5951             x += (width - columnPtr->titleWidth);
5952             break;
5953             case TK_JUSTIFY_CENTER:
5954             x += (width - columnPtr->titleWidth) / 2;
5955             break;
5956             case TK_JUSTIFY_LEFT:
5957             x += columnPtr->pad.side1 + 1;
5958             break;
5959         }
5960     }
5961     if (columnPtr == tvPtr->sortColumnPtr || columnPtr->drawArrow >= 0) {
5962 	/* Make sure there's room for the sorting-direction triangle. */
5963 	if ((x - xOffset) <= (STD_ARROW_WIDTH + 4)) {
5964 	    x = xOffset + STD_ARROW_WIDTH + 4;
5965 	}
5966     }
5967 
5968     if (icon != NULL) {
5969 	int iconX, iconY, iconWidth, iconHeight;
5970 
5971 	iconHeight = TreeViewIconHeight(icon);
5972 	iconWidth = TreeViewIconWidth(icon);
5973 	iconX = x;
5974 	if (columnPtr->titleTextPtr != NULL) {
5975 	    iconX += 2;
5976 	}
5977 	iconY = tvPtr->insetY + (tvPtr->titleHeight - iconHeight) / 2;
5978         columnPtr->iX = iconX;
5979         columnPtr->iY = iconY;
5980         columnPtr->iW = iconWidth;
5981         columnPtr->iH = iconHeight;
5982         if (Blt_TreeViewRedrawIcon(tvPtr, NULL, columnPtr, icon, 0, 0,
5983 	   iconWidth, iconHeight, drawable, iconX, iconY) != TCL_OK) return;
5984 	x += iconWidth + 6;
5985     } else {
5986         columnPtr->iW = 0;
5987     }
5988     if (columnPtr->titleTextPtr != NULL ) {
5989 	TextStyle ts;
5990 	Tk_Font font;
5991 	Shadow shadow;
5992 
5993         font = columnPtr->titleFont?columnPtr->titleFont:tvPtr->titleFont;
5994         if (sPtr && sPtr->font) {
5995             /** Ignore for now as size is taken from titlefont. */
5996             /* font = sPtr->font; */
5997         }
5998         if (sPtr && sPtr->shadow.color) {
5999             shadow = sPtr->shadow;
6000         } else {
6001             shadow = columnPtr->titleShadow;
6002         }
6003         Blt_SetDrawTextStyle(&ts,
6004             font, gc,
6005             fgColor, SELECT_FG(tvPtr), shadow.color, 0.0,
6006             TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0, shadow.offset);
6007         columnPtr->tX = x;
6008         columnPtr->tY = tvPtr->insetY + 1;
6009         columnPtr->tW = columnPtr->titleTextPtr->width;
6010         columnPtr->tH = columnPtr->titleTextPtr->height;
6011         ts.underline = columnPtr->underline;
6012 	Blt_DrawTextLayout(tvPtr->tkwin, drawable, columnPtr->titleTextPtr, &ts,
6013 		x, tvPtr->insetY + 2 + tvPtr->titlePad);
6014     } else {
6015         columnPtr->tW = 0;
6016     }
6017     if ((columnPtr == tvPtr->sortColumnPtr) && (tvPtr->flatView)) {
6018 	Blt_DrawArrow(tvPtr->display, drawable, gc,
6019 		xOffset + ARROW_OFFSET,
6020 		tvPtr->insetY + tvPtr->titleHeight / 2, STD_ARROW_HEIGHT,
6021 		(tvPtr->sortDecreasing) ? ARROW_UP : ARROW_DOWN);
6022     } else if (columnPtr->drawArrow >= 0) {
6023         Blt_DrawArrow(tvPtr->display, drawable, gc,
6024             xOffset + ARROW_OFFSET,
6025             tvPtr->insetY + tvPtr->titleHeight / 2, STD_ARROW_HEIGHT,
6026             columnPtr->drawArrow);
6027     }
6028     Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, cx, tvPtr->insetY,
6029 	columnWidth, tvPtr->titleHeight, columnPtr->titleBorderWidth,
6030 	columnPtr->titleRelief);
6031 }
6032 
6033 void
Blt_TreeViewDrawHeadings(tvPtr,drawable)6034 Blt_TreeViewDrawHeadings(tvPtr, drawable)
6035     TreeView *tvPtr;
6036     Drawable drawable;
6037 {
6038     Blt_ChainLink *linkPtr;
6039     TreeViewColumn *columnPtr;
6040     int x;
6041 
6042     for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
6043 	 linkPtr = Blt_ChainNextLink(linkPtr)) {
6044 	columnPtr = Blt_ChainGetValue(linkPtr);
6045 	if (columnPtr->hidden) {
6046 	    continue;
6047 	}
6048 	x = SCREENX(tvPtr, columnPtr->worldX);
6049 	if ((x + columnPtr->width) < 0) {
6050 	    continue;	/* Don't draw columns before the left edge. */
6051 	}
6052 	if (x > Tk_Width(tvPtr->tkwin)) {
6053 	    break;		/* Discontinue when a column starts beyond
6054 				 * the right edge. */
6055 	}
6056 	DrawTitle(tvPtr, columnPtr, drawable, x);
6057     }
6058 }
6059 
6060 static Tk_3DBorder
GetEntryBorder(TreeView * tvPtr,TreeViewEntry * entryPtr,int n)6061 GetEntryBorder(TreeView *tvPtr, TreeViewEntry *entryPtr, int n)
6062 {
6063     if ((entryPtr->flags & ENTRY_ALTROW) && (tvPtr->altStylePtr)) {
6064         if (tvPtr->altStylePtr->tile) {
6065             return tvPtr->border;
6066         }
6067         if (tvPtr->treeColumn.stylePtr->border == NULL ||
6068         tvPtr->altStylePtr->priority >= (tvPtr->treeColumn.stylePtr->priority-1)) {
6069             return tvPtr->altStylePtr->border;
6070         }
6071     }
6072 
6073     if (entryPtr->border ) {
6074         return entryPtr->border;
6075     }
6076     if (entryPtr->stylePtr && entryPtr->stylePtr->border) {
6077         return entryPtr->stylePtr->border;
6078     }
6079     if (tvPtr->treeColumn.stylePtr && tvPtr->treeColumn.stylePtr->border) {
6080         return tvPtr->treeColumn.stylePtr->border;
6081     }
6082     return NULL;
6083 
6084 }
6085 
6086 
6087 static void
DrawTreeView(tvPtr,drawable,x)6088 DrawTreeView(tvPtr, drawable, x)
6089     TreeView *tvPtr;
6090     Drawable drawable;
6091     int x;
6092 {
6093     register TreeViewEntry **p;
6094     Tk_3DBorder selBorder, altBorder;
6095     TreeViewStyle *sPtr = tvPtr->treeColumn.stylePtr;
6096     TreeViewStyle *ePtr, *aPtr;
6097     int n = 0, isAlt;
6098 
6099     /*
6100      * Draw the backgrounds of selected entries first.  The vertical
6101      * lines connecting child entries will be draw on top.
6102      */
6103     aPtr = tvPtr->altStylePtr;
6104     selBorder = SELECT_BORDER(tvPtr);
6105     if (tvPtr->altStylePtr) {
6106         altBorder = Blt_TreeViewGetStyleBorder(tvPtr, tvPtr->altStylePtr);
6107     }
6108     for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++, n++) {
6109         int y;
6110         isAlt = (((*p)->flags & ENTRY_ALTROW) && (tvPtr->altStylePtr));
6111         ePtr = (*p)->stylePtr;
6112 
6113         y = SCREENY(tvPtr, (*p)->worldY);
6114         if (Blt_TreeViewEntryIsSelected(tvPtr, *p, NULL)) {
6115 
6116             Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, selBorder, x, y,
6117 		tvPtr->treeColumn.width, (*p)->height,
6118 		tvPtr->selBorderWidth, tvPtr->selRelief,
6119 		tvPtr->selectTile, 1, 0 );
6120 	} else if ( (altBorder = GetEntryBorder(tvPtr, *p, n)) ) {
6121 
6122              Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, altBorder, x, y,
6123                   tvPtr->treeColumn.width, (*p)->height,
6124                   0, TK_RELIEF_FLAT, isAlt?(aPtr?aPtr->tile:NULL):((ePtr && ePtr->tile)?ePtr->tile:(sPtr?sPtr->tile:NULL)), 0, 0);
6125 	} else if (ePtr && ePtr->tile) {
6126              Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, selBorder, x, y,
6127                  tvPtr->treeColumn.width, (*p)->height,
6128                  0, TK_RELIEF_FLAT, ePtr->tile, 0, 0);
6129          }
6130     }
6131     if ((tvPtr->lineWidth > 0) && (tvPtr->nVisible > 0)) {
6132 	/* Draw all the vertical lines from topmost node. */
6133 	DrawVerticals(tvPtr, tvPtr->visibleArr[0], drawable);
6134     }
6135 
6136     for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++) {
6137         DrawTreeEntry(tvPtr, *p, drawable);
6138         if (tvPtr->flags & TV_DELETED) break;
6139     }
6140 }
6141 
6142 static void
DrawFlatView(TreeView * tvPtr,Drawable drawable,int x)6143 DrawFlatView(
6144     TreeView *tvPtr,
6145     Drawable drawable,
6146     int x)
6147 {
6148     register TreeViewEntry **p;
6149     Tk_3DBorder selBorder, altBorder;
6150     TreeViewStyle *sPtr = tvPtr->treeColumn.stylePtr;
6151     TreeViewStyle *ePtr, *aPtr;
6152     int n = 0, y, isAlt;
6153     /*
6154      * Draw the backgrounds of selected entries first.  The vertical
6155      * lines connecting child entries will be draw on top.
6156      */
6157     aPtr = tvPtr->altStylePtr;
6158     selBorder = SELECT_BORDER(tvPtr);
6159     if (tvPtr->altStylePtr) {
6160         altBorder = Blt_TreeViewGetStyleBorder(tvPtr, aPtr);
6161     }
6162 
6163     for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++, n++) {
6164         isAlt = (((*p)->flags & ENTRY_ALTROW) && (tvPtr->altStylePtr));
6165         y = SCREENY(tvPtr, (*p)->worldY);
6166         ePtr = (*p)->stylePtr;
6167         if (Blt_TreeViewEntryIsSelected(tvPtr, *p, NULL)) {
6168 
6169             Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, selBorder, x, y,
6170 		tvPtr->treeColumn.width, (*p)->height,
6171 		tvPtr->selBorderWidth, tvPtr->selRelief,
6172 		tvPtr->selectTile, 1, 0 );
6173              /*Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, x, y,
6174              tvPtr->treeColumn.width, (*p)->height - tvPtr->ruleWidth,
6175 		tvPtr->selBorderWidth, tvPtr->selRelief);*/
6176 	} else if ( (altBorder = GetEntryBorder(tvPtr, *p, n)) ) {
6177              Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, altBorder, x, y,
6178                   tvPtr->treeColumn.width, (*p)->height,
6179                   0, TK_RELIEF_FLAT, isAlt?(aPtr?aPtr->tile:NULL):((ePtr && ePtr->tile)?ePtr->tile:(sPtr?sPtr->tile:NULL)), 0, 0);
6180               } else if (ePtr && ePtr->tile) {
6181              Blt_Fill3DRectangleTile(tvPtr->tkwin, drawable, selBorder, x, y,
6182                  tvPtr->treeColumn.width, (*p)->height,
6183                  0, TK_RELIEF_FLAT, ePtr->tile, 0, 0);
6184         }
6185     }
6186     for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++) {
6187 	DrawFlatEntry(tvPtr, *p, drawable);
6188         if (tvPtr->flags & TV_DELETED) break;
6189     }
6190 }
6191 
6192 void
Blt_TreeViewDrawOuterBorders(tvPtr,drawable)6193 Blt_TreeViewDrawOuterBorders(tvPtr, drawable)
6194     TreeView *tvPtr;
6195     Drawable drawable;
6196 {
6197     /* Draw 3D border just inside of the focus highlight ring. */
6198     if ((tvPtr->borderWidth > 0) && (tvPtr->relief != TK_RELIEF_FLAT)) {
6199 	Blt_Draw3DRectangle(tvPtr->tkwin, drawable, tvPtr->border,
6200 	    tvPtr->highlightWidth, tvPtr->highlightWidth,
6201 	    Tk_Width(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
6202 	    Tk_Height(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
6203 	    tvPtr->borderWidth, tvPtr->relief);
6204     }
6205     /* Draw focus highlight ring. */
6206     if (tvPtr->highlightWidth > 0) {
6207 	XColor *color;
6208 	GC gc;
6209 
6210 	color = (tvPtr->flags & TV_FOCUS)
6211 	    ? tvPtr->highlightColor : tvPtr->highlightBgColor;
6212 	gc = Tk_GCForColor(color, drawable);
6213 	Tk_DrawFocusHighlight(tvPtr->tkwin, gc, tvPtr->highlightWidth,
6214 	    drawable);
6215     }
6216     tvPtr->flags &= ~TV_BORDERS;
6217 }
6218 
Blt_TreeViewFill3DTile(TreeView * tvPtr,Drawable drawable,Tk_3DBorder border,int x,int y,int width,int height,int borderWidth,int relief,Blt_Tile tile,int scrollTile,int flags)6219 extern void Blt_TreeViewFill3DTile (TreeView *tvPtr,
6220     Drawable drawable, Tk_3DBorder border, int x, int y, int width, int height,
6221     int borderWidth, int relief, Blt_Tile tile, int scrollTile, int flags) {
6222 
6223     if (tile != NULL) {
6224         if (flags) {
6225             Blt_SetTSOrigin(tvPtr->tkwin, tile, 0, 0);
6226         } else if (scrollTile) {
6227             Blt_SetTSOrigin(tvPtr->tkwin, tile, -tvPtr->xOffset,
6228                 -tvPtr->yOffset);
6229         } else {
6230             Blt_SetTileOrigin(tvPtr->tkwin, tile, 0, 0);
6231         }
6232         Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
6233             width, height, borderWidth, relief);
6234         Blt_TileRectangle(tvPtr->tkwin, drawable, tile, x, y, width, height);
6235         Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, y,
6236             width, height, borderWidth, relief);
6237     } else {
6238         Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
6239             width, height, borderWidth, relief);
6240         }
6241 }
6242 
6243 /*
6244  * ----------------------------------------------------------------------
6245  *
6246  * DisplayTreeView --
6247  *
6248  * 	This procedure is invoked to display the widget.
6249  *
6250  *      Recompute the layout of the text if necessary. This is
6251  *	necessary if the world coordinate system has changed.
6252  *	Specifically, the following may have occurred:
6253  *
6254  *	  1.  a text attribute has changed (font, linespacing, etc.).
6255  *	  2.  an entry's option changed, possibly resizing the entry.
6256  *
6257  *      This is deferred to the display routine since potentially
6258  *      many of these may occur.
6259  *
6260  *	Set the vertical and horizontal scrollbars.  This is done
6261  *	here since the window width and height are needed for the
6262  *	scrollbar calculations.
6263  *
6264  * Results:
6265  *	None.
6266  *
6267  * Side effects:
6268  * 	The widget is redisplayed.
6269  *
6270  * ----------------------------------------------------------------------
6271  */
6272 static void
DisplayTreeView(ClientData clientData)6273 DisplayTreeView(ClientData clientData)	/* Information about widget. */
6274 {
6275     Blt_ChainLink *linkPtr;
6276     TreeView *tvPtr = clientData;
6277     TreeViewColumn *columnPtr;
6278     Pixmap drawable;
6279     int width, height;
6280     int x;
6281 
6282     Blt_TreeViewChanged(tvPtr);
6283     if (tvPtr->flags & TV_DELETED) return;
6284     tvPtr->flags &= ~TV_REDRAW;
6285     if (tvPtr->rootNodeNum>0 && tvPtr->rootPtr->node == NULL) {
6286         /* Reset root to tree top. */
6287         tvPtr->rootNodeNum = 0;
6288         tvPtr->rootNode = Blt_TreeRootNode(tvPtr->tree);
6289         Blt_TreeApply(tvPtr->rootNode, CreateApplyProc, tvPtr);
6290         tvPtr->focusPtr = tvPtr->rootPtr = Blt_NodeToEntry(tvPtr, tvPtr->rootNode);
6291         tvPtr->flags |= TV_DIRTYALL;
6292         tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
6293         Blt_SetFocusItem(tvPtr->bindTable, tvPtr->rootPtr, ITEM_ENTRY);
6294         if (Blt_TreeViewOpenEntry(tvPtr, tvPtr->rootPtr) != TCL_OK) {
6295             return;
6296         }
6297     }
6298     if (tvPtr->tkwin == NULL) {
6299 	return;			/* Window has been destroyed. */
6300     }
6301     if (tvPtr->flags & TV_DIRTYALL) {
6302         TreeViewEntry *entryPtr;
6303         tvPtr->flags &= ~TV_DIRTYALL;
6304         if (Blt_TreeViewUpdateWidget(tvPtr->interp, tvPtr) != TCL_OK) {
6305             return;
6306         }
6307         Blt_TreeViewUpdateStyles(tvPtr);
6308         Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
6309         Blt_TreeViewConfigureColumns(tvPtr);
6310         for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
6311         entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
6312             entryPtr->flags |= ENTRY_DIRTY;
6313         }
6314         Blt_TreeViewMakeStyleDirty(tvPtr);
6315     }
6316     if (tvPtr->flags & TV_LAYOUT) {
6317 	/*
6318 	 * Recompute the layout when entries are opened/closed,
6319 	 * inserted/deleted, or when text attributes change (such as
6320 	 * font, linespacing).
6321 	 */
6322 	if (Blt_TreeViewComputeLayout(tvPtr) != TCL_OK) return;
6323     }
6324     if (tvPtr->flags & TV_SCROLL) {
6325 	/*
6326 	 * Scrolling means that the view port has changed and that the
6327 	 * visible entries need to be recomputed.
6328 	 */
6329 	if (ComputeVisibleEntries(tvPtr) != TCL_OK) return;
6330 
6331 	width = VPORTWIDTH(tvPtr);
6332 	height = VPORTHEIGHT(tvPtr);
6333 	if (tvPtr->flags & TV_XSCROLL) {
6334 	    if (tvPtr->xScrollCmdPrefix != NULL) {
6335 		Blt_UpdateScrollbar(tvPtr->interp, tvPtr->xScrollCmdPrefix,
6336 		    (double)tvPtr->xOffset / tvPtr->worldWidth,
6337 		    (double)(tvPtr->xOffset + width) / tvPtr->worldWidth);
6338 	    }
6339 	}
6340 	if (tvPtr->flags & TV_YSCROLL) {
6341 	    if (tvPtr->yScrollCmdPrefix != NULL) {
6342 		Blt_UpdateScrollbar(tvPtr->interp, tvPtr->yScrollCmdPrefix,
6343 		    (double)tvPtr->yOffset / tvPtr->worldHeight,
6344 		    (double)(tvPtr->yOffset + height) / tvPtr->worldHeight);
6345 	    }
6346 	}
6347 	tvPtr->flags &= ~TV_SCROLL;
6348     }
6349     if (tvPtr->reqWidth == 0) {
6350 
6351 	/*
6352 	 * The first time through this routine, set the requested
6353 	 * width to the computed width.  All we want is to
6354 	 * automatically set the width of the widget, not dynamically
6355 	 * grow/shrink it as attributes change.
6356 	 */
6357 
6358 	tvPtr->reqWidth = tvPtr->worldWidth + 2 * tvPtr->insetX;
6359 	Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
6360     }
6361     if (!Tk_IsMapped(tvPtr->tkwin)) {
6362 	return;
6363     }
6364 
6365     drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin),
6366 	Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin),
6367 	Tk_Depth(tvPtr->tkwin));
6368     tvPtr->flags |= TV_VIEWPORT;
6369     if (Blt_HasTile(tvPtr->tile)) {
6370 	if (tvPtr->scrollTile) {
6371 	    Blt_SetTSOrigin(tvPtr->tkwin, tvPtr->tile, -tvPtr->xOffset,
6372 		-tvPtr->yOffset);
6373 	} else {
6374 	    Blt_SetTileOrigin(tvPtr->tkwin, tvPtr->tile, 0, 0);
6375 	}
6376         Blt_Fill3DRectangle(tvPtr->tkwin, drawable, tvPtr->border, 0, 0,
6377             Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6378         Blt_TileRectangle(tvPtr->tkwin, drawable, tvPtr->tile, 0, 0,
6379 	    Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin));
6380     } else {
6381         Blt_Fill3DRectangle(tvPtr->tkwin, drawable, tvPtr->border, 0, 0,
6382             Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6383     }
6384 
6385     if ((tvPtr->flags & TV_RULE_ACTIVE) &&
6386 	(tvPtr->resizeColumnPtr != NULL)) {
6387 	Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
6388     }
6389     Blt_TreeViewMarkWindows(tvPtr, TV_WINDOW_CLEAR);
6390     {
6391 	register TreeViewEntry **p;
6392 	TreeViewEntry *entryPtr;
6393 	Tk_3DBorder border, selBorder;
6394 	int y, altRow = 0;
6395 	Blt_Tile tile;
6396 
6397         entryPtr = NULL;
6398         for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++, altRow++) {
6399             entryPtr = *p;
6400             if (tvPtr->altStylePtr && ((altRow+tvPtr->nAbove)%2)) {
6401                 entryPtr->flags |= ENTRY_ALTROW;
6402             } else {
6403                 entryPtr->flags &= ~ENTRY_ALTROW;
6404             }
6405         }
6406         selBorder = SELECT_BORDER(tvPtr);
6407 	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
6408 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
6409 
6410 	    columnPtr = Blt_ChainGetValue(linkPtr);
6411 	    columnPtr->flags &= ~COLUMN_DIRTY;
6412 	    if (columnPtr->hidden) {
6413 		continue;
6414 	    }
6415 	    x = SCREENX(tvPtr, columnPtr->worldX);
6416 	    if ((x + columnPtr->width) < 0) {
6417 		continue;	/* Don't draw columns before the left edge. */
6418 	    }
6419 	    if (x > Tk_Width(tvPtr->tkwin)) {
6420 		break;		/* Discontinue when a column starts beyond
6421 				 * the right edge. */
6422 	    }
6423 	    /* Clear the column background. */
6424              if (columnPtr->border && (columnPtr->hasbg || columnPtr->stylePtr == NULL)) {
6425 	        border = columnPtr->border;
6426             } else {
6427                 border = Blt_TreeViewGetStyleBorder(tvPtr, columnPtr->stylePtr);
6428             }
6429             tile = NULL;
6430             if (Blt_HasTile(columnPtr->tile)) {
6431                 tile = columnPtr->tile;
6432             } else if (columnPtr->stylePtr && Blt_HasTile(columnPtr->stylePtr->tile)) {
6433                 tile = columnPtr->stylePtr->tile;
6434             }
6435             if (tile) {
6436                  Blt_TreeViewFill3DTile(tvPtr, drawable, border, x, 0,
6437                     columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT,
6438                     tile, columnPtr->scrollTile, 0);
6439                  Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
6440                     columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6441             } else if (Blt_HasTile(tvPtr->tile)) {
6442                  Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
6443                     columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6444                  Blt_TileRectangle(tvPtr->tkwin, drawable, tvPtr->tile,
6445                     x, 0, columnPtr->width, Tk_Height(tvPtr->tkwin));
6446                  Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
6447                     columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6448              } else if (columnPtr->hasbg && border) {
6449                  Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
6450                  columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6451              } else if (border) {
6452                  Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
6453                     columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
6454             }
6455 	    if (columnPtr == &tvPtr->treeColumn) {
6456 
6457                  if (tvPtr->flatView) {
6458                      DrawFlatView(tvPtr, drawable, x);
6459                  } else {
6460                      DrawTreeView(tvPtr, drawable, x);
6461                  }
6462                  if (tvPtr->flags & TV_DELETED) return;
6463             } else {
6464 
6465                 TreeViewValue *valuePtr;
6466                 TreeViewStyle *csPtr;
6467 		int  ishid;
6468 
6469 		entryPtr = NULL;
6470 		csPtr = CHOOSE(tvPtr->stylePtr, columnPtr->stylePtr);
6471 		for (p = tvPtr->visibleArr; p != NULL && *p != NULL; p++) {
6472 		    int isAlt;
6473 		    entryPtr = *p;
6474 		    isAlt = (entryPtr->flags & ENTRY_ALTROW);
6475 		    y = SCREENY(tvPtr, entryPtr->worldY);
6476 
6477 		    /* Draw the background of the value. */
6478 		    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, columnPtr)) {
6479                           Blt_TreeViewFill3DTile(tvPtr, drawable, selBorder, x, y,
6480                               columnPtr->width, entryPtr->height,
6481                               tvPtr->selBorderWidth, tvPtr->selRelief,
6482                               tvPtr->selectTile, 1, 0);
6483                       } else if (isAlt && tvPtr->altStylePtr
6484                     && tvPtr->altStylePtr->border
6485                     && tvPtr->altStylePtr->priority>=csPtr->priority ) {
6486                          Blt_Fill3DRectangle(tvPtr->tkwin, drawable,
6487                             tvPtr->altStylePtr->border,
6488                             x, y, columnPtr->width,
6489                             entryPtr->height,
6490                             0, TK_RELIEF_FLAT);
6491                     } else if (entryPtr->stylePtr && entryPtr->stylePtr->border) {
6492                          Blt_Fill3DRectangle(tvPtr->tkwin, drawable,
6493                             entryPtr->stylePtr->border,
6494                             x, y, columnPtr->width,
6495                             entryPtr->height,
6496                             0, TK_RELIEF_FLAT);
6497                     }
6498 		    /* Check if there's a corresponding value in the entry. */
6499 		    valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
6500 		    ishid = 0;
6501 		    if (valuePtr != NULL && valuePtr->stylePtr != NULL &&
6502 		        valuePtr->stylePtr->hidden) {
6503                           valuePtr = NULL;
6504                           ishid = 1;
6505                     }
6506                     if (valuePtr == NULL && columnPtr->fillCmd != NULL
6507                         && strlen(Tcl_GetString(columnPtr->fillCmd))) {
6508                         Tcl_Interp *interp = tvPtr->interp;
6509                         int result, objc;
6510                         char *string = Blt_TreeNodeLabel(entryPtr->node);
6511                         Tcl_Obj **objv, *objPtr = Tcl_DuplicateObj(columnPtr->fillCmd);
6512 
6513                         Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(string,-1));
6514                         Tcl_IncrRefCount(objPtr);
6515                         if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) == TCL_OK) {
6516                             Tcl_Obj *listObjPtr;
6517 
6518                             Tcl_Preserve(entryPtr);
6519                             result = Tcl_EvalObjv(interp, objc, objv, TCL_EVAL_GLOBAL);
6520                             if ((entryPtr->flags & ENTRY_DELETED) ||
6521                                 (tvPtr->flags & TV_DELETED)) {
6522                                 Tcl_DecrRefCount(objPtr);
6523                                 Tcl_Release(entryPtr);
6524                                 return;
6525                             }
6526                             string = Tcl_GetStringResult(interp);
6527                             if (result != TCL_ERROR) {
6528                                 listObjPtr = Tcl_DuplicateObj(Tcl_GetObjResult(interp));
6529                             } else {
6530                                 listObjPtr = Tcl_NewStringObj("",-1);
6531                             }
6532                             Tcl_IncrRefCount(listObjPtr);
6533                             if (Blt_TreeSetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node, columnPtr->key, listObjPtr) == TCL_OK) {
6534                                 Blt_TreeViewAddValue(entryPtr, columnPtr);
6535                                 Blt_TreeViewEventuallyRedraw(tvPtr);
6536                             }
6537                             Tcl_DecrRefCount(listObjPtr);
6538                             Tcl_Release(entryPtr);
6539                         }
6540                         Tcl_DecrRefCount(objPtr);
6541                     }
6542                     if (valuePtr == NULL && (tvPtr->flags & TV_FILL_NULL)) {
6543                         valuePtr = columnPtr->defValue;
6544                         if (valuePtr) {
6545                             valuePtr->stylePtr = tvPtr->emptyStylePtr;
6546                         }
6547                     }
6548                     if (valuePtr != NULL) {
6549 			Blt_TreeViewDrawValue(tvPtr, entryPtr, valuePtr,
6550 				drawable, x + columnPtr->pad.side1, y,
6551 				isAlt, ishid);
6552                         if (tvPtr->flags & TV_DELETED) return;
6553                         if (tvPtr->ruleWidth) {
6554                             DrawEntryRule( tvPtr, entryPtr, columnPtr, drawable, x, y);
6555                         }
6556 		    }
6557 		}
6558 	    }
6559 	    if (columnPtr->relief != TK_RELIEF_FLAT) {
6560                  Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x,
6561                      tvPtr->padY, columnPtr->width,
6562                      Tk_Height(tvPtr->tkwin)-(tvPtr->padY*2),
6563 		     columnPtr->borderWidth, columnPtr->relief);
6564 	    }
6565 
6566 	}
6567     }
6568     Blt_TreeViewMarkWindows(tvPtr, TV_WINDOW_UNMAP);
6569     if (tvPtr->flags & TV_SHOW_COLUMN_TITLES) {
6570 	Blt_TreeViewDrawHeadings(tvPtr, drawable);
6571     }
6572     Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
6573     if ((tvPtr->flags & TV_RULE_NEEDED) &&
6574 	(tvPtr->resizeColumnPtr != NULL)) {
6575 	Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
6576     }
6577     /* Now copy the new view to the window. */
6578     XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin),
6579 	tvPtr->lineGC, 0, 0, Tk_Width(tvPtr->tkwin),
6580 	Tk_Height(tvPtr->tkwin), 0, 0);
6581     Tk_FreePixmap(tvPtr->display, drawable);
6582     tvPtr->flags &= ~TV_VIEWPORT;
6583 }
6584 
6585 /*
6586  *----------------------------------------------------------------------
6587  *
6588  * Blt_TreeViewSelectCmdProc --
6589  *
6590  *      Invoked at the next idle point whenever the current
6591  *      selection changes.  Executes some application-specific code
6592  *      in the -selectcommand option.  This provides a way for
6593  *      applications to handle selection changes.
6594  *
6595  * Results:
6596  *      None.
6597  *
6598  * Side effects:
6599  *      Tcl code gets executed for some application-specific task.
6600  *
6601  *----------------------------------------------------------------------
6602  */
6603 void
Blt_TreeViewSelectCmdProc(ClientData clientData)6604 Blt_TreeViewSelectCmdProc(ClientData clientData)
6605 {
6606     TreeView *tvPtr = clientData;
6607 
6608     Tcl_Preserve(tvPtr);
6609     if (tvPtr->selectCmd != NULL) {
6610 	tvPtr->flags &= ~TV_SELECT_PENDING;
6611 	if (Tcl_GlobalEval(tvPtr->interp, tvPtr->selectCmd) != TCL_OK) {
6612 	    Tcl_BackgroundError(tvPtr->interp);
6613 	}
6614     }
6615     Tcl_Release(tvPtr);
6616 }
6617 
6618 /*
6619  * --------------------------------------------------------------
6620  *
6621  * TreeViewObjCmd --
6622  *
6623  * 	This procedure is invoked to process the Tcl command that
6624  * 	corresponds to a widget managed by this module. See the user
6625  * 	documentation for details on what it does.
6626  *
6627  * Results:
6628  *	A standard Tcl result.
6629  *
6630  * Side effects:
6631  *	See the user documentation.
6632  *
6633  * --------------------------------------------------------------
6634  */
6635 /* ARGSUSED */
6636 static int
TreeViewObjCmd(clientData,interp,objc,objv)6637 TreeViewObjCmd(clientData, interp, objc, objv)
6638     ClientData clientData;	/* Main window associated with interpreter. */
6639     Tcl_Interp *interp;		/* Current interpreter. */
6640     int objc;			/* Number of arguments. */
6641     Tcl_Obj *CONST *objv;	/* Argument strings. */
6642 {
6643     Tcl_CmdInfo cmdInfo;
6644     Tcl_Obj *initObjv[2];
6645     TreeView *tvPtr;
6646     char *className;
6647     char *string;
6648 
6649     string = Tcl_GetString(objv[0]);
6650     if (objc < 2) {
6651 	Tcl_AppendResult(interp, "wrong # args: should be \"", string,
6652 		" pathName ?option value?...\"", (char *)NULL);
6653 	return TCL_ERROR;
6654     }
6655     className = (string[0] == 'h') ? "Hiertable" : "TreeView";
6656     tvPtr = CreateTreeView(interp, objv[1], className);
6657     if (tvPtr == NULL) {
6658 	goto error;
6659     }
6660     /*
6661      * Invoke a procedure to initialize various bindings on treeview
6662      * entries.  If the procedure doesn't already exist, source it
6663      * from "$blt_library/treeview.tcl".  We deferred sourcing the
6664      * file until now so that the variable $blt_library could be set
6665      * within a script.
6666      */
6667     if (!Tcl_GetCommandInfo(interp, "blt::tv::Initialize", &cmdInfo)) {
6668 	char cmd[200];
6669 	sprintf(cmd, "set className %s\n\
6670 source [file join $blt_library treeview.tcl]\n\
6671 unset className\n", className);
6672 	if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
6673 	    char info[200];
6674 
6675 	    sprintf(info, "\n    (while loading bindings for %.50s)",
6676 		    Tcl_GetString(objv[0]));
6677 	    Tcl_AddErrorInfo(interp, info);
6678 	    goto error;
6679 	}
6680     }
6681     /*
6682      * Initialize the widget's configuration options here. The options
6683      * need to be set first, so that entry, column, and style
6684      * components can use them for their own GCs.
6685      */
6686     Blt_TreeViewOptsInit(tvPtr);
6687     if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
6688 	objc - 2, objv + 2, (char *)tvPtr, 0, NULL) != TCL_OK) {
6689 	goto error;
6690     }
6691     if (tvPtr->tile != NULL) {
6692         Blt_SetTileChangedProc(tvPtr->tile, Blt_TreeViewTileChangedProc, tvPtr);
6693     }
6694     if (tvPtr->selectTile != NULL) {
6695         Blt_SetTileChangedProc(tvPtr->selectTile, Blt_TreeViewTileChangedProc, tvPtr);
6696     }
6697     if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, "button", "Button",
6698 	 bltTreeViewButtonSpecs, 0, (Tcl_Obj **)NULL, (char *)tvPtr, 0)
6699 	!= TCL_OK) {
6700 	goto error;
6701     }
6702 
6703     /*
6704      * Rebuild the widget's GC and other resources that are predicated
6705      * by the widget's configuration options.  Do the same for the
6706      * default column.
6707      */
6708     if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
6709 	goto error;
6710     }
6711     Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
6712     Blt_TreeViewUpdateStyles(tvPtr);
6713 
6714     /*
6715      * Invoke a procedure to initialize various bindings on treeview
6716      * entries.  If the procedure doesn't already exist, source it
6717      * from "$blt_library/treeview.tcl".  We deferred sourcing the
6718      * file until now so that the variable $blt_library could be set
6719      * within a script.
6720      */
6721     initObjv[0] = Tcl_NewStringObj("blt::tv::Initialize", -1);
6722     initObjv[1] = objv[1];
6723     Tcl_IncrRefCount(initObjv[0]);
6724     if (Tcl_EvalObjv(interp, 2, initObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
6725 	goto error;
6726     }
6727     Tcl_DecrRefCount(initObjv[0]);
6728     Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1));
6729     return TCL_OK;
6730   error:
6731     if (tvPtr)
6732         Tk_DestroyWindow(tvPtr->tkwin);
6733     return TCL_ERROR;
6734 }
6735 
6736 int
Blt_TreeViewInit(Tcl_Interp * interp)6737 Blt_TreeViewInit(Tcl_Interp *interp)
6738 {
6739     static Blt_ObjCmdSpec cmdSpec[] = {
6740 	{ "treeview", TreeViewObjCmd, },
6741 	/*{ "hiertable", TreeViewObjCmd, } */
6742     };
6743 
6744     if (Blt_InitObjCmd(interp, "blt", cmdSpec) == NULL) {
6745 	return TCL_ERROR;
6746     }
6747    /* if (Blt_InitObjCmd(interp, "blt", cmdSpec + 1) == NULL) {
6748 	return TCL_ERROR;
6749     }*/
6750     return TCL_OK;
6751 }
6752 
6753 #endif /* NO_TREEVIEW */
6754