OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / blt2.5 / generic / bltTreeView.c
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
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
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
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
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
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
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
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 *
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
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
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
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 *
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
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 *
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
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 *
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
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 *
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
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
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 *
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
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
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
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
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 *
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
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 *
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
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
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
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
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
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
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
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 *
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
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 *
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
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
1772 Blt_TreeViewIsLeaf(TreeViewEntry *entryPtr) {
1773     if (entryPtr->flags&ENTRY_IS_TREE) return FALSE;
1774     return Blt_TreeIsLeaf(entryPtr->node);
1775 }
1776
1777 int
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
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 *
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 *
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 *
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 *
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 *
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 *
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
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
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, 0);
2124     return TCL_OK;
2125 }
2126
2127 void
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
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
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 *
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
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
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
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
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 *
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
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
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
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
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
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
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 *
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 *
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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, 0);
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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  */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */