source: trunk/kitgen/8.x/blt/generic/bltTreeView.c@ 175

Last change on this file since 175 was 175, checked in by demin, 12 years ago

initial commit

File size: 147.3 KB
Line 
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 */
29
30/*
31 * TODO:
32 *
33 * BUGS:
34 * 1. "open" operation should change scroll offset so that as many
35 * new entries (up to half a screen) can be seen.
36 * 2. "open" needs to adjust the scrolloffset so that the same entry
37 * is seen at the same place.
38 */
39
40#include "bltInt.h"
41
42#ifndef NO_TREEVIEW
43
44#include "bltTreeView.h"
45
46#define BUTTON_PAD 2
47#define BUTTON_IPAD 1
48#define BUTTON_SIZE 7
49#define COLUMN_PAD 2
50#define FOCUS_WIDTH 1
51#define ICON_PADX 2
52#define ICON_PADY 1
53#define INSET_PAD 0
54#define LABEL_PADX 3
55#define LABEL_PADY 0
56
57#include <X11/Xutil.h>
58#include <X11/Xatom.h>
59
60#define DEF_ICON_WIDTH 16
61#define DEF_ICON_HEIGHT 16
62
63static Blt_TreeApplyProc DeleteApplyProc;
64static Blt_TreeApplyProc CreateApplyProc;
65
66extern Blt_CustomOption bltTreeViewDataOption;
67
68static Blt_OptionParseProc ObjToTree;
69static Blt_OptionPrintProc TreeToObj;
70static Blt_OptionFreeProc FreeTree;
71Blt_CustomOption bltTreeViewTreeOption =
72{
73 ObjToTree, TreeToObj, FreeTree, NULL,
74};
75
76static Blt_OptionParseProc ObjToIcons;
77static Blt_OptionPrintProc IconsToObj;
78static Blt_OptionFreeProc FreeIcons;
79Blt_CustomOption bltTreeViewIconsOption =
80{
81 /* Contains a pointer to the widget that's currently being
82 * configured. This is used in the custom configuration parse
83 * routine for icons. */
84 ObjToIcons, IconsToObj, FreeIcons, NULL,
85};
86
87static Blt_OptionParseProc ObjToButton;
88static Blt_OptionPrintProc ButtonToObj;
89static Blt_CustomOption buttonOption = {
90 ObjToButton, ButtonToObj, NULL, NULL,
91};
92
93static Blt_OptionParseProc ObjToUid;
94static Blt_OptionPrintProc UidToObj;
95static Blt_OptionFreeProc FreeUid;
96Blt_CustomOption bltTreeViewUidOption = {
97 ObjToUid, UidToObj, FreeUid, NULL,
98};
99
100static Blt_OptionParseProc ObjToScrollmode;
101static Blt_OptionPrintProc ScrollmodeToObj;
102static Blt_CustomOption scrollmodeOption = {
103 ObjToScrollmode, ScrollmodeToObj, NULL, NULL,
104};
105
106static Blt_OptionParseProc ObjToSelectmode;
107static Blt_OptionPrintProc SelectmodeToObj;
108static Blt_CustomOption selectmodeOption = {
109 ObjToSelectmode, SelectmodeToObj, NULL, NULL,
110};
111
112static Blt_OptionParseProc ObjToSeparator;
113static Blt_OptionPrintProc SeparatorToObj;
114static Blt_OptionFreeProc FreeSeparator;
115static Blt_CustomOption separatorOption = {
116 ObjToSeparator, SeparatorToObj, FreeSeparator, NULL,
117};
118
119static Blt_OptionParseProc ObjToLabel;
120static Blt_OptionPrintProc LabelToObj;
121static Blt_OptionFreeProc FreeLabel;
122static Blt_CustomOption labelOption =
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_ACTIVE_FOREGROUND "black"
149#define DEF_TV_ACTIVE_ICONS \
150 "blt::tv::activeOpenFolder blt::tv::activeCloseFolder"
151#define DEF_TV_ACTIVE_RELIEF "flat"
152#define DEF_TV_ACTIVE_STIPPLE "gray25"
153#define DEF_TV_ALLOW_DUPLICATES "yes"
154#define DEF_TV_BACKGROUND "white"
155#define DEF_TV_BORDERWIDTH STD_BORDERWIDTH
156#define DEF_TV_BUTTON "auto"
157#define DEF_TV_DASHES "dot"
158#define DEF_TV_EXPORT_SELECTION "no"
159#define DEF_TV_FOREGROUND STD_NORMAL_FOREGROUND
160#define DEF_TV_FG_MONO STD_NORMAL_FG_MONO
161#define DEF_TV_FLAT "no"
162#define DEF_TV_FOCUS_DASHES "dot"
163#define DEF_TV_FOCUS_EDIT "no"
164#define DEF_TV_FOCUS_FOREGROUND STD_ACTIVE_FOREGROUND
165#define DEF_TV_FOCUS_FG_MONO STD_ACTIVE_FG_MONO
166#define DEF_TV_FONT "Courier 12"
167#define DEF_TV_HEIGHT "400"
168#define DEF_TV_HIDE_LEAVES "no"
169#define DEF_TV_HIDE_ROOT "yes"
170#define DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND
171#define DEF_TV_FOCUS_HIGHLIGHT_COLOR "black"
172#define DEF_TV_FOCUS_HIGHLIGHT_WIDTH "2"
173#define DEF_TV_ICONS "blt::tv::normalOpenFolder blt::tv::normalCloseFolder"
174#define DEF_TV_VLINE_COLOR RGB_GREY50
175#define DEF_TV_VLINE_MONO STD_NORMAL_FG_MONO
176#define DEF_TV_LINESPACING "0"
177#define DEF_TV_LINEWIDTH "1"
178#define DEF_TV_MAKE_PATH "no"
179#define DEF_TV_NEW_TAGS "no"
180#define DEF_TV_NORMAL_BACKGROUND STD_NORMAL_BACKGROUND
181#define DEF_TV_NORMAL_FG_MONO STD_ACTIVE_FG_MONO
182#define DEF_TV_RELIEF "sunken"
183#define DEF_TV_RESIZE_CURSOR "arrow"
184#define DEF_TV_SCROLL_INCREMENT "20"
185#define DEF_TV_SCROLL_MODE "hierbox"
186#define DEF_TV_SELECT_BACKGROUND STD_SELECT_BACKGROUND /* RGB_LIGHTBLUE1 */
187#define DEF_TV_SELECT_BG_MONO STD_SELECT_BG_MONO
188#define DEF_TV_SELECT_BORDERWIDTH "1"
189#define DEF_TV_SELECT_FOREGROUND STD_SELECT_FOREGROUND
190#define DEF_TV_SELECT_FG_MONO STD_SELECT_FG_MONO
191#define DEF_TV_SELECT_MODE "single"
192#define DEF_TV_SELECT_RELIEF "flat"
193#define DEF_TV_SHOW_ROOT "yes"
194#define DEF_TV_SHOW_TITLES "yes"
195#define DEF_TV_SORT_SELECTION "no"
196#define DEF_TV_TAKE_FOCUS "1"
197#define DEF_TV_TEXT_COLOR STD_NORMAL_FOREGROUND
198#define DEF_TV_TEXT_MONO STD_NORMAL_FG_MONO
199#define DEF_TV_TRIMLEFT ""
200#define DEF_TV_WIDTH "200"
201
202Blt_ConfigSpec bltTreeViewButtonSpecs[] =
203{
204 {BLT_CONFIG_BORDER, "-activebackground", "activeBackground", "Background",
205 DEF_BUTTON_ACTIVE_BACKGROUND, Blt_Offset(TreeView, button.activeBorder),
206 0},
207 {BLT_CONFIG_SYNONYM, "-activebg", "activeBackground", (char *)NULL,
208 (char *)NULL, 0, 0},
209 {BLT_CONFIG_SYNONYM, "-activefg", "activeForeground", (char *)NULL,
210 (char *)NULL, 0, 0},
211 {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground", "Foreground",
212 DEF_BUTTON_ACTIVE_FOREGROUND,
213 Blt_Offset(TreeView, button.activeFgColor), 0},
214 {BLT_CONFIG_BORDER, "-background", "background", "Background",
215 DEF_BUTTON_NORMAL_BACKGROUND, Blt_Offset(TreeView, button.border), 0},
216 {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,
217 0},
218 {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
219 {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
220 DEF_BUTTON_BORDERWIDTH, Blt_Offset(TreeView, button.borderWidth),
221 BLT_CONFIG_DONT_SET_DEFAULT},
222 {BLT_CONFIG_RELIEF, "-closerelief", "closeRelief", "Relief",
223 DEF_BUTTON_CLOSE_RELIEF, Blt_Offset(TreeView, button.closeRelief),
224 BLT_CONFIG_DONT_SET_DEFAULT},
225 {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
226 {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
227 DEF_BUTTON_NORMAL_FOREGROUND, Blt_Offset(TreeView, button.fgColor), 0},
228 {BLT_CONFIG_CUSTOM, "-images", "images", "Icons",
229 (char *)NULL, Blt_Offset(TreeView, button.icons), BLT_CONFIG_NULL_OK,
230 &bltTreeViewIconsOption},
231 {BLT_CONFIG_RELIEF, "-openrelief", "openRelief", "Relief",
232 DEF_BUTTON_OPEN_RELIEF, Blt_Offset(TreeView, button.openRelief),
233 BLT_CONFIG_DONT_SET_DEFAULT},
234 {BLT_CONFIG_DISTANCE, "-size", "size", "Size",
235 DEF_BUTTON_SIZE, Blt_Offset(TreeView, button.reqSize), 0},
236 {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
237 (char *)NULL, 0, 0}
238};
239
240Blt_ConfigSpec bltTreeViewEntrySpecs[] =
241{
242 {BLT_CONFIG_CUSTOM, "-activeicons", (char *)NULL, (char *)NULL,
243 (char *)NULL, Blt_Offset(TreeViewEntry, activeIcons),
244 BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
245 {BLT_CONFIG_CUSTOM, "-bindtags", (char *)NULL, (char *)NULL,
246 (char *)NULL, Blt_Offset(TreeViewEntry, tagsUid),
247 BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
248 {BLT_CONFIG_CUSTOM, "-button", (char *)NULL, (char *)NULL,
249 DEF_TV_BUTTON, Blt_Offset(TreeViewEntry, flags),
250 BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
251 {BLT_CONFIG_CUSTOM, "-closecommand", (char *)NULL, (char *)NULL,
252 (char *)NULL, Blt_Offset(TreeViewEntry, closeCmd),
253 BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
254 {BLT_CONFIG_CUSTOM, "-data", (char *)NULL, (char *)NULL,
255 (char *)NULL, 0, BLT_CONFIG_NULL_OK, &bltTreeViewDataOption},
256 {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL,
257 0, 0},
258 {BLT_CONFIG_FONT, "-font", (char *)NULL, (char *)NULL,
259 (char *)NULL, Blt_Offset(TreeViewEntry, font), 0},
260 {BLT_CONFIG_COLOR, "-foreground", "foreground", (char *)NULL,
261 (char *)NULL, Blt_Offset(TreeViewEntry, color),
262 BLT_CONFIG_NULL_OK},
263 {BLT_CONFIG_DISTANCE, "-height", (char *)NULL, (char *)NULL,
264 (char *)NULL, Blt_Offset(TreeViewEntry, reqHeight),
265 BLT_CONFIG_DONT_SET_DEFAULT},
266 {BLT_CONFIG_CUSTOM, "-icons", (char *)NULL, (char *)NULL,
267 (char *)NULL, Blt_Offset(TreeViewEntry, icons),
268 BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
269 {BLT_CONFIG_CUSTOM, "-label", (char *)NULL, (char *)NULL,
270 (char *)NULL, Blt_Offset(TreeViewEntry, labelUid), 0,
271 &labelOption},
272 {BLT_CONFIG_CUSTOM, "-opencommand", (char *)NULL, (char *)NULL,
273 (char *)NULL, Blt_Offset(TreeViewEntry, openCmd),
274 BLT_CONFIG_NULL_OK, &bltTreeViewUidOption},
275 {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
276 (char *)NULL, Blt_Offset(TreeViewEntry, shadow),
277 BLT_CONFIG_NULL_OK | BLT_CONFIG_COLOR_ONLY},
278 {BLT_CONFIG_SHADOW, "-shadow", (char *)NULL, (char *)NULL,
279 (char *)NULL, Blt_Offset(TreeViewEntry, shadow),
280 BLT_CONFIG_NULL_OK | BLT_CONFIG_MONO_ONLY},
281 {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
282 (char *)NULL, 0, 0}
283};
284
285Blt_ConfigSpec bltTreeViewSpecs[] =
286{
287 {BLT_CONFIG_CUSTOM, "-activeicons", "activeIcons", "Icons",
288 DEF_TV_ACTIVE_ICONS, Blt_Offset(TreeView, activeIcons),
289 BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
290 {BLT_CONFIG_BITFLAG,
291 "-allowduplicates", "allowDuplicates", "AllowDuplicates",
292 DEF_TV_ALLOW_DUPLICATES, Blt_Offset(TreeView, flags),
293 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_ALLOW_DUPLICATES},
294 {BLT_CONFIG_BITFLAG, "-autocreate", "autoCreate", "AutoCreate",
295 DEF_TV_MAKE_PATH, Blt_Offset(TreeView, flags),
296 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_FILL_ANCESTORS},
297 {BLT_CONFIG_BORDER, "-background", "background", "Background",
298 DEF_TV_BACKGROUND, Blt_Offset(TreeView, border), 0},
299 {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,
300 0, 0},
301 {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL,
302 0, 0},
303 {BLT_CONFIG_DISTANCE, "-borderwidth", "borderWidth", "BorderWidth",
304 DEF_TV_BORDERWIDTH, Blt_Offset(TreeView, borderWidth),
305 BLT_CONFIG_DONT_SET_DEFAULT},
306 {BLT_CONFIG_CUSTOM, "-button", "button", "Button",
307 DEF_TV_BUTTON, Blt_Offset(TreeView, buttonFlags),
308 BLT_CONFIG_DONT_SET_DEFAULT, &buttonOption},
309 {BLT_CONFIG_STRING, "-closecommand", "closeCommand", "CloseCommand",
310 (char *)NULL, Blt_Offset(TreeView, closeCmd),
311 BLT_CONFIG_NULL_OK},
312 {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
313 (char *)NULL, Blt_Offset(TreeView, cursor), BLT_CONFIG_NULL_OK},
314 {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes",
315 DEF_TV_DASHES, Blt_Offset(TreeView, dashes),
316 BLT_CONFIG_DONT_SET_DEFAULT},
317 {BLT_CONFIG_BITFLAG, "-exportselection", "exportSelection",
318 "ExportSelection", DEF_TV_EXPORT_SELECTION,
319 Blt_Offset(TreeView, flags), BLT_CONFIG_DONT_SET_DEFAULT,
320 (Blt_CustomOption *)TV_SELECT_EXPORT},
321 {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL,
322 0, 0},
323 {BLT_CONFIG_BOOLEAN, "-flat", "flat", "Flat",
324 DEF_TV_FLAT, Blt_Offset(TreeView, flatView),
325 BLT_CONFIG_DONT_SET_DEFAULT},
326 {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
327 DEF_TV_FOCUS_DASHES, Blt_Offset(TreeView, focusDashes),
328 BLT_CONFIG_NULL_OK},
329 {BLT_CONFIG_COLOR,
330 "-focusforeground", "focusForeground", "FocusForeground",
331 DEF_TV_FOCUS_FOREGROUND, Blt_Offset(TreeView, focusColor),
332 BLT_CONFIG_COLOR_ONLY},
333 {BLT_CONFIG_COLOR,
334 "-focusforeground", "focusForeground", "FocusForeground",
335 DEF_TV_FOCUS_FG_MONO, Blt_Offset(TreeView, focusColor),
336 BLT_CONFIG_MONO_ONLY},
337 {BLT_CONFIG_FONT, "-font", "font", "Font",
338 DEF_TV_FONT, Blt_Offset(TreeView, font), 0},
339 {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
340 DEF_TV_TEXT_COLOR, Blt_Offset(TreeView, fgColor),
341 BLT_CONFIG_COLOR_ONLY},
342 {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
343 DEF_TV_TEXT_MONO, Blt_Offset(TreeView, fgColor),
344 BLT_CONFIG_MONO_ONLY},
345 {BLT_CONFIG_DISTANCE, "-height", "height", "Height",
346 DEF_TV_HEIGHT, Blt_Offset(TreeView, reqHeight),
347 BLT_CONFIG_DONT_SET_DEFAULT},
348 {BLT_CONFIG_BITFLAG, "-hideleaves", "hideLeaves", "HideLeaves",
349 DEF_TV_HIDE_LEAVES, Blt_Offset(TreeView, flags),
350 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_LEAVES},
351 {BLT_CONFIG_BITFLAG, "-hideroot", "hideRoot", "HideRoot",
352 DEF_TV_HIDE_ROOT, Blt_Offset(TreeView, flags),
353 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_HIDE_ROOT},
354 {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
355 "HighlightBackground", DEF_TV_FOCUS_HIGHLIGHT_BACKGROUND,
356 Blt_Offset(TreeView, highlightBgColor), 0},
357 {BLT_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
358 DEF_TV_FOCUS_HIGHLIGHT_COLOR, Blt_Offset(TreeView, highlightColor), 0},
359 {BLT_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
360 "HighlightThickness", DEF_TV_FOCUS_HIGHLIGHT_WIDTH,
361 Blt_Offset(TreeView, highlightWidth), BLT_CONFIG_DONT_SET_DEFAULT},
362 {BLT_CONFIG_CUSTOM, "-icons", "icons", "Icons",
363 DEF_TV_ICONS, Blt_Offset(TreeView, icons),
364 BLT_CONFIG_NULL_OK, &bltTreeViewIconsOption},
365 {BLT_CONFIG_BORDER, "-nofocusselectbackground", "noFocusSelectBackground",
366 "NoFocusSelectBackground", DEF_TV_SELECT_BACKGROUND,
367 Blt_Offset(TreeView, selOutFocusBorder), TK_CONFIG_NULL_OK},
368 {BLT_CONFIG_COLOR, "-nofocusselectforeground", "noFocusSelectForeground",
369 "NoFocusSelectForeground", DEF_TV_SELECT_FOREGROUND,
370 Blt_Offset(TreeView, selOutFocusFgColor), TK_CONFIG_NULL_OK},
371 {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
372 DEF_TV_VLINE_COLOR, Blt_Offset(TreeView, lineColor),
373 BLT_CONFIG_COLOR_ONLY},
374 {BLT_CONFIG_COLOR, "-linecolor", "lineColor", "LineColor",
375 DEF_TV_VLINE_MONO, Blt_Offset(TreeView, lineColor),
376 BLT_CONFIG_MONO_ONLY},
377 {BLT_CONFIG_DISTANCE, "-linespacing", "lineSpacing", "LineSpacing",
378 DEF_TV_LINESPACING, Blt_Offset(TreeView, leader),
379 BLT_CONFIG_DONT_SET_DEFAULT},
380 {BLT_CONFIG_DISTANCE, "-linewidth", "lineWidth", "LineWidth",
381 DEF_TV_LINEWIDTH, Blt_Offset(TreeView, lineWidth),
382 BLT_CONFIG_DONT_SET_DEFAULT},
383 {BLT_CONFIG_BITFLAG, "-newtags", "newTags", "NewTags",
384 DEF_TV_NEW_TAGS, Blt_Offset(TreeView, flags),
385 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_NEW_TAGS},
386 {BLT_CONFIG_STRING, "-opencommand", "openCommand", "OpenCommand",
387 (char *)NULL, Blt_Offset(TreeView, openCmd), BLT_CONFIG_NULL_OK},
388 {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
389 DEF_TV_RELIEF, Blt_Offset(TreeView, relief), 0},
390 {BLT_CONFIG_CURSOR, "-resizecursor", "resizeCursor", "ResizeCursor",
391 DEF_TV_RESIZE_CURSOR, Blt_Offset(TreeView, resizeCursor), 0},
392 {BLT_CONFIG_CUSTOM, "-scrollmode", "scrollMode", "ScrollMode",
393 DEF_TV_SCROLL_MODE, Blt_Offset(TreeView, scrollMode),
394 BLT_CONFIG_DONT_SET_DEFAULT, &scrollmodeOption},
395 {BLT_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
396 DEF_TV_SELECT_BACKGROUND, Blt_Offset(TreeView, selInFocusBorder), 0},
397 {BLT_CONFIG_DISTANCE,
398 "-selectborderwidth", "selectBorderWidth", "BorderWidth",
399 DEF_TV_SELECT_BORDERWIDTH, Blt_Offset(TreeView, selBorderWidth),
400 BLT_CONFIG_DONT_SET_DEFAULT},
401 {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
402 (char *)NULL, Blt_Offset(TreeView, selectCmd), BLT_CONFIG_NULL_OK},
403 {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
404 DEF_TV_SELECT_FOREGROUND, Blt_Offset(TreeView, selInFocusFgColor), 0},
405 {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
406 DEF_TV_SELECT_MODE, Blt_Offset(TreeView, selectMode),
407 BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
408 {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
409 DEF_TV_SELECT_RELIEF, Blt_Offset(TreeView, selRelief),
410 BLT_CONFIG_DONT_SET_DEFAULT},
411 {BLT_CONFIG_CUSTOM, "-separator", "separator", "Separator",
412 (char *)NULL, Blt_Offset(TreeView, pathSep), BLT_CONFIG_NULL_OK,
413 &separatorOption},
414 {BLT_CONFIG_BITFLAG, "-showtitles", "showTitles", "ShowTitles",
415 DEF_TV_SHOW_TITLES, Blt_Offset(TreeView, flags), 0,
416 (Blt_CustomOption *)TV_SHOW_COLUMN_TITLES},
417 {BLT_CONFIG_BITFLAG, "-sortselection", "sortSelection", "SortSelection",
418 DEF_TV_SORT_SELECTION, Blt_Offset(TreeView, flags),
419 BLT_CONFIG_DONT_SET_DEFAULT, (Blt_CustomOption *)TV_SELECT_SORTED},
420 {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
421 DEF_TV_TAKE_FOCUS, Blt_Offset(TreeView, takeFocus),
422 BLT_CONFIG_NULL_OK},
423 {BLT_CONFIG_CUSTOM, "-tree", "tree", "Tree",
424 (char *)NULL, Blt_Offset(TreeView, tree), BLT_CONFIG_NULL_OK,
425 &bltTreeViewTreeOption},
426 {BLT_CONFIG_STRING, "-trim", "trim", "Trim",
427 DEF_TV_TRIMLEFT, Blt_Offset(TreeView, trimLeft),
428 BLT_CONFIG_NULL_OK},
429 {BLT_CONFIG_DISTANCE, "-width", "width", "Width",
430 DEF_TV_WIDTH, Blt_Offset(TreeView, reqWidth),
431 BLT_CONFIG_DONT_SET_DEFAULT},
432 {BLT_CONFIG_STRING,
433 "-xscrollcommand", "xScrollCommand", "ScrollCommand",
434 (char *)NULL, Blt_Offset(TreeView, xScrollCmdPrefix),
435 BLT_CONFIG_NULL_OK},
436 {BLT_CONFIG_DISTANCE,
437 "-xscrollincrement", "xScrollIncrement", "ScrollIncrement",
438 DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, xScrollUnits),
439 BLT_CONFIG_DONT_SET_DEFAULT},
440 {BLT_CONFIG_STRING,
441 "-yscrollcommand", "yScrollCommand", "ScrollCommand",
442 (char *)NULL, Blt_Offset(TreeView, yScrollCmdPrefix),
443 BLT_CONFIG_NULL_OK},
444 {BLT_CONFIG_DISTANCE,
445 "-yscrollincrement", "yScrollIncrement", "ScrollIncrement",
446 DEF_TV_SCROLL_INCREMENT, Blt_Offset(TreeView, yScrollUnits),
447 BLT_CONFIG_DONT_SET_DEFAULT},
448 {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
449 (char *)NULL, 0, 0}
450};
451
452/* Forward Declarations */
453static Blt_TreeNotifyEventProc TreeEventProc;
454static Blt_TreeTraceProc TreeTraceProc;
455static Tcl_CmdDeleteProc WidgetInstCmdDeleteProc;
456static Tcl_FreeProc DestroyTreeView;
457static Tcl_IdleProc DisplayTreeView;
458static Tk_EventProc TreeViewEventProc;
459
460static int ComputeVisibleEntries _ANSI_ARGS_((TreeView *tvPtr));
461
462EXTERN int Blt_TreeCmdGetToken _ANSI_ARGS_((Tcl_Interp *interp,
463 CONST char *treeName, Blt_Tree *treePtr));
464
465extern Blt_TreeApplyProc Blt_TreeViewSortApplyProc;
466static Blt_BindPickProc PickItem;
467static Blt_BindTagProc GetTags;
468static Tcl_FreeProc DestroyEntry;
469static Tcl_ObjCmdProc TreeViewObjCmd;
470static Tk_ImageChangedProc IconChangedProc;
471static Tk_SelectionProc SelectionProc;
472
473/*
474 *----------------------------------------------------------------------
475 *
476 * Blt_TreeViewEventuallyRedraw --
477 *
478 * Queues a request to redraw the widget at the next idle point.
479 *
480 * Results:
481 * None.
482 *
483 * Side effects:
484 * Information gets redisplayed. Right now we don't do selective
485 * redisplays: the whole window will be redrawn.
486 *
487 *----------------------------------------------------------------------
488 */
489void
490Blt_TreeViewEventuallyRedraw(TreeView *tvPtr)
491{
492 if ((tvPtr->tkwin != NULL) && ((tvPtr->flags & TV_REDRAW) == 0)) {
493 tvPtr->flags |= TV_REDRAW;
494 Tcl_DoWhenIdle(DisplayTreeView, tvPtr);
495 }
496}
497
498void
499Blt_TreeViewTraceColumn(TreeView *tvPtr, TreeViewColumn *columnPtr)
500{
501 Blt_TreeCreateTrace(tvPtr->tree, NULL /* Node */, columnPtr->key, NULL,
502 TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET,
503 TreeTraceProc, tvPtr);
504}
505
506static void
507TraceColumns(TreeView *tvPtr)
508{
509 Blt_ChainLink *linkPtr;
510 TreeViewColumn *columnPtr;
511
512 for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
513 linkPtr = Blt_ChainNextLink(linkPtr)) {
514 columnPtr = Blt_ChainGetValue(linkPtr);
515 Blt_TreeCreateTrace(
516 tvPtr->tree,
517 NULL /* Node */,
518 columnPtr->key /* Key pattern */,
519 NULL /* Tag */,
520 TREE_TRACE_FOREIGN_ONLY | TREE_TRACE_WRITE | TREE_TRACE_UNSET,
521 TreeTraceProc /* Callback routine */,
522 tvPtr /* Client data */);
523 }
524}
525
526/*
527 *----------------------------------------------------------------------
528 *
529 * ObjToTree --
530 *
531 * Convert the string representing the name of a tree object
532 * into a tree token.
533 *
534 * Results:
535 * If the string is successfully converted, TCL_OK is returned.
536 * Otherwise, TCL_ERROR is returned and an error message is left
537 * in interpreter's result field.
538 *
539 *----------------------------------------------------------------------
540 */
541/*ARGSUSED*/
542static int
543ObjToTree(
544 ClientData clientData, /* Not used. */
545 Tcl_Interp *interp, /* Interpreter to send results back to */
546 Tk_Window tkwin, /* Not used. */
547 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
548 char *widgRec,
549 int offset)
550{
551 Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
552 Blt_Tree tree;
553 char *string;
554
555 tree = NULL;
556 string = Tcl_GetString(objPtr);
557 if ((string[0] != '\0') &&
558 (Blt_TreeGetToken(interp, string, &tree) != TCL_OK)) {
559 return TCL_ERROR;
560 }
561 *treePtr = tree;
562 return TCL_OK;
563}
564
565/*
566 *----------------------------------------------------------------------
567 *
568 * TreeToObj --
569 *
570 * Results:
571 * The string representation of the button boolean is returned.
572 *
573 *----------------------------------------------------------------------
574 */
575/*ARGSUSED*/
576static Tcl_Obj *
577TreeToObj(
578 ClientData clientData, /* Not used. */
579 Tcl_Interp *interp,
580 Tk_Window tkwin, /* Not used. */
581 char *widgRec,
582 int offset)
583{
584 Blt_Tree tree = *(Blt_Tree *)(widgRec + offset);
585
586 if (tree == NULL) {
587 return bltEmptyStringObjPtr;
588 } else {
589 return Tcl_NewStringObj(Blt_TreeName(tree), -1);
590 }
591}
592
593/*ARGSUSED*/
594static void
595FreeTree(
596 ClientData clientData,
597 Display *display, /* Not used. */
598 char *widgRec,
599 int offset)
600{
601 Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
602
603 if (*treePtr != NULL) {
604 Blt_TreeNode root;
605 TreeView *tvPtr = clientData;
606
607 /*
608 * Release the current tree, removing any entry fields.
609 */
610 root = Blt_TreeRootNode(*treePtr);
611 Blt_TreeApply(root, DeleteApplyProc, tvPtr);
612 Blt_TreeViewClearSelection(tvPtr);
613 Blt_TreeReleaseToken(*treePtr);
614 }
615}
616
617/*
618 *----------------------------------------------------------------------
619 *
620 * ObjToScrollmode --
621 *
622 * Convert the string reprsenting a scroll mode, to its numeric
623 * form.
624 *
625 * Results:
626 * If the string is successfully converted, TCL_OK is returned.
627 * Otherwise, TCL_ERROR is returned and an error message is left
628 * in interpreter's result field.
629 *
630 *----------------------------------------------------------------------
631 */
632/*ARGSUSED*/
633static int
634ObjToScrollmode(
635 ClientData clientData, /* Not used. */
636 Tcl_Interp *interp, /* Interpreter to send results back to */
637 Tk_Window tkwin, /* Not used. */
638 Tcl_Obj *objPtr, /* New legend position string */
639 char *widgRec,
640 int offset)
641{
642 char *string;
643 char c;
644 int *modePtr = (int *)(widgRec + offset);
645
646 string = Tcl_GetString(objPtr);
647 c = string[0];
648 if ((c == 'l') && (strcmp(string, "listbox") == 0)) {
649 *modePtr = BLT_SCROLL_MODE_LISTBOX;
650 } else if ((c == 't') && (strcmp(string, "treeview") == 0)) {
651 *modePtr = BLT_SCROLL_MODE_HIERBOX;
652 } else if ((c == 'h') && (strcmp(string, "hiertable") == 0)) {
653 *modePtr = BLT_SCROLL_MODE_HIERBOX;
654 } else if ((c == 'c') && (strcmp(string, "canvas") == 0)) {
655 *modePtr = BLT_SCROLL_MODE_CANVAS;
656 } else {
657 Tcl_AppendResult(interp, "bad scroll mode \"", string,
658 "\": should be \"treeview\", \"listbox\", or \"canvas\"",
659 (char *)NULL);
660 return TCL_ERROR;
661 }
662 return TCL_OK;
663}
664
665/*
666 *----------------------------------------------------------------------
667 *
668 * ScrollmodeToObj --
669 *
670 * Results:
671 * The string representation of the button boolean is returned.
672 *
673 *----------------------------------------------------------------------
674 */
675/*ARGSUSED*/
676static Tcl_Obj *
677ScrollmodeToObj(
678 ClientData clientData, /* Not used. */
679 Tcl_Interp *interp,
680 Tk_Window tkwin, /* Not used. */
681 char *widgRec,
682 int offset)
683{
684 int mode = *(int *)(widgRec + offset);
685
686 switch (mode) {
687 case BLT_SCROLL_MODE_LISTBOX:
688 return Tcl_NewStringObj("listbox", -1);
689 case BLT_SCROLL_MODE_HIERBOX:
690 return Tcl_NewStringObj("hierbox", -1);
691 case BLT_SCROLL_MODE_CANVAS:
692 return Tcl_NewStringObj("canvas", -1);
693 default:
694 return Tcl_NewStringObj("unknown scroll mode", -1);
695 }
696}
697
698/*
699 *----------------------------------------------------------------------
700 *
701 * ObjToSelectmode --
702 *
703 * Convert the string reprsenting a scroll mode, to its numeric
704 * form.
705 *
706 * Results:
707 * If the string is successfully converted, TCL_OK is returned.
708 * Otherwise, TCL_ERROR is returned and an error message is left
709 * in interpreter's result field.
710 *
711 *----------------------------------------------------------------------
712 */
713/*ARGSUSED*/
714static int
715ObjToSelectmode(
716 ClientData clientData, /* Not used. */
717 Tcl_Interp *interp, /* Interpreter to send results back to */
718 Tk_Window tkwin, /* Not used. */
719 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
720 char *widgRec,
721 int offset)
722{
723 char *string;
724 char c;
725 int *modePtr = (int *)(widgRec + offset);
726
727 string = Tcl_GetString(objPtr);
728 c = string[0];
729 if ((c == 's') && (strcmp(string, "single") == 0)) {
730 *modePtr = SELECT_MODE_SINGLE;
731 } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
732 *modePtr = SELECT_MODE_MULTIPLE;
733 } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
734 *modePtr = SELECT_MODE_SINGLE;
735 } else {
736 Tcl_AppendResult(interp, "bad select mode \"", string,
737 "\": should be \"single\" or \"multiple\"", (char *)NULL);
738 return TCL_ERROR;
739 }
740 return TCL_OK;
741}
742
743/*
744 *----------------------------------------------------------------------
745 *
746 * SelectmodeToObj --
747 *
748 * Results:
749 * The string representation of the button boolean is returned.
750 *
751 *----------------------------------------------------------------------
752 */
753/*ARGSUSED*/
754static Tcl_Obj *
755SelectmodeToObj(
756 ClientData clientData, /* Not used. */
757 Tcl_Interp *interp,
758 Tk_Window tkwin, /* Not used. */
759 char *widgRec,
760 int offset)
761{
762 int mode = *(int *)(widgRec + offset);
763
764 switch (mode) {
765 case SELECT_MODE_SINGLE:
766 return Tcl_NewStringObj("single", -1);
767 case SELECT_MODE_MULTIPLE:
768 return Tcl_NewStringObj("multiple", -1);
769 default:
770 return Tcl_NewStringObj("unknown scroll mode", -1);
771 }
772}
773
774
775/*
776 *----------------------------------------------------------------------
777 *
778 * ObjToButton --
779 *
780 * Convert a string to one of three values.
781 * 0 - false, no, off
782 * 1 - true, yes, on
783 * 2 - auto
784 * Results:
785 * If the string is successfully converted, TCL_OK is returned.
786 * Otherwise, TCL_ERROR is returned and an error message is left in
787 * interpreter's result field.
788 *
789 *----------------------------------------------------------------------
790 */
791/*ARGSUSED*/
792static int
793ObjToButton(
794 ClientData clientData, /* Not used. */
795 Tcl_Interp *interp, /* Interpreter to send results back to */
796 Tk_Window tkwin, /* Not used. */
797 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
798 char *widgRec,
799 int offset)
800{
801 char *string;
802 int *flagsPtr = (int *)(widgRec + offset);
803
804 string = Tcl_GetString(objPtr);
805 if ((string[0] == 'a') && (strcmp(string, "auto") == 0)) {
806 *flagsPtr &= ~BUTTON_MASK;
807 *flagsPtr |= BUTTON_AUTO;
808 } else {
809 int bool;
810
811 if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
812 return TCL_ERROR;
813 }
814 *flagsPtr &= ~BUTTON_MASK;
815 if (bool) {
816 *flagsPtr |= BUTTON_SHOW;
817 }
818 }
819 return TCL_OK;
820}
821
822/*
823 *----------------------------------------------------------------------
824 *
825 * ButtonToObj --
826 *
827 * Results:
828 * The string representation of the button boolean is returned.
829 *
830 *----------------------------------------------------------------------
831 */
832/*ARGSUSED*/
833static Tcl_Obj *
834ButtonToObj(
835 ClientData clientData, /* Not used. */
836 Tcl_Interp *interp,
837 Tk_Window tkwin, /* Not used. */
838 char *widgRec,
839 int offset)
840{
841 int bool;
842 unsigned int flags = *(int *)(widgRec + offset);
843
844 bool = (flags & BUTTON_MASK);
845 if (bool == BUTTON_AUTO) {
846 return Tcl_NewStringObj("auto", 4);
847 } else {
848 return Tcl_NewBooleanObj(bool);
849 }
850}
851
852/*
853 *----------------------------------------------------------------------
854 *
855 * ObjToScrollmode --
856 *
857 * Convert the string reprsenting a scroll mode, to its numeric
858 * form.
859 *
860 * Results:
861 * If the string is successfully converted, TCL_OK is returned.
862 * Otherwise, TCL_ERROR is returned and an error message is left
863 * in interpreter's result field.
864 *
865 *----------------------------------------------------------------------
866 */
867/*ARGSUSED*/
868static int
869ObjToSeparator(clientData, interp, tkwin, objPtr, widgRec, offset)
870 ClientData clientData; /* Not used. */
871 Tcl_Interp *interp; /* Interpreter to send results back to */
872 Tk_Window tkwin; /* Not used. */
873 Tcl_Obj *objPtr; /* Tcl_Obj representing the new value. */
874 char *widgRec;
875 int offset;
876{
877 char **sepPtr = (char **)(widgRec + offset);
878 char *string;
879
880 string = Tcl_GetString(objPtr);
881 if (*string == '\0') {
882 *sepPtr = SEPARATOR_LIST;
883 } else if (strcmp(string, "none") == 0) {
884 *sepPtr = SEPARATOR_NONE;
885 } else {
886 *sepPtr = Blt_Strdup(string);
887 }
888 return TCL_OK;
889}
890
891/*
892 *----------------------------------------------------------------------
893 *
894 * SeparatorToObj --
895 *
896 * Results:
897 * The string representation of the separator is returned.
898 *
899 *----------------------------------------------------------------------
900 */
901/*ARGSUSED*/
902static Tcl_Obj *
903SeparatorToObj(
904 ClientData clientData, /* Not used. */
905 Tcl_Interp *interp,
906 Tk_Window tkwin, /* Not used. */
907 char *widgRec,
908 int offset)
909{
910 char *separator = *(char **)(widgRec + offset);
911
912 if (separator == SEPARATOR_NONE) {
913 return bltEmptyStringObjPtr;
914 } else if (separator == SEPARATOR_LIST) {
915 return Tcl_NewStringObj("list", -1);
916 } else {
917 return Tcl_NewStringObj(separator, -1);
918 }
919}
920
921/*
922 *----------------------------------------------------------------------
923 *
924 * FreeSeparator --
925 *
926 * Free the UID from the widget record, setting it to NULL.
927 *
928 * Results:
929 * The UID in the widget record is set to NULL.
930 *
931 *----------------------------------------------------------------------
932 */
933/*ARGSUSED*/
934static void
935FreeSeparator(
936 ClientData clientData,
937 Display *display, /* Not used. */
938 char *widgRec,
939 int offset)
940{
941 char *separator = *(char **)(widgRec + offset);
942
943 if ((separator != SEPARATOR_LIST) && (separator != SEPARATOR_NONE)) {
944 Blt_Free(separator);
945 }
946}
947
948/*
949 *----------------------------------------------------------------------
950 *
951 * ObjToLabel --
952 *
953 * Convert the string representing the label.
954 *
955 * Results:
956 * If the string is successfully converted, TCL_OK is returned.
957 * Otherwise, TCL_ERROR is returned and an error message is left
958 * in interpreter's result field.
959 *
960 *----------------------------------------------------------------------
961 */
962/*ARGSUSED*/
963static int
964ObjToLabel(
965 ClientData clientData, /* Not used. */
966 Tcl_Interp *interp, /* Interpreter to send results back to */
967 Tk_Window tkwin, /* Not used. */
968 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
969 char *widgRec,
970 int offset)
971{
972 UID *labelPtr = (UID *)(widgRec + offset);
973 char *string;
974
975 string = Tcl_GetString(objPtr);
976 if (string[0] != '\0') {
977 TreeView *tvPtr = clientData;
978
979 *labelPtr = Blt_TreeViewGetUid(tvPtr, string);
980 }
981 return TCL_OK;
982}
983
984/*
985 *----------------------------------------------------------------------
986 *
987 * TreeToObj --
988 *
989 * Results:
990 * The string of the entry's label is returned.
991 *
992 *----------------------------------------------------------------------
993 */
994/*ARGSUSED*/
995static Tcl_Obj *
996LabelToObj(
997 ClientData clientData, /* Not used. */
998 Tcl_Interp *interp,
999 Tk_Window tkwin, /* Not used. */
1000 char *widgRec,
1001 int offset)
1002{
1003 UID labelUid = *(UID *)(widgRec + offset);
1004 char *string;
1005
1006 if (labelUid == NULL) {
1007 TreeViewEntry *entryPtr = (TreeViewEntry *)widgRec;
1008
1009 string = Blt_TreeNodeLabel(entryPtr->node);
1010 } else {
1011 string = labelUid;
1012 }
1013 return Tcl_NewStringObj(string, -1);
1014}
1015
1016/*ARGSUSED*/
1017static void
1018FreeLabel(
1019 ClientData clientData,
1020 Display *display, /* Not used. */
1021 char *widgRec,
1022 int offset)
1023{
1024 UID *labelPtr = (UID *)(widgRec + offset);
1025
1026 if (*labelPtr != NULL) {
1027 TreeView *tvPtr = clientData;
1028
1029 Blt_TreeViewFreeUid(tvPtr, *labelPtr);
1030 }
1031}
1032
1033/*
1034 *----------------------------------------------------------------------
1035 *
1036 * Blt_TreeViewGetUid --
1037 *
1038 * Gets or creates a unique string identifier. Strings are
1039 * reference counted. The string is placed into a hashed table
1040 * local to the treeview.
1041 *
1042 * Results:
1043 * Returns the pointer to the hashed string.
1044 *
1045 *----------------------------------------------------------------------
1046 */
1047UID
1048Blt_TreeViewGetUid(TreeView *tvPtr, CONST char *string)
1049{
1050 Blt_HashEntry *hPtr;
1051 int isNew;
1052 int refCount;
1053
1054 hPtr = Blt_CreateHashEntry(&tvPtr->uidTable, string, &isNew);
1055 if (isNew) {
1056 refCount = 1;
1057 } else {
1058 refCount = (int)Blt_GetHashValue(hPtr);
1059 refCount++;
1060 }
1061 Blt_SetHashValue(hPtr, (ClientData)refCount);
1062 return Blt_GetHashKey(&tvPtr->uidTable, hPtr);
1063}
1064
1065/*
1066 *----------------------------------------------------------------------
1067 *
1068 * Blt_TreeViewFreeUid --
1069 *
1070 * Releases the uid. Uids are reference counted, so only when
1071 * the reference count is zero (i.e. no one else is using the
1072 * string) is the entry removed from the hash table.
1073 *
1074 * Results:
1075 * None.
1076 *
1077 *----------------------------------------------------------------------
1078 */
1079void
1080Blt_TreeViewFreeUid(TreeView *tvPtr, UID uid)
1081{
1082 Blt_HashEntry *hPtr;
1083 int refCount;
1084
1085 hPtr = Blt_FindHashEntry(&tvPtr->uidTable, uid);
1086 assert(hPtr != NULL);
1087 refCount = (int)Blt_GetHashValue(hPtr);
1088 refCount--;
1089 if (refCount > 0) {
1090 Blt_SetHashValue(hPtr, (ClientData)refCount);
1091 } else {
1092 Blt_DeleteHashEntry(&tvPtr->uidTable, hPtr);
1093 }
1094}
1095
1096/*
1097 *----------------------------------------------------------------------
1098 *
1099 * ObjToUid --
1100 *
1101 * Converts the string to a Uid. Uid's are hashed, reference
1102 * counted strings.
1103 *
1104 *----------------------------------------------------------------------
1105 */
1106/*ARGSUSED*/
1107static int
1108ObjToUid(
1109 ClientData clientData, /* Not used. */
1110 Tcl_Interp *interp, /* Interpreter to send results back to */
1111 Tk_Window tkwin, /* Not used. */
1112 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
1113 char *widgRec,
1114 int offset)
1115{
1116 TreeView *tvPtr = clientData;
1117 UID *uidPtr = (UID *)(widgRec + offset);
1118 UID newId;
1119 char *string;
1120
1121 newId = NULL;
1122 string = Tcl_GetString(objPtr);
1123 if (*string != '\0') {
1124 newId = Blt_TreeViewGetUid(tvPtr, string);
1125 }
1126 *uidPtr = newId;
1127 return TCL_OK;
1128}
1129
1130/*
1131 *----------------------------------------------------------------------
1132 *
1133 * UidToObj --
1134 *
1135 * Returns the uid as a string.
1136 *
1137 * Results:
1138 * The fill style string is returned.
1139 *
1140 *----------------------------------------------------------------------
1141 */
1142/*ARGSUSED*/
1143static Tcl_Obj *
1144UidToObj(
1145 ClientData clientData, /* Not used. */
1146 Tcl_Interp *interp,
1147 Tk_Window tkwin, /* Not used. */
1148 char *widgRec,
1149 int offset)
1150{
1151 UID uid = *(UID *)(widgRec + offset);
1152
1153 if (uid == NULL) {
1154 return bltEmptyStringObjPtr;
1155 }
1156 return Tcl_NewStringObj(uid, -1);
1157}
1158
1159/*
1160 *----------------------------------------------------------------------
1161 *
1162 * FreeUid --
1163 *
1164 * Free the UID from the widget record, setting it to NULL.
1165 *
1166 * Results:
1167 * The UID in the widget record is set to NULL.
1168 *
1169 *----------------------------------------------------------------------
1170 */
1171/*ARGSUSED*/
1172static void
1173FreeUid(
1174 ClientData clientData,
1175 Display *display, /* Not used. */
1176 char *widgRec,
1177 int offset)
1178{
1179 UID *uidPtr = (UID *)(widgRec + offset);
1180
1181 if (*uidPtr != NULL) {
1182 TreeView *tvPtr = clientData;
1183
1184 Blt_TreeViewFreeUid(tvPtr, *uidPtr);
1185 }
1186}
1187
1188/*
1189 *----------------------------------------------------------------------
1190 *
1191 * IconChangedProc
1192 *
1193 *
1194 * Results:
1195 * None.
1196 *
1197 *----------------------------------------------------------------------
1198 */
1199/* ARGSUSED */
1200static void
1201IconChangedProc(
1202 ClientData clientData,
1203 int x, /* Not used. */
1204 int y, /* Not used. */
1205 int width, /* Not used. */
1206 int height, /* Not used. */
1207 int imageWidth, /* Not used. */
1208 int imageHeight) /* Not used. */
1209{
1210 TreeView *tvPtr = clientData;
1211
1212 tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL);
1213 Blt_TreeViewEventuallyRedraw(tvPtr);
1214}
1215
1216TreeViewIcon
1217Blt_TreeViewGetIcon(TreeView *tvPtr, CONST char *iconName)
1218{
1219 Blt_HashEntry *hPtr;
1220 int isNew;
1221 struct TreeViewIconStruct *iconPtr;
1222
1223 hPtr = Blt_CreateHashEntry(&tvPtr->iconTable, iconName, &isNew);
1224 if (isNew) {
1225 Tk_Image tkImage;
1226 int width, height;
1227
1228 tkImage = Tk_GetImage(tvPtr->interp, tvPtr->tkwin, (char *)iconName,
1229 IconChangedProc, tvPtr);
1230 if (tkImage == NULL) {
1231 Blt_DeleteHashEntry(&tvPtr->iconTable, hPtr);
1232 return NULL;
1233 }
1234 Tk_SizeOfImage(tkImage, &width, &height);
1235 iconPtr = Blt_Malloc(sizeof(struct TreeViewIconStruct));
1236 iconPtr->tkImage = tkImage;
1237 iconPtr->hashPtr = hPtr;
1238 iconPtr->refCount = 1;
1239 iconPtr->width = width;
1240 iconPtr->height = height;
1241 Blt_SetHashValue(hPtr, iconPtr);
1242 } else {
1243 iconPtr = Blt_GetHashValue(hPtr);
1244 iconPtr->refCount++;
1245 }
1246 return iconPtr;
1247}
1248
1249void
1250Blt_TreeViewFreeIcon(
1251 TreeView *tvPtr,
1252 struct TreeViewIconStruct *iconPtr)
1253{
1254 iconPtr->refCount--;
1255 if (iconPtr->refCount == 0) {
1256 Blt_DeleteHashEntry(&tvPtr->iconTable, iconPtr->hashPtr);
1257 Tk_FreeImage(iconPtr->tkImage);
1258 Blt_Free(iconPtr);
1259 }
1260}
1261
1262static void
1263DumpIconTable(TreeView *tvPtr)
1264{
1265 Blt_HashEntry *hPtr;
1266 Blt_HashSearch cursor;
1267 struct TreeViewIconStruct *iconPtr;
1268
1269 for (hPtr = Blt_FirstHashEntry(&tvPtr->iconTable, &cursor);
1270 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1271 iconPtr = Blt_GetHashValue(hPtr);
1272 Tk_FreeImage(iconPtr->tkImage);
1273 Blt_Free(iconPtr);
1274 }
1275 Blt_DeleteHashTable(&tvPtr->iconTable);
1276}
1277
1278/*
1279 *----------------------------------------------------------------------
1280 *
1281 * ObjToIcons --
1282 *
1283 * Convert a list of image names into Tk images.
1284 *
1285 * Results:
1286 * If the string is successfully converted, TCL_OK is returned.
1287 * Otherwise, TCL_ERROR is returned and an error message is left in
1288 * interpreter's result field.
1289 *
1290 *----------------------------------------------------------------------
1291 */
1292/*ARGSUSED*/
1293static int
1294ObjToIcons(
1295 ClientData clientData, /* Not used. */
1296 Tcl_Interp *interp, /* Interpreter to send results back to */
1297 Tk_Window tkwin, /* Not used. */
1298 Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
1299 char *widgRec,
1300 int offset)
1301{
1302 Tcl_Obj **objv;
1303 TreeView *tvPtr = clientData;
1304 TreeViewIcon **iconPtrPtr = (TreeViewIcon **)(widgRec + offset);
1305 TreeViewIcon *icons;
1306 int objc;
1307 int result;
1308
1309 result = TCL_OK;
1310 icons = NULL;
1311 if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1312 return TCL_ERROR;
1313 }
1314 if (objc > 0) {
1315 register int i;
1316
1317 icons = Blt_Malloc(sizeof(TreeViewIcon *) * (objc + 1));
1318 assert(icons);
1319 for (i = 0; i < objc; i++) {
1320 icons[i] = Blt_TreeViewGetIcon(tvPtr, Tcl_GetString(objv[i]));
1321 if (icons[i] == NULL) {
1322 result = TCL_ERROR;
1323 break;
1324 }
1325 }
1326 icons[i] = NULL;
1327 }
1328 *iconPtrPtr = icons;
1329 return result;
1330}
1331
1332/*
1333 *----------------------------------------------------------------------
1334 *
1335 * IconsToObj --
1336 *
1337 * Converts the icon into its string representation (its name).
1338 *
1339 * Results:
1340 * The name of the icon is returned.
1341 *
1342 *----------------------------------------------------------------------
1343 */
1344/*ARGSUSED*/
1345static Tcl_Obj *
1346IconsToObj(
1347 ClientData clientData, /* Not used. */
1348 Tcl_Interp *interp,
1349 Tk_Window tkwin, /* Not used. */
1350 char *widgRec,
1351 int offset)
1352{
1353 TreeViewIcon *icons = *(TreeViewIcon **)(widgRec + offset);
1354 Tcl_Obj *listObjPtr;
1355
1356 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1357 if (icons != NULL) {
1358 register TreeViewIcon *iconPtr;
1359 Tcl_Obj *objPtr;
1360
1361 for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
1362 objPtr = Tcl_NewStringObj(Blt_NameOfImage((*iconPtr)->tkImage), -1);
1363 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1364
1365 }
1366 }
1367 return listObjPtr;
1368}
1369
1370/*ARGSUSED*/
1371static void
1372FreeIcons(
1373 ClientData clientData,
1374 Display *display, /* Not used. */
1375 char *widgRec,
1376 int offset)
1377{
1378 TreeViewIcon *icons = *(TreeViewIcon **)(widgRec + offset);
1379
1380 if (icons != NULL) {
1381 register TreeViewIcon *iconPtr;
1382 TreeView *tvPtr = clientData;
1383
1384 for (iconPtr = icons; *iconPtr != NULL; iconPtr++) {
1385 Blt_TreeViewFreeIcon(tvPtr, *iconPtr);
1386 }
1387 Blt_Free(icons);
1388 }
1389}
1390
1391TreeViewEntry *
1392Blt_NodeToEntry(TreeView *tvPtr, Blt_TreeNode node)
1393{
1394 Blt_HashEntry *hPtr;
1395
1396 hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
1397 if (hPtr == NULL) {
1398 abort();
1399 return NULL;
1400 }
1401 return Blt_GetHashValue(hPtr);
1402}
1403
1404int
1405Blt_TreeViewApply(
1406 TreeView *tvPtr,
1407 TreeViewEntry *entryPtr, /* Root entry of subtree. */
1408 TreeViewApplyProc *proc, /* Procedure to call for each entry. */
1409 unsigned int flags)
1410{
1411 if ((flags & ENTRY_HIDDEN) &&
1412 (Blt_TreeViewEntryIsHidden(entryPtr))) {
1413 return TCL_OK; /* Hidden node. */
1414 }
1415 if ((flags & ENTRY_HIDDEN) && (entryPtr->flags & ENTRY_HIDDEN)) {
1416 return TCL_OK; /* Hidden node. */
1417 }
1418 if (((flags & ENTRY_CLOSED) == 0) ||
1419 ((entryPtr->flags & ENTRY_CLOSED) == 0)) {
1420 TreeViewEntry *childPtr;
1421 Blt_TreeNode node, next;
1422
1423 for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
1424 node = next) {
1425 next = Blt_TreeNextSibling(node);
1426 /*
1427 * Get the next child before calling Blt_TreeViewApply
1428 * recursively. This is because the apply callback may
1429 * delete the node and its link.
1430 */
1431 childPtr = Blt_NodeToEntry(tvPtr, node);
1432 if (Blt_TreeViewApply(tvPtr, childPtr, proc, flags) != TCL_OK) {
1433 return TCL_ERROR;
1434 }
1435 }
1436 }
1437 if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
1438 return TCL_ERROR;
1439 }
1440 return TCL_OK;
1441}
1442
1443int
1444Blt_TreeViewEntryIsHidden(TreeViewEntry *entryPtr)
1445{
1446 TreeView *tvPtr = entryPtr->tvPtr;
1447
1448 if ((tvPtr->flags & TV_HIDE_LEAVES) && (Blt_TreeIsLeaf(entryPtr->node))) {
1449 return TRUE;
1450 }
1451 return (entryPtr->flags & ENTRY_HIDDEN) ? TRUE : FALSE;
1452}
1453
1454#ifdef notdef
1455int
1456Blt_TreeViewEntryIsMapped(TreeViewEntry *entryPtr)
1457{
1458 TreeView *tvPtr = entryPtr->tvPtr;
1459
1460 /* Don't check if the entry itself is open, only that its
1461 * ancestors are. */
1462 if (Blt_TreeViewEntryIsHidden(entryPtr)) {
1463 return FALSE;
1464 }
1465 if (entryPtr == tvPtr->rootPtr) {
1466 return TRUE;
1467 }
1468 entryPtr = Blt_TreeViewParentEntry(entryPtr);
1469 while (entryPtr != tvPtr->rootPtr) {
1470 if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
1471 return FALSE;
1472 }
1473 entryPtr = Blt_TreeViewParentEntry(entryPtr);
1474 }
1475 return TRUE;
1476}
1477#endif
1478
1479TreeViewEntry *
1480Blt_TreeViewFirstChild(TreeViewEntry *entryPtr, unsigned int mask)
1481{
1482 Blt_TreeNode node;
1483 TreeView *tvPtr = entryPtr->tvPtr;
1484
1485 for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
1486 node = Blt_TreeNextSibling(node)) {
1487 entryPtr = Blt_NodeToEntry(tvPtr, node);
1488 if (((mask & ENTRY_HIDDEN) == 0) ||
1489 (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1490 return entryPtr;
1491 }
1492 }
1493 return NULL;
1494}
1495
1496TreeViewEntry *
1497Blt_TreeViewLastChild(TreeViewEntry *entryPtr, unsigned int mask)
1498{
1499 Blt_TreeNode node;
1500 TreeView *tvPtr = entryPtr->tvPtr;
1501
1502 for (node = Blt_TreeLastChild(entryPtr->node); node != NULL;
1503 node = Blt_TreePrevSibling(node)) {
1504 entryPtr = Blt_NodeToEntry(tvPtr, node);
1505 if (((mask & ENTRY_HIDDEN) == 0) ||
1506 (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1507 return entryPtr;
1508 }
1509 }
1510 return NULL;
1511}
1512
1513TreeViewEntry *
1514Blt_TreeViewNextSibling(TreeViewEntry *entryPtr, unsigned int mask)
1515{
1516 Blt_TreeNode node;
1517 TreeView *tvPtr = entryPtr->tvPtr;
1518
1519 for (node = Blt_TreeNextSibling(entryPtr->node); node != NULL;
1520 node = Blt_TreeNextSibling(node)) {
1521 entryPtr = Blt_NodeToEntry(tvPtr, node);
1522 if (((mask & ENTRY_HIDDEN) == 0) ||
1523 (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1524 return entryPtr;
1525 }
1526 }
1527 return NULL;
1528}
1529
1530TreeViewEntry *
1531Blt_TreeViewPrevSibling(TreeViewEntry *entryPtr, unsigned int mask)
1532{
1533 Blt_TreeNode node;
1534 TreeView *tvPtr = entryPtr->tvPtr;
1535
1536 for (node = Blt_TreePrevSibling(entryPtr->node); node != NULL;
1537 node = Blt_TreePrevSibling(node)) {
1538 entryPtr = Blt_NodeToEntry(tvPtr, node);
1539 if (((mask & ENTRY_HIDDEN) == 0) ||
1540 (!Blt_TreeViewEntryIsHidden(entryPtr))) {
1541 return entryPtr;
1542 }
1543 }
1544 return NULL;
1545}
1546
1547/*
1548 *----------------------------------------------------------------------
1549 *
1550 * Blt_TreeViewPrevEntry --
1551 *
1552 * Returns the "previous" node in the tree. This node (in
1553 * depth-first order) is its parent if the node has no siblings
1554 * that are previous to it. Otherwise it is the last descendant
1555 * of the last sibling. In this case, descend the sibling's
1556 * hierarchy, using the last child at any ancestor, until we
1557 * we find a leaf.
1558 *
1559 *----------------------------------------------------------------------
1560 */
1561TreeViewEntry *
1562Blt_TreeViewPrevEntry(TreeViewEntry *entryPtr, unsigned int mask)
1563{
1564 TreeView *tvPtr = entryPtr->tvPtr;
1565 TreeViewEntry *prevPtr;
1566
1567 if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
1568 return NULL; /* The root is the first node. */
1569 }
1570 prevPtr = Blt_TreeViewPrevSibling(entryPtr, mask);
1571 if (prevPtr == NULL) {
1572 /* There are no siblings previous to this one, so pick the parent. */
1573 prevPtr = Blt_TreeViewParentEntry(entryPtr);
1574 } else {
1575 /*
1576 * Traverse down the right-most thread in order to select the
1577 * last entry. Stop if we find a "closed" entry or reach a leaf.
1578 */
1579 entryPtr = prevPtr;
1580 while ((entryPtr->flags & mask) == 0) {
1581 entryPtr = Blt_TreeViewLastChild(entryPtr, mask);
1582 if (entryPtr == NULL) {
1583 break; /* Found a leaf. */
1584 }
1585 prevPtr = entryPtr;
1586 }
1587 }
1588 if (prevPtr == NULL) {
1589 return NULL;
1590 }
1591 return prevPtr;
1592}
1593
1594
1595/*
1596 *----------------------------------------------------------------------
1597 *
1598 * Blt_TreeViewNextNode --
1599 *
1600 * Returns the "next" node in relation to the given node.
1601 * The next node (in depth-first order) is either the first
1602 * child of the given node the next sibling if the node has
1603 * no children (the node is a leaf). If the given node is the
1604 * last sibling, then try it's parent next sibling. Continue
1605 * until we either find a next sibling for some ancestor or
1606 * we reach the root node. In this case the current node is
1607 * the last node in the tree.
1608 *
1609 *----------------------------------------------------------------------
1610 */
1611TreeViewEntry *
1612Blt_TreeViewNextEntry(TreeViewEntry *entryPtr, unsigned int mask)
1613{
1614 TreeView *tvPtr = entryPtr->tvPtr;
1615 TreeViewEntry *nextPtr;
1616 int ignoreLeaf;
1617
1618 ignoreLeaf = ((tvPtr->flags & TV_HIDE_LEAVES) &&
1619 (Blt_TreeIsLeaf(entryPtr->node)));
1620
1621 if ((!ignoreLeaf) && ((entryPtr->flags & mask) == 0)) {
1622 nextPtr = Blt_TreeViewFirstChild(entryPtr, mask);
1623 if (nextPtr != NULL) {
1624 return nextPtr; /* Pick the first sub-node. */
1625 }
1626 }
1627
1628
1629 /*
1630 * Back up until to a level where we can pick a "next sibling".
1631 * For the last entry we'll thread our way back to the root.
1632 */
1633
1634 while (entryPtr != tvPtr->rootPtr) {
1635 nextPtr = Blt_TreeViewNextSibling(entryPtr, mask);
1636 if (nextPtr != NULL) {
1637 return nextPtr;
1638 }
1639 entryPtr = Blt_TreeViewParentEntry(entryPtr);
1640 }
1641 return NULL; /* At root, no next node. */
1642}
1643
1644void
1645Blt_TreeViewConfigureButtons(TreeView *tvPtr)
1646{
1647 GC newGC;
1648 TreeViewButton *buttonPtr = &tvPtr->button;
1649 XGCValues gcValues;
1650 unsigned long gcMask;
1651
1652 gcMask = GCForeground;
1653 gcValues.foreground = buttonPtr->fgColor->pixel;
1654 newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
1655 if (buttonPtr->normalGC != NULL) {
1656 Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
1657 }
1658 buttonPtr->normalGC = newGC;
1659
1660 gcMask = GCForeground;
1661 gcValues.foreground = buttonPtr->activeFgColor->pixel;
1662 newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
1663 if (buttonPtr->activeGC != NULL) {
1664 Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
1665 }
1666 buttonPtr->activeGC = newGC;
1667
1668 buttonPtr->width = buttonPtr->height = ODD(buttonPtr->reqSize);
1669 if (buttonPtr->icons != NULL) {
1670 register int i;
1671 int width, height;
1672
1673 for (i = 0; i < 2; i++) {
1674 if (buttonPtr->icons[i] == NULL) {
1675 break;
1676 }
1677 width = TreeViewIconWidth(buttonPtr->icons[i]);
1678 height = TreeViewIconWidth(buttonPtr->icons[i]);
1679 if (buttonPtr->width < width) {
1680 buttonPtr->width = width;
1681 }
1682 if (buttonPtr->height < height) {
1683 buttonPtr->height = height;
1684 }
1685 }
1686 }
1687 buttonPtr->width += 2 * buttonPtr->borderWidth;
1688 buttonPtr->height += 2 * buttonPtr->borderWidth;
1689}
1690
1691int
1692Blt_TreeViewConfigureEntry(
1693 TreeView *tvPtr,
1694 TreeViewEntry *entryPtr,
1695 int objc,
1696 Tcl_Obj *CONST *objv,
1697 int flags)
1698{
1699 GC newGC;
1700 Blt_ChainLink *linkPtr;
1701 TreeViewColumn *columnPtr;
1702
1703 bltTreeViewIconsOption.clientData = tvPtr;
1704 bltTreeViewUidOption.clientData = tvPtr;
1705 labelOption.clientData = tvPtr;
1706 if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
1707 bltTreeViewEntrySpecs, objc, objv, (char *)entryPtr, flags) != TCL_OK) {
1708 return TCL_ERROR;
1709 }
1710 /*
1711 * Check if there are values that need to be added
1712 */
1713 for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
1714 linkPtr = Blt_ChainNextLink(linkPtr)) {
1715 columnPtr = Blt_ChainGetValue(linkPtr);
1716 Blt_TreeViewAddValue(entryPtr, columnPtr);
1717 }
1718
1719 newGC = NULL;
1720 if ((entryPtr->font != NULL) || (entryPtr->color != NULL)) {
1721 Tk_Font font;
1722 XColor *colorPtr;
1723 XGCValues gcValues;
1724 unsigned long gcMask;
1725
1726 font = entryPtr->font;
1727 if (font == NULL) {
1728 font = Blt_TreeViewGetStyleFont(tvPtr, tvPtr->treeColumn.stylePtr);
1729 }
1730 colorPtr = CHOOSE(tvPtr->fgColor, entryPtr->color);
1731 gcMask = GCForeground | GCFont;
1732 gcValues.foreground = colorPtr->pixel;
1733 gcValues.font = Tk_FontId(font);
1734 newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
1735 }
1736 if (entryPtr->gc != NULL) {
1737 Tk_FreeGC(tvPtr->display, entryPtr->gc);
1738 }
1739 /* Assume all changes require a new layout. */
1740 entryPtr->gc = newGC;
1741 entryPtr->flags |= ENTRY_LAYOUT_PENDING;
1742 if (Blt_ObjConfigModified(bltTreeViewEntrySpecs, "-font", (char *)NULL)) {
1743 tvPtr->flags |= TV_UPDATE;
1744 }
1745 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1746 return TCL_OK;
1747}
1748
1749void
1750Blt_TreeViewDestroyValue(TreeView *tvPtr, TreeViewValue *valuePtr)
1751{
1752 if (valuePtr->stylePtr != NULL) {
1753 Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
1754 }
1755 if (valuePtr->textPtr != NULL) {
1756 Blt_Free(valuePtr->textPtr);
1757 }
1758}
1759
1760
1761static void
1762DestroyEntry(DestroyData data)
1763{
1764 TreeViewEntry *entryPtr = (TreeViewEntry *)data;
1765 TreeView *tvPtr;
1766
1767 tvPtr = entryPtr->tvPtr;
1768 bltTreeViewIconsOption.clientData = tvPtr;
1769 bltTreeViewUidOption.clientData = tvPtr;
1770 labelOption.clientData = tvPtr;
1771 Blt_FreeObjOptions(bltTreeViewEntrySpecs, (char *)entryPtr, tvPtr->display,
1772 0);
1773 if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
1774 /* Don't clear tags unless this client is the only one using
1775 * the tag table.*/
1776 Blt_TreeClearTags(tvPtr->tree, entryPtr->node);
1777 }
1778 if (entryPtr->gc != NULL) {
1779 Tk_FreeGC(tvPtr->display, entryPtr->gc);
1780 }
1781 if (entryPtr->shadow.color != NULL) {
1782 Tk_FreeColor(entryPtr->shadow.color);
1783 }
1784 /* Delete the chain of data values from the entry. */
1785 if (entryPtr->values != NULL) {
1786 TreeViewValue *valuePtr, *nextPtr;
1787
1788 for (valuePtr = entryPtr->values; valuePtr != NULL;
1789 valuePtr = nextPtr) {
1790 nextPtr = valuePtr->nextPtr;
1791 Blt_TreeViewDestroyValue(tvPtr, valuePtr);
1792 }
1793 entryPtr->values = NULL;
1794 }
1795 if (entryPtr->fullName != NULL) {
1796 Blt_Free(entryPtr->fullName);
1797 }
1798 if (entryPtr->textPtr != NULL) {
1799 Blt_Free(entryPtr->textPtr);
1800 }
1801 Blt_PoolFreeItem(tvPtr->entryPool, entryPtr);
1802}
1803
1804TreeViewEntry *
1805Blt_TreeViewParentEntry(TreeViewEntry *entryPtr)
1806{
1807 TreeView *tvPtr = entryPtr->tvPtr;
1808 Blt_TreeNode node;
1809
1810 if (entryPtr->node == Blt_TreeRootNode(tvPtr->tree)) {
1811 return NULL;
1812 }
1813 node = Blt_TreeNodeParent(entryPtr->node);
1814 if (node == NULL) {
1815 return NULL;
1816 }
1817 return Blt_NodeToEntry(tvPtr, node);
1818}
1819
1820static void
1821FreeEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
1822{
1823 Blt_HashEntry *hPtr;
1824
1825 if (entryPtr == tvPtr->activePtr) {
1826 tvPtr->activePtr = Blt_TreeViewParentEntry(entryPtr);
1827 }
1828 if (entryPtr == tvPtr->activeButtonPtr) {
1829 tvPtr->activeButtonPtr = NULL;
1830 }
1831 if (entryPtr == tvPtr->focusPtr) {
1832 tvPtr->focusPtr = Blt_TreeViewParentEntry(entryPtr);
1833 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
1834 }
1835 if (entryPtr == tvPtr->selAnchorPtr) {
1836 tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
1837 }
1838 Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
1839 Blt_TreeViewPruneSelection(tvPtr, entryPtr);
1840 Blt_DeleteBindings(tvPtr->bindTable, entryPtr);
1841 hPtr = Blt_FindHashEntry(&tvPtr->entryTable, entryPtr->node);
1842 if (hPtr != NULL) {
1843 Blt_DeleteHashEntry(&tvPtr->entryTable, hPtr);
1844 }
1845 entryPtr->node = NULL;
1846
1847 Tcl_EventuallyFree(entryPtr, DestroyEntry);
1848 /*
1849 * Indicate that the screen layout of the hierarchy may have changed
1850 * because this node was deleted. The screen positions of the nodes
1851 * in tvPtr->visibleArr are invalidated.
1852 */
1853 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1854 Blt_TreeViewEventuallyRedraw(tvPtr);
1855}
1856
1857int
1858Blt_TreeViewEntryIsSelected(TreeView *tvPtr, TreeViewEntry *entryPtr)
1859{
1860 Blt_HashEntry *hPtr;
1861
1862 hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
1863 return (hPtr != NULL);
1864}
1865
1866void
1867Blt_TreeViewSelectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
1868{
1869 int isNew;
1870 Blt_HashEntry *hPtr;
1871
1872 hPtr = Blt_CreateHashEntry(&tvPtr->selectTable, (char *)entryPtr, &isNew);
1873 if (isNew) {
1874 Blt_ChainLink *linkPtr;
1875
1876 linkPtr = Blt_ChainAppend(tvPtr->selChainPtr, entryPtr);
1877 Blt_SetHashValue(hPtr, linkPtr);
1878 }
1879}
1880
1881void
1882Blt_TreeViewDeselectEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
1883{
1884 Blt_HashEntry *hPtr;
1885
1886 hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
1887 if (hPtr != NULL) {
1888 Blt_ChainLink *linkPtr;
1889
1890 linkPtr = Blt_GetHashValue(hPtr);
1891 Blt_ChainDeleteLink(tvPtr->selChainPtr, linkPtr);
1892 Blt_DeleteHashEntry(&tvPtr->selectTable, hPtr);
1893 }
1894}
1895
1896char *
1897Blt_TreeViewGetFullName(
1898 TreeView *tvPtr,
1899 TreeViewEntry *entryPtr,
1900 int checkEntryLabel,
1901 Tcl_DString *resultPtr)
1902{
1903 Blt_TreeNode node;
1904 char **names; /* Used the stack the component names. */
1905 char *staticSpace[64];
1906 int level;
1907 register int i;
1908
1909 level = Blt_TreeNodeDepth(tvPtr->tree, entryPtr->node);
1910 if (tvPtr->rootPtr->labelUid == NULL) {
1911 level--;
1912 }
1913 if (level > 64) {
1914 names = Blt_Malloc((level + 2) * sizeof(char *));
1915 assert(names);
1916 } else {
1917 names = staticSpace;
1918 }
1919 for (i = level; i >= 0; i--) {
1920 /* Save the name of each ancestor in the name array. */
1921 if (checkEntryLabel) {
1922 names[i] = GETLABEL(entryPtr);
1923 } else {
1924 names[i] = Blt_TreeNodeLabel(entryPtr->node);
1925 }
1926 node = Blt_TreeNodeParent(entryPtr->node);
1927 if (node != NULL) {
1928 entryPtr = Blt_NodeToEntry(tvPtr, node);
1929 }
1930 }
1931 Tcl_DStringInit(resultPtr);
1932 if (level >= 0) {
1933 if ((tvPtr->pathSep == SEPARATOR_LIST) ||
1934 (tvPtr->pathSep == SEPARATOR_NONE)) {
1935 for (i = 0; i <= level; i++) {
1936 Tcl_DStringAppendElement(resultPtr, names[i]);
1937 }
1938 } else {
1939 Tcl_DStringAppend(resultPtr, names[0], -1);
1940 for (i = 1; i <= level; i++) {
1941 Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
1942 Tcl_DStringAppend(resultPtr, names[i], -1);
1943 }
1944 }
1945 } else {
1946 if ((tvPtr->pathSep != SEPARATOR_LIST) &&
1947 (tvPtr->pathSep != SEPARATOR_NONE)) {
1948 Tcl_DStringAppend(resultPtr, tvPtr->pathSep, -1);
1949 }
1950 }
1951 if (names != staticSpace) {
1952 Blt_Free(names);
1953 }
1954 return Tcl_DStringValue(resultPtr);
1955}
1956
1957
1958int
1959Blt_TreeViewCloseEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
1960{
1961 char *cmd;
1962
1963 if (entryPtr->flags & ENTRY_CLOSED) {
1964 return TCL_OK; /* Entry is already closed. */
1965 }
1966 entryPtr->flags |= ENTRY_CLOSED;
1967
1968 /*
1969 * Invoke the entry's "close" command, if there is one. Otherwise
1970 * try the treeview's global "close" command.
1971 */
1972 cmd = CHOOSE(tvPtr->closeCmd, entryPtr->closeCmd);
1973 if (cmd != NULL) {
1974 Tcl_DString dString;
1975 int result;
1976
1977 Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
1978 Tcl_Preserve(entryPtr);
1979 result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
1980 Tcl_Release(entryPtr);
1981 Tcl_DStringFree(&dString);
1982 if (result != TCL_OK) {
1983 return TCL_ERROR;
1984 }
1985 }
1986 tvPtr->flags |= TV_LAYOUT;
1987 return TCL_OK;
1988}
1989
1990
1991int
1992Blt_TreeViewOpenEntry(TreeView *tvPtr, TreeViewEntry *entryPtr)
1993{
1994 char *cmd;
1995
1996 if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
1997 return TCL_OK; /* Entry is already open. */
1998 }
1999 entryPtr->flags &= ~ENTRY_CLOSED;
2000 /*
2001 * If there's a "open" command proc specified for the entry, use
2002 * that instead of the more general "open" proc for the entire
2003 * treeview.
2004 */
2005 cmd = CHOOSE(tvPtr->openCmd, entryPtr->openCmd);
2006 if (cmd != NULL) {
2007 Tcl_DString dString;
2008 int result;
2009
2010 Blt_TreeViewPercentSubst(tvPtr, entryPtr, cmd, &dString);
2011 Tcl_Preserve(entryPtr);
2012 result = Tcl_GlobalEval(tvPtr->interp, Tcl_DStringValue(&dString));
2013 Tcl_Release(entryPtr);
2014 Tcl_DStringFree(&dString);
2015 if (result != TCL_OK) {
2016 return TCL_ERROR;
2017 }
2018 }
2019 tvPtr->flags |= TV_LAYOUT;
2020 return TCL_OK;
2021}
2022
2023/*
2024 *----------------------------------------------------------------------
2025 *
2026 * Blt_TreeViewCreateEntry --
2027 *
2028 * This procedure is called by the Tree object when a node is
2029 * created and inserted into the tree. It adds a new treeview
2030 * entry field to the node.
2031 *
2032 * Results:
2033 * Returns the entry.
2034 *
2035 *----------------------------------------------------------------------
2036 */
2037int
2038Blt_TreeViewCreateEntry(
2039 TreeView *tvPtr,
2040 Blt_TreeNode node, /* Node that has just been created. */
2041 int objc,
2042 Tcl_Obj *CONST *objv,
2043 int flags)
2044{
2045 TreeViewEntry *entryPtr;
2046 int isNew;
2047 Blt_HashEntry *hPtr;
2048
2049 hPtr = Blt_CreateHashEntry(&tvPtr->entryTable, (char *)node, &isNew);
2050 if (isNew) {
2051 /* Create the entry structure */
2052 entryPtr = Blt_PoolAllocItem(tvPtr->entryPool, sizeof(TreeViewEntry));
2053 memset(entryPtr, 0, sizeof(TreeViewEntry));
2054 entryPtr->flags = tvPtr->buttonFlags | ENTRY_CLOSED;
2055 entryPtr->tvPtr = tvPtr;
2056 entryPtr->labelUid = NULL;
2057 entryPtr->node = node;
2058 Blt_SetHashValue(hPtr, entryPtr);
2059
2060 } else {
2061 entryPtr = Blt_GetHashValue(hPtr);
2062 }
2063 if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, objc, objv, flags)
2064 != TCL_OK) {
2065 FreeEntry(tvPtr, entryPtr);
2066 return TCL_ERROR; /* Error configuring the entry. */
2067 }
2068 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2069 Blt_TreeViewEventuallyRedraw(tvPtr);
2070 return TCL_OK;
2071}
2072
2073
2074/*ARGSUSED*/
2075static int
2076CreateApplyProc(
2077 Blt_TreeNode node, /* Node that has just been created. */
2078 ClientData clientData,
2079 int order) /* Not used. */
2080{
2081 TreeView *tvPtr = clientData;
2082 return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
2083}
2084
2085/*ARGSUSED*/
2086static int
2087DeleteApplyProc(
2088 Blt_TreeNode node,
2089 ClientData clientData,
2090 int order) /* Not used. */
2091{
2092 TreeView *tvPtr = clientData;
2093 /*
2094 * Unsetting the tree value triggers a call back to destroy the entry
2095 * and also releases the Tcl_Obj that contains it.
2096 */
2097 return Blt_TreeUnsetValueByKey(tvPtr->interp, tvPtr->tree, node,
2098 tvPtr->treeColumn.key);
2099}
2100
2101static int
2102TreeEventProc(ClientData clientData, Blt_TreeNotifyEvent *eventPtr)
2103{
2104 Blt_TreeNode node;
2105 TreeView *tvPtr = clientData;
2106
2107 node = Blt_TreeGetNode(eventPtr->tree, eventPtr->inode);
2108 switch (eventPtr->type) {
2109 case TREE_NOTIFY_CREATE:
2110 return Blt_TreeViewCreateEntry(tvPtr, node, 0, NULL, 0);
2111 case TREE_NOTIFY_DELETE:
2112 /*
2113 * Deleting the tree node triggers a call back to free the
2114 * treeview entry that is associated with it.
2115 */
2116 if (node != NULL) {
2117 FreeEntry(tvPtr, Blt_NodeToEntry(tvPtr, node));
2118 }
2119 break;
2120 case TREE_NOTIFY_RELABEL:
2121 if (node != NULL) {
2122 TreeViewEntry *entryPtr;
2123
2124 entryPtr = Blt_NodeToEntry(tvPtr, node);
2125 entryPtr->flags |= ENTRY_DIRTY;
2126 }
2127 /*FALLTHRU*/
2128 case TREE_NOTIFY_MOVE:
2129 case TREE_NOTIFY_SORT:
2130 Blt_TreeViewEventuallyRedraw(tvPtr);
2131 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY);
2132 break;
2133 default:
2134 /* empty */
2135 break;
2136 }
2137 return TCL_OK;
2138}
2139
2140TreeViewValue *
2141Blt_TreeViewFindValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
2142{
2143 register TreeViewValue *valuePtr;
2144
2145 for (valuePtr = entryPtr->values; valuePtr != NULL;
2146 valuePtr = valuePtr->nextPtr) {
2147 if (valuePtr->columnPtr == columnPtr) {
2148 return valuePtr;
2149 }
2150 }
2151 return NULL;
2152}
2153
2154void
2155Blt_TreeViewAddValue(TreeViewEntry *entryPtr, TreeViewColumn *columnPtr)
2156{
2157 if (Blt_TreeViewFindValue(entryPtr, columnPtr) == NULL) {
2158 Tcl_Obj *objPtr;
2159
2160 if (Blt_TreeViewGetData(entryPtr, columnPtr->key, &objPtr) == TCL_OK) {
2161 TreeViewValue *valuePtr;
2162
2163 /* Add a new value only if a data entry exists. */
2164 valuePtr = Blt_PoolAllocItem(entryPtr->tvPtr->valuePool,
2165 sizeof(TreeViewValue));
2166 valuePtr->columnPtr = columnPtr;
2167 valuePtr->nextPtr = entryPtr->values;
2168 valuePtr->textPtr = NULL;
2169 valuePtr->width = valuePtr->height = 0;
2170 valuePtr->stylePtr = NULL;
2171 valuePtr->string = NULL;
2172 entryPtr->values = valuePtr;
2173 }
2174 }
2175 entryPtr->tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2176 entryPtr->flags |= ENTRY_DIRTY;
2177}
2178
2179/*
2180 *----------------------------------------------------------------------
2181 *
2182 * TreeTraceProc --
2183 *
2184 * Mirrors the individual values of the tree object (they must
2185 * also be listed in the widget's columns chain). This is because
2186 * it must track and save the sizes of each individual data
2187 * entry, rather than re-computing all the sizes each time the
2188 * widget is redrawn.
2189 *
2190 * This procedure is called by the Tree object when a node data
2191 * value is set unset.
2192 *
2193 * Results:
2194 * Returns TCL_OK.
2195 *
2196 *----------------------------------------------------------------------
2197 */
2198/*ARGSUSED*/
2199static int
2200TreeTraceProc(
2201 ClientData clientData,
2202 Tcl_Interp *interp,
2203 Blt_TreeNode node, /* Node that has just been updated. */
2204 Blt_TreeKey key, /* Key of value that's been updated. */
2205 unsigned int flags)
2206{
2207 Blt_HashEntry *hPtr;
2208 TreeView *tvPtr = clientData;
2209 TreeViewColumn *columnPtr;
2210 TreeViewEntry *entryPtr;
2211 TreeViewValue *valuePtr, *nextPtr, *lastPtr;
2212
2213 hPtr = Blt_FindHashEntry(&tvPtr->entryTable, (char *)node);
2214 if (hPtr == NULL) {
2215 return TCL_OK; /* Not a node that we're interested in. */
2216 }
2217 entryPtr = Blt_GetHashValue(hPtr);
2218 flags &= TREE_TRACE_WRITE | TREE_TRACE_READ | TREE_TRACE_UNSET;
2219 switch (flags) {
2220 case TREE_TRACE_WRITE:
2221 hPtr = Blt_FindHashEntry(&tvPtr->columnTable, key);
2222 if (hPtr == NULL) {
2223 return TCL_OK; /* Data value isn't used by widget. */
2224 }
2225 columnPtr = Blt_GetHashValue(hPtr);
2226 if (columnPtr != &tvPtr->treeColumn) {
2227 Blt_TreeViewAddValue(entryPtr, columnPtr);
2228 }
2229 entryPtr->flags |= ENTRY_DIRTY;
2230 Blt_TreeViewEventuallyRedraw(tvPtr);
2231 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2232 break;
2233
2234 case TREE_TRACE_UNSET:
2235 lastPtr = NULL;
2236 for(valuePtr = entryPtr->values; valuePtr != NULL;
2237 valuePtr = nextPtr) {
2238 nextPtr = valuePtr->nextPtr;
2239 if (valuePtr->columnPtr->key == key) {
2240 Blt_TreeViewDestroyValue(tvPtr, valuePtr);
2241 if (lastPtr == NULL) {
2242 entryPtr->values = nextPtr;
2243 } else {
2244 lastPtr->nextPtr = nextPtr;
2245 }
2246 entryPtr->flags |= ENTRY_DIRTY;
2247 Blt_TreeViewEventuallyRedraw(tvPtr);
2248 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2249 break;
2250 }
2251 lastPtr = valuePtr;
2252 }
2253 break;
2254
2255 default:
2256 break;
2257 }
2258 return TCL_OK;
2259}
2260
2261
2262static void
2263GetValueSize(
2264 TreeView *tvPtr,
2265 TreeViewEntry *entryPtr,
2266 TreeViewValue *valuePtr,
2267 TreeViewStyle *stylePtr)
2268{
2269 TreeViewColumn *columnPtr;
2270
2271 columnPtr = valuePtr->columnPtr;
2272 valuePtr->width = valuePtr->height = 0;
2273 if (entryPtr->flags & ENTRY_DIRTY) { /* Reparse the data. */
2274 char *string;
2275 TreeViewIcon icon;
2276
2277 Tcl_Obj *valueObjPtr;
2278 TreeViewStyle *newStylePtr;
2279
2280 icon = NULL;
2281 newStylePtr = NULL;
2282 if (Blt_TreeViewGetData(entryPtr, valuePtr->columnPtr->key,
2283 &valueObjPtr) != TCL_OK) {
2284 return; /* No data ??? */
2285 }
2286 string = Tcl_GetString(valueObjPtr);
2287 valuePtr->string = string;
2288 if (string[0] == '@') { /* Name of style or Tk image. */
2289 int objc;
2290 Tcl_Obj **objv;
2291
2292 if ((Tcl_ListObjGetElements(tvPtr->interp, valueObjPtr, &objc,
2293 &objv) != TCL_OK) || (objc < 1) || (objc > 2)) {
2294 goto handleString;
2295 }
2296 if (objc > 0) {
2297 char *name;
2298
2299 name = Tcl_GetString(objv[0]) + 1;
2300 if (Blt_TreeViewGetStyle((Tcl_Interp *)NULL, tvPtr, name,
2301 &newStylePtr) != TCL_OK) {
2302 icon = Blt_TreeViewGetIcon(tvPtr, name);
2303 if (icon == NULL) {
2304 goto handleString;
2305 }
2306 /* Create a new style by the name of the image. */
2307 newStylePtr = Blt_TreeViewCreateStyle((Tcl_Interp *)NULL,
2308 tvPtr, STYLE_TEXTBOX, name);
2309 assert(newStylePtr);
2310 Blt_TreeViewUpdateStyleGCs(tvPtr, newStylePtr);
2311 }
2312
2313 }
2314 if (valuePtr->stylePtr != NULL) {
2315 Blt_TreeViewFreeStyle(tvPtr, valuePtr->stylePtr);
2316 }
2317 if (icon != NULL) {
2318 Blt_TreeViewSetStyleIcon(tvPtr, newStylePtr, icon);
2319 }
2320 valuePtr->stylePtr = newStylePtr;
2321 valuePtr->string = (objc > 1) ? Tcl_GetString(objv[1]) : NULL;
2322 }
2323 }
2324 handleString:
2325 stylePtr = CHOOSE(columnPtr->stylePtr, valuePtr->stylePtr);
2326 /* Measure the text string. */
2327 (*stylePtr->classPtr->measProc)(tvPtr, stylePtr, valuePtr);
2328}
2329
2330static void
2331GetRowExtents(
2332 TreeView *tvPtr,
2333 TreeViewEntry *entryPtr,
2334 int *widthPtr,
2335 int *heightPtr)
2336{
2337 TreeViewValue *valuePtr;
2338 int valueWidth; /* Width of individual value. */
2339 int width, height; /* Compute dimensions of row. */
2340 TreeViewStyle *stylePtr;
2341
2342 width = height = 0;
2343 for (valuePtr = entryPtr->values; valuePtr != NULL;
2344 valuePtr = valuePtr->nextPtr) {
2345 stylePtr = valuePtr->stylePtr;
2346 if (stylePtr == NULL) {
2347 stylePtr = valuePtr->columnPtr->stylePtr;
2348 }
2349 if ((entryPtr->flags & ENTRY_DIRTY) ||
2350 (stylePtr->flags & STYLE_DIRTY)) {
2351 GetValueSize(tvPtr, entryPtr, valuePtr, stylePtr);
2352 }
2353 if (valuePtr->height > height) {
2354 height = valuePtr->height;
2355 }
2356 valueWidth = valuePtr->width;
2357 width += valueWidth;
2358 }
2359 *widthPtr = width;
2360 *heightPtr = height;
2361}
2362
2363/*
2364 *----------------------------------------------------------------------
2365 *
2366 * Blt_TreeViewNearestEntry --
2367 *
2368 * Finds the entry closest to the given screen X-Y coordinates
2369 * in the viewport.
2370 *
2371 * Results:
2372 * Returns the pointer to the closest node. If no node is
2373 * visible (nodes may be hidden), NULL is returned.
2374 *
2375 *----------------------------------------------------------------------
2376 */
2377/*ARGSUSED*/
2378TreeViewEntry *
2379Blt_TreeViewNearestEntry(TreeView *tvPtr, int x, int y, int selectOne)
2380{
2381 TreeViewEntry *lastPtr, *entryPtr;
2382 register TreeViewEntry **p;
2383
2384 /*
2385 * We implicitly can pick only visible entries. So make sure that
2386 * the tree exists.
2387 */
2388 if (tvPtr->nVisible == 0) {
2389 return NULL;
2390 }
2391 if (y < tvPtr->titleHeight) {
2392 return (selectOne) ? tvPtr->visibleArr[0] : NULL;
2393 }
2394 /*
2395 * Since the entry positions were previously computed in world
2396 * coordinates, convert Y-coordinate from screen to world
2397 * coordinates too.
2398 */
2399 y = WORLDY(tvPtr, y);
2400 lastPtr = tvPtr->visibleArr[0];
2401 for (p = tvPtr->visibleArr; *p != NULL; p++) {
2402 entryPtr = *p;
2403 /*
2404 * If the start of the next entry starts beyond the point,
2405 * use the last entry.
2406 */
2407 if (entryPtr->worldY > y) {
2408 return (selectOne) ? entryPtr : NULL;
2409 }
2410 if (y < (entryPtr->worldY + entryPtr->height)) {
2411 return entryPtr; /* Found it. */
2412 }
2413 lastPtr = entryPtr;
2414 }
2415 return (selectOne) ? lastPtr : NULL;
2416}
2417
2418
2419ClientData
2420Blt_TreeViewEntryTag(TreeView *tvPtr, CONST char *string)
2421{
2422 Blt_HashEntry *hPtr;
2423 int isNew; /* Not used. */
2424
2425 hPtr = Blt_CreateHashEntry(&tvPtr->entryTagTable, string, &isNew);
2426 return Blt_GetHashKey(&tvPtr->entryTagTable, hPtr);
2427}
2428
2429ClientData
2430Blt_TreeViewButtonTag(TreeView *tvPtr, CONST char *string)
2431{
2432 Blt_HashEntry *hPtr;
2433 int isNew; /* Not used. */
2434
2435 hPtr = Blt_CreateHashEntry(&tvPtr->buttonTagTable, string, &isNew);
2436 return Blt_GetHashKey(&tvPtr->buttonTagTable, hPtr);
2437}
2438
2439ClientData
2440Blt_TreeViewColumnTag(TreeView *tvPtr, CONST char *string)
2441{
2442 Blt_HashEntry *hPtr;
2443 int isNew; /* Not used. */
2444
2445 hPtr = Blt_CreateHashEntry(&tvPtr->columnTagTable, string, &isNew);
2446 return Blt_GetHashKey(&tvPtr->columnTagTable, hPtr);
2447}
2448
2449ClientData
2450Blt_TreeViewStyleTag(TreeView *tvPtr, CONST char *string)
2451{
2452 Blt_HashEntry *hPtr;
2453 int isNew; /* Not used. */
2454
2455 hPtr = Blt_CreateHashEntry(&tvPtr->styleTagTable, string, &isNew);
2456 return Blt_GetHashKey(&tvPtr->styleTagTable, hPtr);
2457}
2458
2459static void
2460GetTags(
2461 Blt_BindTable table,
2462 ClientData object, /* Object picked. */
2463 ClientData context, /* Context of object. */
2464 Blt_List ids) /* (out) List of binding ids to be
2465 * applied for this object. */
2466{
2467 TreeView *tvPtr;
2468 int nNames;
2469 char **names;
2470 register char **p;
2471
2472 tvPtr = Blt_GetBindingData(table);
2473 if (context == (ClientData)ITEM_ENTRY_BUTTON) {
2474 TreeViewEntry *entryPtr = object;
2475
2476 Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Button"), 0);
2477 if (entryPtr->tagsUid != NULL) {
2478 if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
2479 &names) == TCL_OK) {
2480 for (p = names; *p != NULL; p++) {
2481 Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, *p), 0);
2482 }
2483 Blt_Free(names);
2484 }
2485 } else {
2486 Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "Entry"), 0);
2487 Blt_ListAppend(ids, Blt_TreeViewButtonTag(tvPtr, "all"), 0);
2488 }
2489 } else if (context == (ClientData)ITEM_COLUMN_TITLE) {
2490 TreeViewColumn *columnPtr = object;
2491
2492 Blt_ListAppend(ids, (char *)columnPtr, 0);
2493 if (columnPtr->tagsUid != NULL) {
2494 if (Tcl_SplitList((Tcl_Interp *)NULL, columnPtr->tagsUid, &nNames,
2495 &names) == TCL_OK) {
2496 for (p = names; *p != NULL; p++) {
2497 Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, *p), 0);
2498 }
2499 Blt_Free(names);
2500 }
2501 }
2502 } else if (context == ITEM_COLUMN_RULE) {
2503 Blt_ListAppend(ids, Blt_TreeViewColumnTag(tvPtr, "Rule"), 0);
2504 } else {
2505 TreeViewEntry *entryPtr = object;
2506
2507 Blt_ListAppend(ids, (char *)entryPtr, 0);
2508 if (entryPtr->tagsUid != NULL) {
2509 if (Tcl_SplitList((Tcl_Interp *)NULL, entryPtr->tagsUid, &nNames,
2510 &names) == TCL_OK) {
2511 for (p = names; *p != NULL; p++) {
2512 Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, *p), 0);
2513 }
2514 Blt_Free(names);
2515 }
2516 } else if (context == ITEM_ENTRY){
2517 Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
2518 Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
2519 } else {
2520 TreeViewValue *valuePtr = context;
2521
2522 if (valuePtr != NULL) {
2523 TreeViewStyle *stylePtr = valuePtr->stylePtr;
2524
2525 if (stylePtr == NULL) {
2526 stylePtr = valuePtr->columnPtr->stylePtr;
2527 }
2528 Blt_ListAppend(ids,
2529 Blt_TreeViewEntryTag(tvPtr, stylePtr->name), 0);
2530 Blt_ListAppend(ids,
2531 Blt_TreeViewEntryTag(tvPtr, valuePtr->columnPtr->key), 0);
2532 Blt_ListAppend(ids,
2533 Blt_TreeViewEntryTag(tvPtr, stylePtr->classPtr->className),
2534 0);
2535#ifndef notdef
2536 Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "Entry"), 0);
2537 Blt_ListAppend(ids, Blt_TreeViewEntryTag(tvPtr, "all"), 0);
2538#endif
2539 }
2540 }
2541 }
2542}
2543
2544/*ARGSUSED*/
2545static ClientData
2546PickItem(
2547 ClientData clientData,
2548 int x,
2549 int y, /* Screen coordinates of the test point. */
2550 ClientData *contextPtr) /* (out) Context of item selected: should
2551 * be ITEM_ENTRY, ITEM_ENTRY_BUTTON,
2552 * ITEM_COLUMN_TITLE,
2553 * ITEM_COLUMN_RULE, or
2554 * ITEM_STYLE. */
2555{
2556 TreeView *tvPtr = clientData;
2557 TreeViewEntry *entryPtr;
2558 TreeViewColumn *columnPtr;
2559
2560 if (contextPtr != NULL) {
2561 *contextPtr = NULL;
2562 }
2563 if (tvPtr->flags & TV_DIRTY) {
2564 /* Can't trust the selected entry if nodes have been added or
2565 * deleted. So recompute the layout. */
2566 if (tvPtr->flags & TV_LAYOUT) {
2567 Blt_TreeViewComputeLayout(tvPtr);
2568 }
2569 ComputeVisibleEntries(tvPtr);
2570 }
2571 columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, contextPtr);
2572 if ((*contextPtr != NULL) && (tvPtr->flags & TV_SHOW_COLUMN_TITLES)) {
2573 return columnPtr;
2574 }
2575 if (tvPtr->nVisible == 0) {
2576 return NULL;
2577 }
2578 entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
2579 if (entryPtr == NULL) {
2580 return NULL;
2581 }
2582 x = WORLDX(tvPtr, x);
2583 y = WORLDY(tvPtr, y);
2584 if (contextPtr != NULL) {
2585 *contextPtr = ITEM_ENTRY;
2586 if (columnPtr != NULL) {
2587 TreeViewValue *valuePtr;
2588
2589 valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
2590 if (valuePtr != NULL) {
2591 TreeViewStyle *stylePtr;
2592
2593 stylePtr = valuePtr->stylePtr;
2594 if (stylePtr == NULL) {
2595 stylePtr = valuePtr->columnPtr->stylePtr;
2596 }
2597 if ((stylePtr->classPtr->pickProc == NULL) ||
2598 ((*stylePtr->classPtr->pickProc)(entryPtr, valuePtr,
2599 stylePtr, x, y))) {
2600 *contextPtr = valuePtr;
2601 }
2602 }
2603 }
2604 if (entryPtr->flags & ENTRY_HAS_BUTTON) {
2605 TreeViewButton *buttonPtr = &tvPtr->button;
2606 int left, right, top, bottom;
2607
2608 left = entryPtr->worldX + entryPtr->buttonX - BUTTON_PAD;
2609 right = left + buttonPtr->width + 2 * BUTTON_PAD;
2610 top = entryPtr->worldY + entryPtr->buttonY - BUTTON_PAD;
2611 bottom = top + buttonPtr->height + 2 * BUTTON_PAD;
2612 if ((x >= left) && (x < right) && (y >= top) && (y < bottom)) {
2613 *contextPtr = (ClientData)ITEM_ENTRY_BUTTON;
2614 }
2615 }
2616 }
2617 return entryPtr;
2618}
2619
2620static void
2621GetEntryExtents(TreeView *tvPtr, TreeViewEntry *entryPtr)
2622{
2623 Tk_Font font;
2624 TreeViewIcon *icons;
2625 char *label;
2626 int entryWidth, entryHeight;
2627 int width, height;
2628
2629 /*
2630 * FIXME: Use of DIRTY flag inconsistent. When does it
2631 * mean "dirty entry"? When does it mean "dirty column"?
2632 * Does it matter? probably
2633 */
2634 if ((entryPtr->flags & ENTRY_DIRTY) || (tvPtr->flags & TV_UPDATE)) {
2635 Tk_FontMetrics fontMetrics;
2636
2637 entryPtr->iconWidth = entryPtr->iconHeight = 0;
2638 icons = CHOOSE(tvPtr->icons, entryPtr->icons);
2639 if (icons != NULL) {
2640 register int i;
2641
2642 for (i = 0; i < 2; i++) {
2643 if (icons[i] == NULL) {
2644 break;
2645 }
2646 if (entryPtr->iconWidth < TreeViewIconWidth(icons[i])) {
2647 entryPtr->iconWidth = TreeViewIconWidth(icons[i]);
2648 }
2649 if (entryPtr->iconHeight < TreeViewIconHeight(icons[i])) {
2650 entryPtr->iconHeight = TreeViewIconHeight(icons[i]);
2651 }
2652 }
2653 }
2654 if ((icons == NULL) || (icons[0] == NULL)) {
2655 entryPtr->iconWidth = DEF_ICON_WIDTH;
2656 entryPtr->iconHeight = DEF_ICON_HEIGHT;
2657 }
2658 entryPtr->iconWidth += 2 * ICON_PADX;
2659 entryPtr->iconHeight += 2 * ICON_PADY;
2660 entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
2661 font = entryPtr->font;
2662 if (font == NULL) {
2663 font = Blt_TreeViewGetStyleFont(tvPtr, tvPtr->treeColumn.stylePtr);
2664 }
2665 if (entryPtr->fullName != NULL) {
2666 Blt_Free(entryPtr->fullName);
2667 entryPtr->fullName = NULL;
2668 }
2669 if (entryPtr->textPtr != NULL) {
2670 Blt_Free(entryPtr->textPtr);
2671 entryPtr->textPtr = NULL;
2672 }
2673
2674 Tk_GetFontMetrics(font, &fontMetrics);
2675 entryPtr->lineHeight = fontMetrics.linespace;
2676 entryPtr->lineHeight += 2 * (FOCUS_WIDTH + LABEL_PADY +
2677 tvPtr->selBorderWidth) + tvPtr->leader;
2678 label = GETLABEL(entryPtr);
2679 if (label[0] == '\0') {
2680 width = height = entryPtr->lineHeight;
2681 } else {
2682 TextStyle ts;
2683
2684 Blt_InitTextStyle(&ts);
2685 ts.shadow.offset = entryPtr->shadow.offset;
2686 ts.font = font;
2687
2688 if (tvPtr->flatView) {
2689 Tcl_DString dString;
2690
2691 Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
2692 entryPtr->fullName = Blt_Strdup(Tcl_DStringValue(&dString));
2693 Tcl_DStringFree(&dString);
2694 entryPtr->textPtr = Blt_GetTextLayout(entryPtr->fullName, &ts);
2695 } else {
2696 entryPtr->textPtr = Blt_GetTextLayout(label, &ts);
2697 }
2698 width = entryPtr->textPtr->width;
2699 height = entryPtr->textPtr->height;
2700 }
2701 width += 2 * (FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth);
2702 height += 2 * (FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth);
2703 width = ODD(width);
2704 if (entryPtr->reqHeight > height) {
2705 height = entryPtr->reqHeight;
2706 }
2707 height = ODD(height);
2708 entryWidth = width;
2709 if (entryHeight < height) {
2710 entryHeight = height;
2711 }
2712 entryPtr->labelWidth = width;
2713 entryPtr->labelHeight = height;
2714 } else {
2715 entryHeight = entryPtr->labelHeight;
2716 entryWidth = entryPtr->labelWidth;
2717 }
2718 /*
2719 * Find the maximum height of the data value entries. This also has
2720 * the side effect of contributing the maximum width of the column.
2721 */
2722 GetRowExtents(tvPtr, entryPtr, &width, &height);
2723 if (entryHeight < height) {
2724 entryHeight = height;
2725 }
2726 entryPtr->width = entryWidth + COLUMN_PAD;
2727 entryPtr->height = entryHeight + tvPtr->leader;
2728 /*
2729 * Force the height of the entry to an even number. This is to
2730 * make the dots or the vertical line segments coincide with the
2731 * start of the horizontal lines.
2732 */
2733 if (entryPtr->height & 0x01) {
2734 entryPtr->height++;
2735 }
2736 entryPtr->flags &= ~ENTRY_DIRTY;
2737}
2738
2739/*
2740 * TreeView Procedures
2741 */
2742
2743/*
2744 * ----------------------------------------------------------------------
2745 *
2746 * CreateTreeView --
2747 *
2748 * ----------------------------------------------------------------------
2749 */
2750static TreeView *
2751CreateTreeView(
2752 Tcl_Interp *interp,
2753 Tcl_Obj *objPtr, /* Name of the new widget. */
2754 CONST char *className)
2755{
2756 Tk_Window tkwin;
2757 TreeView *tvPtr;
2758 char *name;
2759 Tcl_DString dString;
2760 int result;
2761
2762 name = Tcl_GetString(objPtr);
2763 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), name,
2764 (char *)NULL);
2765 if (tkwin == NULL) {
2766 return NULL;
2767 }
2768 Tk_SetClass(tkwin, (char *)className);
2769
2770 tvPtr = Blt_Calloc(1, sizeof(TreeView));
2771 assert(tvPtr);
2772 tvPtr->tkwin = tkwin;
2773 tvPtr->display = Tk_Display(tkwin);
2774 tvPtr->interp = interp;
2775 tvPtr->flags = (TV_HIDE_ROOT | TV_SHOW_COLUMN_TITLES |
2776 TV_DIRTY | TV_LAYOUT | TV_RESORT);
2777 tvPtr->leader = 0;
2778 tvPtr->dashes = 1;
2779 tvPtr->highlightWidth = 2;
2780 tvPtr->selBorderWidth = 1;
2781 tvPtr->borderWidth = 2;
2782 tvPtr->relief = TK_RELIEF_SUNKEN;
2783 tvPtr->selRelief = TK_RELIEF_FLAT;
2784 tvPtr->scrollMode = BLT_SCROLL_MODE_HIERBOX;
2785 tvPtr->selectMode = SELECT_MODE_SINGLE;
2786 tvPtr->button.closeRelief = tvPtr->button.openRelief = TK_RELIEF_SOLID;
2787 tvPtr->reqWidth = 200;
2788 tvPtr->reqHeight = 400;
2789 tvPtr->xScrollUnits = tvPtr->yScrollUnits = 20;
2790 tvPtr->lineWidth = 1;
2791 tvPtr->button.borderWidth = 1;
2792 tvPtr->colChainPtr = Blt_ChainCreate();
2793 tvPtr->buttonFlags = BUTTON_AUTO;
2794 tvPtr->selChainPtr = Blt_ChainCreate();
2795 Blt_InitHashTableWithPool(&tvPtr->entryTable, BLT_ONE_WORD_KEYS);
2796 Blt_InitHashTable(&tvPtr->columnTable, BLT_ONE_WORD_KEYS);
2797 Blt_InitHashTable(&tvPtr->iconTable, BLT_STRING_KEYS);
2798 Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
2799 Blt_InitHashTable(&tvPtr->uidTable, BLT_STRING_KEYS);
2800 Blt_InitHashTable(&tvPtr->styleTable, BLT_STRING_KEYS);
2801 tvPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, tvPtr, PickItem,
2802 GetTags);
2803 Blt_InitHashTable(&tvPtr->entryTagTable, BLT_STRING_KEYS);
2804 Blt_InitHashTable(&tvPtr->columnTagTable, BLT_STRING_KEYS);
2805 Blt_InitHashTable(&tvPtr->buttonTagTable, BLT_STRING_KEYS);
2806 Blt_InitHashTable(&tvPtr->styleTagTable, BLT_STRING_KEYS);
2807
2808 tvPtr->entryPool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
2809 tvPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
2810#if (TK_MAJOR_VERSION > 4)
2811 Blt_SetWindowInstanceData(tkwin, tvPtr);
2812#endif
2813 tvPtr->cmdToken = Tcl_CreateObjCommand(interp, Tk_PathName(tvPtr->tkwin),
2814 Blt_TreeViewWidgetInstCmd, tvPtr, WidgetInstCmdDeleteProc);
2815
2816#ifdef ITCL_NAMESPACES
2817 Itk_SetWidgetCommand(tvPtr->tkwin, tvPtr->cmdToken);
2818#endif
2819 Tk_CreateSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING, SelectionProc,
2820 tvPtr, XA_STRING);
2821 Tk_CreateEventHandler(tvPtr->tkwin, ExposureMask | StructureNotifyMask |
2822 FocusChangeMask, TreeViewEventProc, tvPtr);
2823 /*
2824 * Create a default style. This must exist before we can create
2825 * the treeview column.
2826 */
2827 tvPtr->stylePtr = Blt_TreeViewCreateStyle(interp, tvPtr, STYLE_TEXTBOX,
2828 "text");
2829 if (tvPtr->stylePtr == NULL) {
2830 return NULL;
2831 }
2832 /* Create a default column to display the view of the tree. */
2833 Tcl_DStringInit(&dString);
2834 Tcl_DStringAppend(&dString, "BLT TreeView ", -1);
2835 Tcl_DStringAppend(&dString, Tk_PathName(tvPtr->tkwin), -1);
2836 result = Blt_TreeViewCreateColumn(tvPtr, &tvPtr->treeColumn,
2837 Tcl_DStringValue(&dString), "");
2838 Tcl_DStringFree(&dString);
2839 if (result != TCL_OK) {
2840 return NULL;
2841 }
2842 Blt_ChainAppend(tvPtr->colChainPtr, &tvPtr->treeColumn);
2843 return tvPtr;
2844}
2845
2846/*
2847 * ----------------------------------------------------------------------
2848 *
2849 * DestroyTreeView --
2850 *
2851 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
2852 * to clean up the internal structure of a TreeView at a safe time
2853 * (when no-one is using it anymore).
2854 *
2855 * Results:
2856 * None.
2857 *
2858 * Side effects:
2859 * Everything associated with the widget is freed up.
2860 *
2861 * ----------------------------------------------------------------------
2862 */
2863static void
2864DestroyTreeView(DestroyData dataPtr) /* Pointer to the widget record. */
2865{
2866 Blt_HashEntry *hPtr;
2867 Blt_HashSearch cursor;
2868 TreeView *tvPtr = (TreeView *)dataPtr;
2869 TreeViewButton *buttonPtr;
2870 TreeViewEntry *entryPtr;
2871 TreeViewStyle *stylePtr;
2872
2873 Blt_TreeDeleteEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc,
2874 tvPtr);
2875 for (hPtr = Blt_FirstHashEntry(&tvPtr->entryTable, &cursor); hPtr != NULL;
2876 hPtr = Blt_NextHashEntry(&cursor)) {
2877 entryPtr = Blt_GetHashValue(hPtr);
2878 DestroyEntry((ClientData)entryPtr);
2879 }
2880 bltTreeViewTreeOption.clientData = tvPtr;
2881 bltTreeViewIconsOption.clientData = tvPtr;
2882 Blt_FreeObjOptions(bltTreeViewSpecs, (char *)tvPtr, tvPtr->display, 0);
2883 if (tvPtr->tkwin != NULL) {
2884 Tk_DeleteSelHandler(tvPtr->tkwin, XA_PRIMARY, XA_STRING);
2885 }
2886 if (tvPtr->lineGC != NULL) {
2887 Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
2888 }
2889 if (tvPtr->focusGC != NULL) {
2890 Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
2891 }
2892 if (tvPtr->visibleArr != NULL) {
2893 Blt_Free(tvPtr->visibleArr);
2894 }
2895 if (tvPtr->flatArr != NULL) {
2896 Blt_Free(tvPtr->flatArr);
2897 }
2898 if (tvPtr->levelInfo != NULL) {
2899 Blt_Free(tvPtr->levelInfo);
2900 }
2901 buttonPtr = &tvPtr->button;
2902 if (buttonPtr->activeGC != NULL) {
2903 Tk_FreeGC(tvPtr->display, buttonPtr->activeGC);
2904 }
2905 if (buttonPtr->normalGC != NULL) {
2906 Tk_FreeGC(tvPtr->display, buttonPtr->normalGC);
2907 }
2908 if (tvPtr->stylePtr != NULL) {
2909 Blt_TreeViewFreeStyle(tvPtr, tvPtr->stylePtr);
2910 }
2911
2912 Blt_TreeViewDestroyColumns(tvPtr);
2913 Blt_DestroyBindingTable(tvPtr->bindTable);
2914 Blt_ChainDestroy(tvPtr->selChainPtr);
2915 Blt_DeleteHashTable(&tvPtr->entryTagTable);
2916 Blt_DeleteHashTable(&tvPtr->columnTagTable);
2917 Blt_DeleteHashTable(&tvPtr->buttonTagTable);
2918 Blt_DeleteHashTable(&tvPtr->styleTagTable);
2919
2920 for (hPtr = Blt_FirstHashEntry(&tvPtr->styleTable, &cursor); hPtr != NULL;
2921 hPtr = Blt_NextHashEntry(&cursor)) {
2922 stylePtr = Blt_GetHashValue(hPtr);
2923 /* stylePtr->refCount = 0; */
2924 stylePtr->flags &= ~STYLE_USER;
2925 Blt_TreeViewFreeStyle(tvPtr, stylePtr);
2926 }
2927 if (tvPtr->comboWin != NULL) {
2928 Tk_DestroyWindow(tvPtr->comboWin);
2929 }
2930 Blt_DeleteHashTable(&tvPtr->styleTable);
2931 Blt_DeleteHashTable(&tvPtr->selectTable);
2932 Blt_DeleteHashTable(&tvPtr->uidTable);
2933 Blt_DeleteHashTable(&tvPtr->entryTable);
2934
2935 Blt_PoolDestroy(tvPtr->entryPool);
2936 Blt_PoolDestroy(tvPtr->valuePool);
2937 DumpIconTable(tvPtr);
2938 Blt_Free(tvPtr);
2939}
2940
2941/*
2942 * --------------------------------------------------------------
2943 *
2944 * TreeViewEventProc --
2945 *
2946 * This procedure is invoked by the Tk dispatcher for various
2947 * events on treeview widgets.
2948 *
2949 * Results:
2950 * None.
2951 *
2952 * Side effects:
2953 * When the window gets deleted, internal structures get
2954 * cleaned up. When it gets exposed, it is redisplayed.
2955 *
2956 * --------------------------------------------------------------
2957 */
2958static void
2959TreeViewEventProc(
2960 ClientData clientData, /* Information about window. */
2961 XEvent *eventPtr) /* Information about event. */
2962{
2963 TreeView *tvPtr = clientData;
2964
2965 if (eventPtr->type == Expose) {
2966 if (eventPtr->xexpose.count == 0) {
2967 Blt_TreeViewEventuallyRedraw(tvPtr);
2968 Blt_PickCurrentItem(tvPtr->bindTable);
2969 }
2970 } else if (eventPtr->type == ConfigureNotify) {
2971 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
2972 Blt_TreeViewEventuallyRedraw(tvPtr);
2973 } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
2974 if (eventPtr->xfocus.detail != NotifyInferior) {
2975 if (eventPtr->type == FocusIn) {
2976 tvPtr->flags |= TV_FOCUS;
2977 } else {
2978 tvPtr->flags &= ~TV_FOCUS;
2979 }
2980 Blt_TreeViewEventuallyRedraw(tvPtr);
2981 }
2982 } else if (eventPtr->type == DestroyNotify) {
2983 if (tvPtr->tkwin != NULL) {
2984 tvPtr->tkwin = NULL;
2985 Tcl_DeleteCommandFromToken(tvPtr->interp, tvPtr->cmdToken);
2986 }
2987 if (tvPtr->flags & TV_REDRAW) {
2988 Tcl_CancelIdleCall(DisplayTreeView, tvPtr);
2989 }
2990 if (tvPtr->flags & TV_SELECT_PENDING) {
2991 Tcl_CancelIdleCall(Blt_TreeViewSelectCmdProc, tvPtr);
2992 }
2993 Tcl_EventuallyFree(tvPtr, DestroyTreeView);
2994 }
2995}
2996
2997/* Selection Procedures */
2998/*
2999 *----------------------------------------------------------------------
3000 *
3001 * SelectionProc --
3002 *
3003 * This procedure is called back by Tk when the selection is
3004 * requested by someone. It returns part or all of the selection
3005 * in a buffer provided by the caller.
3006 *
3007 * Results:
3008 * The return value is the number of non-NULL bytes stored at
3009 * buffer. Buffer is filled (or partially filled) with a
3010 * NUL-terminated string containing part or all of the
3011 * selection, as given by offset and maxBytes.
3012 *
3013 * Side effects:
3014 * None.
3015 *
3016 *----------------------------------------------------------------------
3017 */
3018static int
3019SelectionProc(
3020 ClientData clientData, /* Information about the widget. */
3021 int offset, /* Offset within selection of first
3022 * character to be returned. */
3023 char *buffer, /* Location in which to place
3024 * selection. */
3025 int maxBytes) /* Maximum number of bytes to place
3026 * at buffer, not including terminating
3027 * NULL character. */
3028{
3029 Tcl_DString dString;
3030 TreeView *tvPtr = clientData;
3031 TreeViewEntry *entryPtr;
3032 int size;
3033
3034 if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
3035 return -1;
3036 }
3037 /*
3038 * Retrieve the names of the selected entries.
3039 */
3040 Tcl_DStringInit(&dString);
3041 if (tvPtr->flags & TV_SELECT_SORTED) {
3042 Blt_ChainLink *linkPtr;
3043
3044 for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr);
3045 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3046 entryPtr = Blt_ChainGetValue(linkPtr);
3047 Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
3048 Tcl_DStringAppend(&dString, "\n", -1);
3049 }
3050 } else {
3051 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3052 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3053 if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
3054 Tcl_DStringAppend(&dString, GETLABEL(entryPtr), -1);
3055 Tcl_DStringAppend(&dString, "\n", -1);
3056 }
3057 }
3058 }
3059 size = Tcl_DStringLength(&dString) - offset;
3060 strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
3061 Tcl_DStringFree(&dString);
3062 buffer[maxBytes] = '\0';
3063 return (size > maxBytes) ? maxBytes : size;
3064}
3065
3066/*
3067 *----------------------------------------------------------------------
3068 *
3069 * WidgetInstCmdDeleteProc --
3070 *
3071 * This procedure is invoked when a widget command is deleted. If
3072 * the widget isn't already in the process of being destroyed,
3073 * this command destroys it.
3074 *
3075 * Results:
3076 * None.
3077 *
3078 * Side effects:
3079 * The widget is destroyed.
3080 *
3081 *----------------------------------------------------------------------
3082 */
3083static void
3084WidgetInstCmdDeleteProc(ClientData clientData)
3085{
3086 TreeView *tvPtr = clientData;
3087
3088 /*
3089 * This procedure could be invoked either because the window was
3090 * destroyed and the command was then deleted (in which case tkwin
3091 * is NULL) or because the command was deleted, and then this
3092 * procedure destroys the widget.
3093 */
3094 if (tvPtr->tkwin != NULL) {
3095 Tk_Window tkwin;
3096
3097 tkwin = tvPtr->tkwin;
3098 tvPtr->tkwin = NULL;
3099 Tk_DestroyWindow(tkwin);
3100#ifdef ITCL_NAMESPACES
3101 Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
3102#endif /* ITCL_NAMESPACES */
3103 }
3104}
3105
3106/*
3107 * ----------------------------------------------------------------------
3108 *
3109 * Blt_TreeViewUpdateWidget --
3110 *
3111 * Updates the GCs and other information associated with the
3112 * treeview widget.
3113 *
3114 * Results:
3115 * The return value is a standard Tcl result. If TCL_ERROR is
3116 * returned, then interp->result contains an error message.
3117 *
3118 * Side effects:
3119 * Configuration information, such as text string, colors, font,
3120 * etc. get set for tvPtr; old resources get freed, if there
3121 * were any. The widget is redisplayed.
3122 *
3123 * ----------------------------------------------------------------------
3124 */
3125int
3126Blt_TreeViewUpdateWidget(Tcl_Interp *interp, TreeView *tvPtr)
3127{
3128 GC newGC;
3129 XGCValues gcValues;
3130 int setupTree;
3131 unsigned long gcMask;
3132
3133 /*
3134 * GC for dotted vertical line.
3135 */
3136 gcMask = (GCForeground | GCLineWidth);
3137 gcValues.foreground = tvPtr->lineColor->pixel;
3138 gcValues.line_width = tvPtr->lineWidth;
3139 if (tvPtr->dashes > 0) {
3140 gcMask |= (GCLineStyle | GCDashList);
3141 gcValues.line_style = LineOnOffDash;
3142 gcValues.dashes = tvPtr->dashes;
3143 }
3144 newGC = Tk_GetGC(tvPtr->tkwin, gcMask, &gcValues);
3145 if (tvPtr->lineGC != NULL) {
3146 Tk_FreeGC(tvPtr->display, tvPtr->lineGC);
3147 }
3148 tvPtr->lineGC = newGC;
3149
3150 /*
3151 * GC for active label. Dashed outline.
3152 */
3153 gcMask = GCForeground | GCLineStyle;
3154 gcValues.foreground = tvPtr->focusColor->pixel;
3155 gcValues.line_style = (LineIsDashed(tvPtr->focusDashes))
3156 ? LineOnOffDash : LineSolid;
3157 newGC = Blt_GetPrivateGC(tvPtr->tkwin, gcMask, &gcValues);
3158 if (LineIsDashed(tvPtr->focusDashes)) {
3159 tvPtr->focusDashes.offset = 2;
3160 Blt_SetDashes(tvPtr->display, newGC, &tvPtr->focusDashes);
3161 }
3162 if (tvPtr->focusGC != NULL) {
3163 Blt_FreePrivateGC(tvPtr->display, tvPtr->focusGC);
3164 }
3165 tvPtr->focusGC = newGC;
3166
3167 Blt_TreeViewConfigureButtons(tvPtr);
3168 tvPtr->inset = tvPtr->highlightWidth + tvPtr->borderWidth + INSET_PAD;
3169
3170 setupTree = FALSE;
3171
3172 /*
3173 * If no tree object was named, allocate a new one. The name will
3174 * be the same as the widget pathname.
3175 */
3176 if (tvPtr->tree == NULL) {
3177 Blt_Tree token;
3178 char *string;
3179
3180 string = Tk_PathName(tvPtr->tkwin);
3181 if (Blt_TreeCreate(interp, string, &token) != TCL_OK) {
3182 return TCL_ERROR;
3183 }
3184 tvPtr->tree = token;
3185 setupTree = TRUE;
3186 }
3187
3188 /*
3189 * If the tree object was changed, we need to setup the new one.
3190 */
3191 if (Blt_ObjConfigModified(bltTreeViewSpecs, "-tree", (char *)NULL)) {
3192 setupTree = TRUE;
3193 }
3194
3195 /*
3196 * These options change the layout of the box. Mark the widget for update.
3197 */
3198 if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font",
3199 "-linespacing", "-*width", "-height", "-hide*", "-tree", "-flat",
3200 (char *)NULL)) {
3201 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
3202 }
3203 /*
3204 * If the tree view was changed, mark all the nodes dirty (we'll
3205 * be switching back to either the full path name or the label)
3206 * and free the array representing the flattened view of the tree.
3207 */
3208 if (Blt_ObjConfigModified(bltTreeViewSpecs, "-hideleaves", "-flat",
3209 (char *)NULL)) {
3210 TreeViewEntry *entryPtr;
3211
3212 tvPtr->flags |= (TV_DIRTY | TV_RESORT);
3213 /* Mark all entries dirty. */
3214 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3215 entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
3216 entryPtr->flags |= ENTRY_DIRTY;
3217 }
3218 if ((!tvPtr->flatView) && (tvPtr->flatArr != NULL)) {
3219 Blt_Free(tvPtr->flatArr);
3220 tvPtr->flatArr = NULL;
3221 }
3222 }
3223 if ((tvPtr->reqHeight != Tk_ReqHeight(tvPtr->tkwin)) ||
3224 (tvPtr->reqWidth != Tk_ReqWidth(tvPtr->tkwin))) {
3225 Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
3226 }
3227
3228 if (setupTree) {
3229 Blt_TreeNode root;
3230
3231 Blt_TreeCreateEventHandler(tvPtr->tree, TREE_NOTIFY_ALL, TreeEventProc,
3232 tvPtr);
3233 TraceColumns(tvPtr);
3234 root = Blt_TreeRootNode(tvPtr->tree);
3235
3236 /* Automatically add view-entry values to the new tree. */
3237 Blt_TreeApply(root, CreateApplyProc, tvPtr);
3238 tvPtr->focusPtr = tvPtr->rootPtr = Blt_NodeToEntry(tvPtr, root);
3239 tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
3240 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->rootPtr, ITEM_ENTRY);
3241
3242 /* Automatically open the root node. */
3243 if (Blt_TreeViewOpenEntry(tvPtr, tvPtr->rootPtr) != TCL_OK) {
3244 return TCL_ERROR;
3245 }
3246 if (!(tvPtr->flags & TV_NEW_TAGS)) {
3247 Blt_Tree tree;
3248
3249 if (Blt_TreeCmdGetToken(interp, Blt_TreeName(tvPtr->tree),
3250 &tree) == TCL_OK) {
3251 Blt_TreeShareTagTable(tree, tvPtr->tree);
3252 }
3253 }
3254 }
3255
3256 if (Blt_ObjConfigModified(bltTreeViewSpecs, "-font", "-color",
3257 (char *)NULL)) {
3258 Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
3259 }
3260 Blt_TreeViewEventuallyRedraw(tvPtr);
3261 return TCL_OK;
3262}
3263
3264/*
3265 * ----------------------------------------------------------------------
3266 *
3267 * ResetCoordinates --
3268 *
3269 * Determines the maximum height of all visible entries.
3270 *
3271 * 1. Sets the worldY coordinate for all mapped/open entries.
3272 * 2. Determines if entry needs a button.
3273 * 3. Collects the minimum height of open/mapped entries. (Do for all
3274 * entries upon insert).
3275 * 4. Figures out horizontal extent of each entry (will be width of
3276 * tree view column).
3277 * 5. Collects maximum icon size for each level.
3278 * 6. The height of its vertical line
3279 *
3280 * Results:
3281 * Returns 1 if beyond the last visible entry, 0 otherwise.
3282 *
3283 * Side effects:
3284 * The array of visible nodes is filled.
3285 *
3286 * ----------------------------------------------------------------------
3287 */
3288static void
3289ResetCoordinates(
3290 TreeView *tvPtr,
3291 TreeViewEntry *entryPtr,
3292 int *yPtr)
3293{
3294 int depth;
3295
3296 entryPtr->worldY = -1;
3297 entryPtr->vertLineLength = -1;
3298 if ((entryPtr != tvPtr->rootPtr) &&
3299 (Blt_TreeViewEntryIsHidden(entryPtr))) {
3300 return; /* If the entry is hidden, then do nothing. */
3301 }
3302 entryPtr->worldY = *yPtr;
3303 entryPtr->vertLineLength = -(*yPtr);
3304 *yPtr += entryPtr->height;
3305
3306 depth = DEPTH(tvPtr, entryPtr->node) + 1;
3307 if (tvPtr->levelInfo[depth].labelWidth < entryPtr->labelWidth) {
3308 tvPtr->levelInfo[depth].labelWidth = entryPtr->labelWidth;
3309 }
3310 if (tvPtr->levelInfo[depth].iconWidth < entryPtr->iconWidth) {
3311 tvPtr->levelInfo[depth].iconWidth = entryPtr->iconWidth;
3312 }
3313 tvPtr->levelInfo[depth].iconWidth |= 0x01;
3314
3315 if ((entryPtr->flags & ENTRY_CLOSED) == 0) {
3316 TreeViewEntry *bottomPtr, *childPtr;
3317
3318 bottomPtr = entryPtr;
3319 for (childPtr = Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN);
3320 childPtr != NULL;
3321 childPtr = Blt_TreeViewNextSibling(childPtr, ENTRY_HIDDEN)){
3322 ResetCoordinates(tvPtr, childPtr, yPtr);
3323 bottomPtr = childPtr;
3324 }
3325 entryPtr->vertLineLength += bottomPtr->worldY;
3326 }
3327}
3328
3329static void
3330AdjustColumns(TreeView *tvPtr)
3331{
3332 Blt_ChainLink *linkPtr;
3333 TreeViewColumn *columnPtr;
3334 double weight;
3335 int nOpen;
3336 int size, avail, ration, growth;
3337
3338 growth = VPORTWIDTH(tvPtr) - tvPtr->worldWidth;
3339 nOpen = 0;
3340 weight = 0.0;
3341 /* Find out how many columns still have space available */
3342 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
3343 linkPtr = Blt_ChainNextLink(linkPtr)) {
3344 columnPtr = Blt_ChainGetValue(linkPtr);
3345 if ((columnPtr->hidden) ||
3346 (columnPtr->weight == 0.0) ||
3347 (columnPtr->width >= columnPtr->max) ||
3348 (columnPtr->reqWidth > 0)) {
3349 continue;
3350 }
3351 nOpen++;
3352 weight += columnPtr->weight;
3353 }
3354
3355 while ((nOpen > 0) && (weight > 0.0) && (growth > 0)) {
3356 ration = (int)(growth / weight);
3357 if (ration == 0) {
3358 ration = 1;
3359 }
3360 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
3361 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3362 columnPtr = Blt_ChainGetValue(linkPtr);
3363 if ((columnPtr->hidden) ||
3364 (columnPtr->weight == 0.0) ||
3365 (columnPtr->width >= columnPtr->max) ||
3366 (columnPtr->reqWidth > 0)) {
3367 continue;
3368 }
3369 size = (int)(ration * columnPtr->weight);
3370 if (size > growth) {
3371 size = growth;
3372 }
3373 avail = columnPtr->max - columnPtr->width;
3374 if (size > avail) {
3375 size = avail;
3376 nOpen--;
3377 weight -= columnPtr->weight;
3378 }
3379 growth -= size;
3380 columnPtr->width += size;
3381 }
3382 }
3383}
3384
3385/*
3386 * ----------------------------------------------------------------------
3387 *
3388 * ComputeFlatLayout --
3389 *
3390 * Recompute the layout when entries are opened/closed,
3391 * inserted/deleted, or when text attributes change (such as
3392 * font, linespacing).
3393 *
3394 * Results:
3395 * None.
3396 *
3397 * Side effects:
3398 * The world coordinates are set for all the opened entries.
3399 *
3400 * ----------------------------------------------------------------------
3401 */
3402static void
3403ComputeFlatLayout(TreeView *tvPtr)
3404{
3405 Blt_ChainLink *linkPtr;
3406 TreeViewColumn *columnPtr;
3407 TreeViewEntry **p;
3408 TreeViewEntry *entryPtr;
3409 int count;
3410 int maxX;
3411 int y;
3412
3413 /*
3414 * Pass 1: Reinitialize column sizes and loop through all nodes.
3415 *
3416 * 1. Recalculate the size of each entry as needed.
3417 * 2. The maximum depth of the tree.
3418 * 3. Minimum height of an entry. Dividing this by the
3419 * height of the widget gives a rough estimate of the
3420 * maximum number of visible entries.
3421 * 4. Build an array to hold level information to be filled
3422 * in on pass 2.
3423 */
3424 if (tvPtr->flags & (TV_DIRTY | TV_UPDATE)) {
3425 int position;
3426
3427 /* Reset the positions of all the columns and initialize the
3428 * column used to track the widest value. */
3429 position = 1;
3430 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
3431 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3432 columnPtr = Blt_ChainGetValue(linkPtr);
3433 columnPtr->maxWidth = 0;
3434 columnPtr->max = SHRT_MAX;
3435 if (columnPtr->reqMax > 0) {
3436 columnPtr->max = columnPtr->reqMax;
3437 }
3438 columnPtr->position = position;
3439 position++;
3440 }
3441
3442 /* If the view needs to be resorted, free the old view. */
3443 if ((tvPtr->flags & TV_RESORT) && (tvPtr->flatArr != NULL)) {
3444 Blt_Free(tvPtr->flatArr);
3445 tvPtr->flatArr = NULL;
3446 }
3447
3448 /* Recreate the flat view of all the open and not-hidden entries. */
3449 if (tvPtr->flatArr == NULL) {
3450 count = 0;
3451 /* Count the number of open entries to allocate for the array. */
3452 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3453 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3454 if ((tvPtr->flags & TV_HIDE_ROOT) &&
3455 (entryPtr == tvPtr->rootPtr)) {
3456 continue;
3457 }
3458 count++;
3459 }
3460 tvPtr->nEntries = count;
3461
3462 /* Allocate an array for the flat view. */
3463 tvPtr->flatArr = Blt_Calloc((count + 1), sizeof(TreeViewEntry *));
3464 assert(tvPtr->flatArr);
3465
3466 /* Fill the array with open and not-hidden entries */
3467 p = tvPtr->flatArr;
3468 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3469 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3470 if ((tvPtr->flags & TV_HIDE_ROOT) &&
3471 (entryPtr == tvPtr->rootPtr)) {
3472 continue;
3473 }
3474 *p++ = entryPtr;
3475 }
3476 *p = NULL;
3477 tvPtr->flags &= ~TV_SORTED; /* Indicate the view isn't sorted. */
3478 }
3479
3480 /* Collect the extents of the entries in the flat view. */
3481 tvPtr->depth = 0;
3482 tvPtr->minHeight = SHRT_MAX;
3483 for (p = tvPtr->flatArr; *p != NULL; p++) {
3484 entryPtr = *p;
3485 GetEntryExtents(tvPtr, entryPtr);
3486 if (tvPtr->minHeight > entryPtr->height) {
3487 tvPtr->minHeight = entryPtr->height;
3488 }
3489 entryPtr->flags &= ~ENTRY_HAS_BUTTON;
3490 }
3491 if (tvPtr->levelInfo != NULL) {
3492 Blt_Free(tvPtr->levelInfo);
3493 }
3494 tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
3495 assert(tvPtr->levelInfo);
3496 tvPtr->flags &= ~(TV_DIRTY | TV_UPDATE | TV_RESORT);
3497 if (tvPtr->flags & TV_SORT_AUTO) {
3498 /* If we're auto-sorting, schedule the view to be resorted. */
3499 tvPtr->flags |= TV_SORT_PENDING;
3500 }
3501 }
3502
3503 if (tvPtr->flags & TV_SORT_PENDING) {
3504 Blt_TreeViewSortFlatView(tvPtr);
3505 }
3506
3507 tvPtr->levelInfo[0].labelWidth = tvPtr->levelInfo[0].x =
3508 tvPtr->levelInfo[0].iconWidth = 0;
3509 /*
3510 * Pass 2: Loop through all open/mapped nodes.
3511 *
3512 * 1. Set world y-coordinates for entries. We must defer
3513 * setting the x-coordinates until we know the maximum
3514 * icon sizes at each level.
3515 * 2. Compute the maximum depth of the tree.
3516 * 3. Build an array to hold level information.
3517 */
3518 y = 0;
3519 count = 0;
3520 for(p = tvPtr->flatArr; *p != NULL; p++) {
3521 entryPtr = *p;
3522 entryPtr->flatIndex = count++;
3523 entryPtr->worldY = y;
3524 entryPtr->vertLineLength = 0;
3525 y += entryPtr->height;
3526 if (tvPtr->levelInfo[0].labelWidth < entryPtr->labelWidth) {
3527 tvPtr->levelInfo[0].labelWidth = entryPtr->labelWidth;
3528 }
3529 if (tvPtr->levelInfo[0].iconWidth < entryPtr->iconWidth) {
3530 tvPtr->levelInfo[0].iconWidth = entryPtr->iconWidth;
3531 }
3532 }
3533 tvPtr->levelInfo[0].iconWidth |= 0x01;
3534 tvPtr->worldHeight = y; /* Set the scroll height of the hierarchy. */
3535 if (tvPtr->worldHeight < 1) {
3536 tvPtr->worldHeight = 1;
3537 }
3538 maxX = tvPtr->levelInfo[0].iconWidth + tvPtr->levelInfo[0].labelWidth;
3539 tvPtr->treeColumn.maxWidth = maxX;
3540 tvPtr->treeWidth = maxX;
3541 tvPtr->flags |= TV_VIEWPORT;
3542}
3543
3544/*
3545 * ----------------------------------------------------------------------
3546 *
3547 * ComputeTreeLayout --
3548 *
3549 * Recompute the layout when entries are opened/closed,
3550 * inserted/deleted, or when text attributes change (such as
3551 * font, linespacing).
3552 *
3553 * Results:
3554 * None.
3555 *
3556 * Side effects:
3557 * The world coordinates are set for all the opened entries.
3558 *
3559 * ----------------------------------------------------------------------
3560 */
3561static void
3562ComputeTreeLayout(TreeView *tvPtr)
3563{
3564 Blt_ChainLink *linkPtr;
3565 TreeViewColumn *columnPtr;
3566 TreeViewEntry *entryPtr;
3567 int maxX, x, y;
3568 int sum;
3569 register int i;
3570
3571 /*
3572 * Pass 1: Reinitialize column sizes and loop through all nodes.
3573 *
3574 * 1. Recalculate the size of each entry as needed.
3575 * 2. The maximum depth of the tree.
3576 * 3. Minimum height of an entry. Dividing this by the
3577 * height of the widget gives a rough estimate of the
3578 * maximum number of visible entries.
3579 * 4. Build an array to hold level information to be filled
3580 * in on pass 2.
3581 */
3582 if (tvPtr->flags & TV_DIRTY) {
3583 int position;
3584
3585 position = 1;
3586 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
3587 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3588 columnPtr = Blt_ChainGetValue(linkPtr);
3589 columnPtr->maxWidth = 0;
3590 columnPtr->max = SHRT_MAX;
3591 if (columnPtr->reqMax > 0) {
3592 columnPtr->max = columnPtr->reqMax;
3593 }
3594 columnPtr->position = position;
3595 position++;
3596 }
3597 tvPtr->minHeight = SHRT_MAX;
3598 tvPtr->depth = 0;
3599 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3600 entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
3601 GetEntryExtents(tvPtr, entryPtr);
3602 if (tvPtr->minHeight > entryPtr->height) {
3603 tvPtr->minHeight = entryPtr->height;
3604 }
3605 /*
3606 * Determine if the entry should display a button
3607 * (indicating that it has children) and mark the
3608 * entry accordingly.
3609 */
3610 entryPtr->flags &= ~ENTRY_HAS_BUTTON;
3611 if (entryPtr->flags & BUTTON_SHOW) {
3612 entryPtr->flags |= ENTRY_HAS_BUTTON;
3613 } else if (entryPtr->flags & BUTTON_AUTO) {
3614 if (tvPtr->flags & TV_HIDE_LEAVES) {
3615 /* Check that a non-leaf child exists */
3616 if (Blt_TreeViewFirstChild(entryPtr, ENTRY_HIDDEN)
3617 != NULL) {
3618 entryPtr->flags |= ENTRY_HAS_BUTTON;
3619 }
3620 } else if (!Blt_TreeIsLeaf(entryPtr->node)) {
3621 entryPtr->flags |= ENTRY_HAS_BUTTON;
3622 }
3623 }
3624 /* Determine the depth of the tree. */
3625 if (tvPtr->depth < DEPTH(tvPtr, entryPtr->node)) {
3626 tvPtr->depth = DEPTH(tvPtr, entryPtr->node);
3627 }
3628 }
3629 if (tvPtr->flags & TV_SORT_PENDING) {
3630 Blt_TreeViewSortTreeView(tvPtr);
3631 }
3632 if (tvPtr->levelInfo != NULL) {
3633 Blt_Free(tvPtr->levelInfo);
3634 }
3635 tvPtr->levelInfo = Blt_Calloc(tvPtr->depth + 2, sizeof(LevelInfo));
3636 assert(tvPtr->levelInfo);
3637 tvPtr->flags &= ~(TV_DIRTY | TV_RESORT);
3638 }
3639 for (i = 0; i <= (tvPtr->depth + 1); i++) {
3640 tvPtr->levelInfo[i].labelWidth = tvPtr->levelInfo[i].x =
3641 tvPtr->levelInfo[i].iconWidth = 0;
3642 }
3643 /*
3644 * Pass 2: Loop through all open/mapped nodes.
3645 *
3646 * 1. Set world y-coordinates for entries. We must defer
3647 * setting the x-coordinates until we know the maximum
3648 * icon sizes at each level.
3649 * 2. Compute the maximum depth of the tree.
3650 * 3. Build an array to hold level information.
3651 */
3652 y = 0;
3653 if (tvPtr->flags & TV_HIDE_ROOT) {
3654 /* If the root entry is to be hidden, cheat by offsetting
3655 * the y-coordinates by the height of the entry. */
3656 y = -(tvPtr->rootPtr->height);
3657 }
3658 ResetCoordinates(tvPtr, tvPtr->rootPtr, &y);
3659 tvPtr->worldHeight = y; /* Set the scroll height of the hierarchy. */
3660 if (tvPtr->worldHeight < 1) {
3661 tvPtr->worldHeight = 1;
3662 }
3663 sum = maxX = 0;
3664 for (i = 0; i <= (tvPtr->depth + 1); i++) {
3665 sum += tvPtr->levelInfo[i].iconWidth;
3666 if (i <= tvPtr->depth) {
3667 tvPtr->levelInfo[i + 1].x = sum;
3668 }
3669 x = sum + tvPtr->levelInfo[i].labelWidth;
3670 if (x > maxX) {
3671 maxX = x;
3672 }
3673 }
3674 tvPtr->treeColumn.maxWidth = maxX;
3675 tvPtr->treeWidth = maxX;
3676}
3677
3678
3679static void
3680LayoutColumns(TreeView *tvPtr)
3681{
3682 Blt_ChainLink *linkPtr;
3683 TreeViewColumn *columnPtr;
3684 int sum;
3685
3686 /* The width of the widget (in world coordinates) is the sum
3687 * of the column widths. */
3688
3689 tvPtr->worldWidth = tvPtr->titleHeight = 0;
3690 sum = 0;
3691 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
3692 linkPtr = Blt_ChainNextLink(linkPtr)) {
3693 columnPtr = Blt_ChainGetValue(linkPtr);
3694 columnPtr->width = 0;
3695 if (!columnPtr->hidden) {
3696 if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES) &&
3697 (tvPtr->titleHeight < columnPtr->titleHeight)) {
3698 tvPtr->titleHeight = columnPtr->titleHeight;
3699 }
3700 if (columnPtr->reqWidth > 0) {
3701 columnPtr->width = columnPtr->reqWidth;
3702 } else {
3703 /* The computed width of a column is the maximum of
3704 * the title width and the widest entry. */
3705 columnPtr->width = MAX(columnPtr->titleWidth,
3706 columnPtr->maxWidth);
3707 /* Check that the width stays within any constraints that
3708 * have been set. */
3709 if ((columnPtr->reqMin > 0) &&
3710 (columnPtr->reqMin > columnPtr->width)) {
3711 columnPtr->width = columnPtr->reqMin;
3712 }
3713 if ((columnPtr->reqMax > 0) &&
3714 (columnPtr->reqMax < columnPtr->width)) {
3715 columnPtr->width = columnPtr->reqMax;
3716 }
3717 }
3718 columnPtr->width +=
3719 PADDING(columnPtr->pad) + 2 * columnPtr->borderWidth;
3720 }
3721 columnPtr->worldX = sum;
3722 sum += columnPtr->width;
3723 }
3724 tvPtr->worldWidth = sum;
3725 if (VPORTWIDTH(tvPtr) > sum) {
3726 AdjustColumns(tvPtr);
3727 }
3728 sum = 0;
3729 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
3730 linkPtr = Blt_ChainNextLink(linkPtr)) {
3731 columnPtr = Blt_ChainGetValue(linkPtr);
3732 columnPtr->worldX = sum;
3733 sum += columnPtr->width;
3734 }
3735 if (tvPtr->titleHeight > 0) {
3736 /* If any headings are displayed, add some extra padding to
3737 * the height. */
3738 tvPtr->titleHeight += 4;
3739 }
3740 /* tvPtr->worldWidth += 10; */
3741 if (tvPtr->yScrollUnits < 1) {
3742 tvPtr->yScrollUnits = 1;
3743 }
3744 if (tvPtr->xScrollUnits < 1) {
3745 tvPtr->xScrollUnits = 1;
3746 }
3747 if (tvPtr->worldWidth < 1) {
3748 tvPtr->worldWidth = 1;
3749 }
3750 tvPtr->flags &= ~TV_LAYOUT;
3751 tvPtr->flags |= TV_SCROLL;
3752}
3753
3754/*
3755 * ----------------------------------------------------------------------
3756 *
3757 * Blt_TreeViewComputeLayout --
3758 *
3759 * Recompute the layout when entries are opened/closed,
3760 * inserted/deleted, or when text attributes change (such as
3761 * font, linespacing).
3762 *
3763 * Results:
3764 * None.
3765 *
3766 * Side effects:
3767 * The world coordinates are set for all the opened entries.
3768 *
3769 * ----------------------------------------------------------------------
3770 */
3771void
3772Blt_TreeViewComputeLayout(TreeView *tvPtr)
3773{
3774 Blt_ChainLink *linkPtr;
3775 TreeViewColumn *columnPtr;
3776 TreeViewEntry *entryPtr;
3777 TreeViewValue *valuePtr;
3778
3779 if (tvPtr->flatView) {
3780 ComputeFlatLayout(tvPtr);
3781 } else {
3782 ComputeTreeLayout(tvPtr);
3783 }
3784 /*
3785 * Determine the width of each column based upon the entries
3786 * that as open (not hidden). The widest entry in a column
3787 * determines the width of that column.
3788 */
3789
3790 /* Initialize the columns. */
3791 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
3792 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3793 columnPtr = Blt_ChainGetValue(linkPtr);
3794 columnPtr->maxWidth = 0;
3795 columnPtr->max = SHRT_MAX;
3796 if (columnPtr->reqMax > 0) {
3797 columnPtr->max = columnPtr->reqMax;
3798 }
3799 }
3800 /* The treeview column width was computed earlier. */
3801 tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
3802
3803 /*
3804 * Look at all open entries and their values. Determine the column
3805 * widths by tracking the maximum width value in each column.
3806 */
3807 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
3808 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3809 for (valuePtr = entryPtr->values; valuePtr != NULL;
3810 valuePtr = valuePtr->nextPtr) {
3811 if (valuePtr->columnPtr->maxWidth < valuePtr->width) {
3812 valuePtr->columnPtr->maxWidth = valuePtr->width;
3813 }
3814 }
3815 }
3816 /* Now layout the columns with the proper sizes. */
3817 LayoutColumns(tvPtr);
3818}
3819
3820
3821/*
3822 * ----------------------------------------------------------------------
3823 *
3824 * ComputeVisibleEntries --
3825 *
3826 * The entries visible in the viewport (the widget's window) are
3827 * inserted into the array of visible nodes.
3828 *
3829 * Results:
3830 * Returns 1 if beyond the last visible entry, 0 otherwise.
3831 *
3832 * Side effects:
3833 * The array of visible nodes is filled.
3834 *
3835 * ----------------------------------------------------------------------
3836 */
3837static int
3838ComputeVisibleEntries(TreeView *tvPtr)
3839{
3840 int height;
3841 int level;
3842 int nSlots;
3843 int x, maxX;
3844 int xOffset, yOffset;
3845
3846 xOffset = Blt_AdjustViewport(tvPtr->xOffset, tvPtr->worldWidth,
3847 VPORTWIDTH(tvPtr), tvPtr->xScrollUnits, tvPtr->scrollMode);
3848 yOffset = Blt_AdjustViewport(tvPtr->yOffset,
3849 tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
3850 tvPtr->scrollMode);
3851
3852 if ((xOffset != tvPtr->xOffset) || (yOffset != tvPtr->yOffset)) {
3853 tvPtr->yOffset = yOffset;
3854 tvPtr->xOffset = xOffset;
3855 tvPtr->flags |= TV_VIEWPORT;
3856 }
3857 height = VPORTHEIGHT(tvPtr);
3858
3859 /* Allocate worst case number of slots for entry array. */
3860 nSlots = (height / tvPtr->minHeight) + 3;
3861 if (nSlots != tvPtr->nVisible) {
3862 if (tvPtr->visibleArr != NULL) {
3863 Blt_Free(tvPtr->visibleArr);
3864 }
3865 tvPtr->visibleArr = Blt_Calloc(nSlots, sizeof(TreeViewEntry *));
3866 assert(tvPtr->visibleArr);
3867 }
3868 tvPtr->nVisible = 0;
3869
3870 if (tvPtr->rootPtr->flags & ENTRY_HIDDEN) {
3871 return TCL_OK; /* Root node is hidden. */
3872 }
3873 /* Find the node where the view port starts. */
3874 if (tvPtr->flatView) {
3875 register TreeViewEntry **p, *entryPtr;
3876
3877 /* Find the starting entry visible in the viewport. It can't
3878 * be hidden or any of it's ancestors closed. */
3879 again:
3880 for (p = tvPtr->flatArr; *p != NULL; p++) {
3881 entryPtr = *p;
3882 if ((entryPtr->worldY + entryPtr->height) > tvPtr->yOffset) {
3883 break;
3884 }
3885 }
3886 /*
3887 * If we can't find the starting node, then the view must be
3888 * scrolled down, but some nodes were deleted. Reset the view
3889 * back to the top and try again.
3890 */
3891 if (*p == NULL) {
3892 if (tvPtr->yOffset == 0) {
3893 return TCL_OK; /* All entries are hidden. */
3894 }
3895 tvPtr->yOffset = 0;
3896 goto again;
3897 }
3898
3899 maxX = 0;
3900 height += tvPtr->yOffset;
3901 for (/* empty */; *p != NULL; p++) {
3902 entryPtr = *p;
3903 entryPtr->worldX = LEVELX(0) + tvPtr->treeColumn.worldX;
3904 x = entryPtr->worldX + ICONWIDTH(0) + entryPtr->width;
3905 if (x > maxX) {
3906 maxX = x;
3907 }
3908 if (entryPtr->worldY >= height) {
3909 break;
3910 }
3911 tvPtr->visibleArr[tvPtr->nVisible] = *p;
3912 tvPtr->nVisible++;
3913 }
3914 tvPtr->visibleArr[tvPtr->nVisible] = NULL;
3915 } else {
3916 TreeViewEntry *entryPtr;
3917
3918 entryPtr = tvPtr->rootPtr;
3919 while ((entryPtr->worldY + entryPtr->height) <= tvPtr->yOffset) {
3920 for (entryPtr = Blt_TreeViewLastChild(entryPtr, ENTRY_HIDDEN);
3921 entryPtr != NULL;
3922 entryPtr = Blt_TreeViewPrevSibling(entryPtr, ENTRY_HIDDEN)) {
3923 if (entryPtr->worldY <= tvPtr->yOffset) {
3924 break;
3925 }
3926 }
3927 /*
3928 * If we can't find the starting node, then the view must be
3929 * scrolled down, but some nodes were deleted. Reset the view
3930 * back to the top and try again.
3931 */
3932 if (entryPtr == NULL) {
3933 if (tvPtr->yOffset == 0) {
3934 return TCL_OK; /* All entries are hidden. */
3935 }
3936 tvPtr->yOffset = 0;
3937 continue;
3938 }
3939 }
3940
3941
3942 height += tvPtr->yOffset;
3943 maxX = 0;
3944 tvPtr->treeColumn.maxWidth = tvPtr->treeWidth;
3945
3946 for (/* empty */; entryPtr != NULL;
3947 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
3948 /*
3949 * Compute and save the entry's X-coordinate now that we know
3950 * the maximum level offset for the entire widget.
3951 */
3952 level = DEPTH(tvPtr, entryPtr->node);
3953 entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
3954
3955 x = entryPtr->worldX + ICONWIDTH(level) + ICONWIDTH(level + 1) +
3956 entryPtr->width;
3957 if (x > maxX) {
3958 maxX = x;
3959 }
3960 if (entryPtr->worldY >= height) {
3961 break;
3962 }
3963 tvPtr->visibleArr[tvPtr->nVisible] = entryPtr;
3964 tvPtr->nVisible++;
3965 }
3966 tvPtr->visibleArr[tvPtr->nVisible] = NULL;
3967 }
3968 /*
3969 * -------------------------------------------------------------------
3970 *
3971 * Note: It's assumed that the view port always starts at or
3972 * over an entry. Check that a change in the hierarchy
3973 * (e.g. closing a node) hasn't left the viewport beyond
3974 * the last entry. If so, adjust the viewport to start
3975 * on the last entry.
3976 *
3977 * -------------------------------------------------------------------
3978 */
3979 if (tvPtr->xOffset > (tvPtr->worldWidth - tvPtr->xScrollUnits)) {
3980 tvPtr->xOffset = tvPtr->worldWidth - tvPtr->xScrollUnits;
3981 }
3982 if (tvPtr->yOffset > (tvPtr->worldHeight - tvPtr->yScrollUnits)) {
3983 tvPtr->yOffset = tvPtr->worldHeight - tvPtr->yScrollUnits;
3984 }
3985 tvPtr->xOffset = Blt_AdjustViewport(tvPtr->xOffset,
3986 tvPtr->worldWidth, VPORTWIDTH(tvPtr), tvPtr->xScrollUnits,
3987 tvPtr->scrollMode);
3988 tvPtr->yOffset = Blt_AdjustViewport(tvPtr->yOffset,
3989 tvPtr->worldHeight, VPORTHEIGHT(tvPtr), tvPtr->yScrollUnits,
3990 tvPtr->scrollMode);
3991
3992 Blt_PickCurrentItem(tvPtr->bindTable);
3993 tvPtr->flags &= ~TV_DIRTY;
3994 return TCL_OK;
3995}
3996
3997
3998/*
3999 * ---------------------------------------------------------------------------
4000 *
4001 * DrawVerticals --
4002 *
4003 * Draws vertical lines for the ancestor nodes. While the entry
4004 * of the ancestor may not be visible, its vertical line segment
4005 * does extent into the viewport. So walk back up the hierarchy
4006 * drawing lines until we get to the root.
4007 *
4008 * Results:
4009 * None.
4010 *
4011 * Side Effects:
4012 * Vertical lines are drawn for the ancestor nodes.
4013 *
4014 * ---------------------------------------------------------------------------
4015 */
4016static void
4017DrawVerticals(
4018 TreeView *tvPtr, /* Widget record containing the attribute
4019 * information for buttons. */
4020 TreeViewEntry *entryPtr, /* Entry to be drawn. */
4021 Drawable drawable) /* Pixmap or window to draw into. */
4022{
4023 int height, level;
4024 int x, y;
4025 int x1, y1, x2, y2;
4026
4027 while (entryPtr != tvPtr->rootPtr) {
4028 entryPtr = Blt_TreeViewParentEntry(entryPtr);
4029 if (entryPtr == NULL) {
4030 break;
4031 }
4032 level = DEPTH(tvPtr, entryPtr->node);
4033 /*
4034 * World X-coordinates aren't computed only for entries that are
4035 * outside the view port. So for each off-screen ancestor node
4036 * compute it here too.
4037 */
4038 entryPtr->worldX = LEVELX(level) + tvPtr->treeColumn.worldX;
4039 x = SCREENX(tvPtr, entryPtr->worldX);
4040 y = SCREENY(tvPtr, entryPtr->worldY);
4041 height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
4042 tvPtr->button.height);
4043 y += (height - tvPtr->button.height) / 2;
4044 x1 = x2 = x + ICONWIDTH(level) + ICONWIDTH(level + 1) / 2;
4045 y1 = y + tvPtr->button.height / 2;
4046 y2 = y1 + entryPtr->vertLineLength;
4047 if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
4048 y1 += entryPtr->height;
4049 }
4050 /*
4051 * Clip the line's Y-coordinates at the viewport borders.
4052 */
4053 if (y1 < 0) {
4054 y1 = (y1 & 0x1); /* Make sure the dotted line starts on
4055 * the same even/odd pixel. */
4056 }
4057 if (y2 > Tk_Height(tvPtr->tkwin)) {
4058 y2 = Tk_Height(tvPtr->tkwin);
4059 }
4060 if ((y1 < Tk_Height(tvPtr->tkwin)) && (y2 > 0)) {
4061 XDrawLine(tvPtr->display, drawable, tvPtr->lineGC,
4062 x1, y1, x2, y2);
4063 }
4064 }
4065}
4066
4067void
4068Blt_TreeViewDrawRule(
4069 TreeView *tvPtr, /* Widget record containing the
4070 * attribute information for rules. */
4071 TreeViewColumn *columnPtr,
4072 Drawable drawable) /* Pixmap or window to draw into. */
4073{
4074 int x, y1, y2;
4075
4076 x = SCREENX(tvPtr, columnPtr->worldX) +
4077 columnPtr->width + tvPtr->ruleMark - tvPtr->ruleAnchor - 1;
4078
4079 y1 = tvPtr->titleHeight + tvPtr->inset;
4080 y2 = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
4081 XDrawLine(tvPtr->display, drawable, columnPtr->ruleGC, x, y1, x, y2);
4082 tvPtr->flags = TOGGLE(tvPtr->flags, TV_RULE_ACTIVE);
4083}
4084
4085/*
4086 * ---------------------------------------------------------------------------
4087 *
4088 * Blt_TreeViewDrawButton --
4089 *
4090 * Draws a button for the given entry. The button is drawn
4091 * centered in the region immediately to the left of the origin
4092 * of the entry (computed in the layout routines). The height
4093 * and width of the button were previously calculated from the
4094 * average row height.
4095 *
4096 * button height = entry height - (2 * some arbitrary padding).
4097 * button width = button height.
4098 *
4099 * The button may have a border. The symbol (either a plus or
4100 * minus) is slight smaller than the width or height minus the
4101 * border.
4102 *
4103 * x,y origin of entry
4104 *
4105 * +---+
4106 * | + | icon label
4107 * +---+
4108 * closed
4109 *
4110 * |----|----| horizontal offset
4111 *
4112 * +---+
4113 * | - | icon label
4114 * +---+
4115 * open
4116 *
4117 * Results:
4118 * None.
4119 *
4120 * Side Effects:
4121 * A button is drawn for the entry.
4122 *
4123 * ---------------------------------------------------------------------------
4124 */
4125void
4126Blt_TreeViewDrawButton(
4127 TreeView *tvPtr, /* Widget record containing the
4128 * attribute information for
4129 * buttons. */
4130 TreeViewEntry *entryPtr, /* Entry. */
4131 Drawable drawable, /* Pixmap or window to draw into. */
4132 int x,
4133 int y)
4134{
4135 Tk_3DBorder border;
4136 TreeViewButton *buttonPtr = &tvPtr->button;
4137 TreeViewIcon icon;
4138 int relief;
4139 int width, height;
4140
4141 if (entryPtr == tvPtr->activeButtonPtr) {
4142 border = buttonPtr->activeBorder;
4143 } else {
4144 border = buttonPtr->border;
4145 }
4146 if (entryPtr->flags & ENTRY_CLOSED) {
4147 relief = buttonPtr->closeRelief;
4148 } else {
4149 relief = buttonPtr->openRelief;
4150 }
4151 if (relief == TK_RELIEF_SOLID) {
4152 relief = TK_RELIEF_FLAT;
4153 }
4154 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, y,
4155 buttonPtr->width, buttonPtr->height, buttonPtr->borderWidth, relief);
4156
4157 x += buttonPtr->borderWidth;
4158 y += buttonPtr->borderWidth;
4159 width = buttonPtr->width - (2 * buttonPtr->borderWidth);
4160 height = buttonPtr->height - (2 * buttonPtr->borderWidth);
4161
4162 icon = NULL;
4163 if (buttonPtr->icons != NULL) { /* Open or close button icon? */
4164 icon = buttonPtr->icons[0];
4165 if (((entryPtr->flags & ENTRY_CLOSED) == 0) &&
4166 (buttonPtr->icons[1] != NULL)) {
4167 icon = buttonPtr->icons[1];
4168 }
4169 }
4170 if (icon != NULL) { /* Icon or rectangle? */
4171 Tk_RedrawImage(TreeViewIconBits(icon), 0, 0, width, height, drawable,
4172 x, y);
4173 } else {
4174 int top, bottom, left, right;
4175 XSegment segments[6];
4176 int count;
4177 GC gc;
4178
4179 gc = (entryPtr == tvPtr->activeButtonPtr)
4180 ? buttonPtr->activeGC : buttonPtr->normalGC;
4181 if (relief == TK_RELIEF_FLAT) {
4182 /* Draw the box outline */
4183
4184 left = x - buttonPtr->borderWidth;
4185 top = y - buttonPtr->borderWidth;
4186 right = left + buttonPtr->width - 1;
4187 bottom = top + buttonPtr->height - 1;
4188
4189 segments[0].x1 = left;
4190 segments[0].x2 = right;
4191 segments[0].y2 = segments[0].y1 = top;
4192 segments[1].x2 = segments[1].x1 = right;
4193 segments[1].y1 = top;
4194 segments[1].y2 = bottom;
4195 segments[2].x2 = segments[2].x1 = left;
4196 segments[2].y1 = top;
4197 segments[2].y2 = bottom;
4198#ifdef WIN32
4199 segments[2].y2++;
4200#endif
4201 segments[3].x1 = left;
4202 segments[3].x2 = right;
4203 segments[3].y2 = segments[3].y1 = bottom;
4204#ifdef WIN32
4205 segments[3].x2++;
4206#endif
4207 }
4208 top = y + height / 2;
4209 left = x + BUTTON_IPAD;
4210 right = x + width - BUTTON_IPAD;
4211
4212 segments[4].y1 = segments[4].y2 = top;
4213 segments[4].x1 = left;
4214 segments[4].x2 = right - 1;
4215#ifdef WIN32
4216 segments[4].x2++;
4217#endif
4218
4219 count = 5;
4220 if (entryPtr->flags & ENTRY_CLOSED) { /* Draw the vertical
4221 * line for the plus. */
4222 top = y + BUTTON_IPAD;
4223 bottom = y + height - BUTTON_IPAD;
4224 segments[5].y1 = top;
4225 segments[5].y2 = bottom - 1;
4226 segments[5].x1 = segments[5].x2 = x + width / 2;
4227#ifdef WIN32
4228 segments[5].y2++;
4229#endif
4230 count = 6;
4231 }
4232 XDrawSegments(tvPtr->display, drawable, gc, segments, count);
4233 }
4234}
4235
4236
4237/*
4238 * ---------------------------------------------------------------------------
4239 *
4240 * Blt_TreeViewGetEntryIcon --
4241 *
4242 * Selects the correct image for the entry's icon depending upon
4243 * the current state of the entry: active/inactive normal/selected.
4244 *
4245 * active - normal
4246 * active - selected
4247 * inactive - normal
4248 * inactive - selected
4249 *
4250 * Results:
4251 * Returns the image for the icon.
4252 *
4253 * ---------------------------------------------------------------------------
4254 */
4255TreeViewIcon
4256Blt_TreeViewGetEntryIcon(TreeView *tvPtr, TreeViewEntry *entryPtr)
4257{
4258 TreeViewIcon *icons;
4259 TreeViewIcon icon;
4260
4261 int isActive, hasFocus;
4262
4263 isActive = (entryPtr == tvPtr->activePtr);
4264 hasFocus = (entryPtr == tvPtr->focusPtr);
4265 icons = NULL;
4266 if (isActive) {
4267 icons = CHOOSE(tvPtr->activeIcons, entryPtr->activeIcons);
4268 }
4269 if (icons == NULL) {
4270 icons = CHOOSE(tvPtr->icons, entryPtr->icons);
4271
4272 }
4273 icon = NULL;
4274 if (icons != NULL) { /* Selected or normal icon? */
4275 icon = icons[0];
4276 if ((hasFocus) && (icons[1] != NULL)) {
4277 icon = icons[1];
4278 }
4279 }
4280 return icon;
4281}
4282
4283
4284int
4285Blt_TreeViewDrawIcon(
4286 TreeView *tvPtr, /* Widget record containing the attribute
4287 * information for buttons. */
4288 TreeViewEntry *entryPtr, /* Entry to display. */
4289 Drawable drawable, /* Pixmap or window to draw into. */
4290 int x,
4291 int y)
4292{
4293 TreeViewIcon icon;
4294
4295 icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
4296
4297 if (icon != NULL) { /* Icon or default icon bitmap? */
4298 int entryHeight;
4299 int level;
4300 int maxY;
4301 int top, bottom;
4302 int topInset, botInset;
4303 int width, height;
4304
4305 level = DEPTH(tvPtr, entryPtr->node);
4306 entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
4307 tvPtr->button.height);
4308 height = TreeViewIconHeight(icon);
4309 width = TreeViewIconWidth(icon);
4310 if (tvPtr->flatView) {
4311 x += (ICONWIDTH(0) - width) / 2;
4312 } else {
4313 x += (ICONWIDTH(level + 1) - width) / 2;
4314 }
4315 y += (entryHeight - height) / 2;
4316 botInset = tvPtr->inset - INSET_PAD;
4317 topInset = tvPtr->titleHeight + tvPtr->inset;
4318 maxY = Tk_Height(tvPtr->tkwin) - botInset;
4319 top = 0;
4320 bottom = y + height;
4321 if (y < topInset) {
4322 height += y - topInset;
4323 top = -y + topInset;
4324 y = topInset;
4325 } else if (bottom >= maxY) {
4326 height = maxY - y;
4327 }
4328 Tk_RedrawImage(TreeViewIconBits(icon), 0, top, width, height,
4329 drawable, x, y);
4330 }
4331 return (icon != NULL);
4332}
4333
4334static int
4335DrawLabel(
4336 TreeView *tvPtr, /* Widget record. */
4337 TreeViewEntry *entryPtr, /* Entry attribute information. */
4338 Drawable drawable, /* Pixmap or window to draw into. */
4339 int x,
4340 int y)
4341{
4342 char *label;
4343 int entryHeight;
4344 int isFocused;
4345 int width, height; /* Width and height of label. */
4346 int selected;
4347
4348 entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
4349 tvPtr->button.height);
4350 isFocused = ((entryPtr == tvPtr->focusPtr) &&
4351 (tvPtr->flags & TV_FOCUS));
4352 selected = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
4353
4354 /* Includes padding, selection 3-D border, and focus outline. */
4355 width = entryPtr->labelWidth;
4356 height = entryPtr->labelHeight;
4357
4358 /* Center the label, if necessary, vertically along the entry row. */
4359 if (height < entryHeight) {
4360 y += (entryHeight - height) / 2;
4361 }
4362 if (isFocused) { /* Focus outline */
4363 if (selected) {
4364 XColor *color;
4365
4366 color = SELECT_FG(tvPtr);
4367 XSetForeground(tvPtr->display, tvPtr->focusGC, color->pixel);
4368 }
4369 XDrawRectangle(tvPtr->display, drawable, tvPtr->focusGC, x, y,
4370 width - 1, height - 1);
4371 if (selected) {
4372 XSetForeground(tvPtr->display, tvPtr->focusGC,
4373 tvPtr->focusColor->pixel);
4374 }
4375 }
4376 x += FOCUS_WIDTH + LABEL_PADX + tvPtr->selBorderWidth;
4377 y += FOCUS_WIDTH + LABEL_PADY + tvPtr->selBorderWidth;
4378
4379 label = GETLABEL(entryPtr);
4380 if (label[0] != '\0') {
4381 GC gc;
4382 TreeViewStyle *stylePtr;
4383 TextStyle ts;
4384 Tk_Font font;
4385 XColor *normalColor, *activeColor;
4386
4387 stylePtr = tvPtr->treeColumn.stylePtr;
4388 font = entryPtr->font;
4389 if (font == NULL) {
4390 font = Blt_TreeViewGetStyleFont(tvPtr, stylePtr);
4391 }
4392 normalColor = entryPtr->color;
4393 if (normalColor == NULL) {
4394 normalColor = Blt_TreeViewGetStyleFg(tvPtr, stylePtr);
4395 }
4396 activeColor = (selected) ? SELECT_FG(tvPtr) : normalColor;
4397 gc = entryPtr->gc;
4398 if (gc == NULL) {
4399 gc = Blt_TreeViewGetStyleGC(stylePtr);
4400 }
4401 Blt_SetDrawTextStyle(&ts, font, gc, normalColor, activeColor,
4402 entryPtr->shadow.color, 0.0, TK_ANCHOR_NW, TK_JUSTIFY_LEFT, 0,
4403 entryPtr->shadow.offset);
4404 ts.state = (selected || (entryPtr->gc == NULL)) ? STATE_ACTIVE : 0;
4405 Blt_DrawTextLayout(tvPtr->tkwin, drawable, entryPtr->textPtr,
4406 &ts, x, y);
4407 }
4408 return entryHeight;
4409}
4410
4411/*
4412 * ---------------------------------------------------------------------------
4413 *
4414 * DrawFlatEntry --
4415 *
4416 * Draws a button for the given entry. Note that buttons should only
4417 * be drawn if the entry has sub-entries to be opened or closed. It's
4418 * the responsibility of the calling routine to ensure this.
4419 *
4420 * The button is drawn centered in the region immediately to the left
4421 * of the origin of the entry (computed in the layout routines). The
4422 * height and width of the button were previously calculated from the
4423 * average row height.
4424 *
4425 * button height = entry height - (2 * some arbitrary padding).
4426 * button width = button height.
4427 *
4428 * The button has a border. The symbol (either a plus or minus) is
4429 * slight smaller than the width or height minus the border.
4430 *
4431 * x,y origin of entry
4432 *
4433 * +---+
4434 * | + | icon label
4435 * +---+
4436 * closed
4437 *
4438 * |----|----| horizontal offset
4439 *
4440 * +---+
4441 * | - | icon label
4442 * +---+
4443 * open
4444 *
4445 * Results:
4446 * None.
4447 *
4448 * Side Effects:
4449 * A button is drawn for the entry.
4450 *
4451 * ---------------------------------------------------------------------------
4452 */
4453static void
4454DrawFlatEntry(
4455 TreeView *tvPtr, /* Widget record containing the attribute
4456 * information for buttons. */
4457 TreeViewEntry *entryPtr, /* Entry to be drawn. */
4458 Drawable drawable) /* Pixmap or window to draw into. */
4459{
4460 int level;
4461 int x, y;
4462
4463 entryPtr->flags &= ~ENTRY_REDRAW;
4464
4465 x = SCREENX(tvPtr, entryPtr->worldX);
4466 y = SCREENY(tvPtr, entryPtr->worldY);
4467 if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y)) {
4468 x -= (DEF_ICON_WIDTH * 2) / 3;
4469 }
4470 level = 0;
4471 x += ICONWIDTH(level);
4472 /* Entry label. */
4473 DrawLabel(tvPtr, entryPtr, drawable, x, y);
4474}
4475
4476/*
4477 * ---------------------------------------------------------------------------
4478 *
4479 * DrawTreeEntry --
4480 *
4481 * Draws a button for the given entry. Note that buttons should only
4482 * be drawn if the entry has sub-entries to be opened or closed. It's
4483 * the responsibility of the calling routine to ensure this.
4484 *
4485 * The button is drawn centered in the region immediately to the left
4486 * of the origin of the entry (computed in the layout routines). The
4487 * height and width of the button were previously calculated from the
4488 * average row height.
4489 *
4490 * button height = entry height - (2 * some arbitrary padding).
4491 * button width = button height.
4492 *
4493 * The button has a border. The symbol (either a plus or minus) is
4494 * slight smaller than the width or height minus the border.
4495 *
4496 * x,y origin of entry
4497 *
4498 * +---+
4499 * | + | icon label
4500 * +---+
4501 * closed
4502 *
4503 * |----|----| horizontal offset
4504 *
4505 * +---+
4506 * | - | icon label
4507 * +---+
4508 * open
4509 *
4510 * Results:
4511 * None.
4512 *
4513 * Side Effects:
4514 * A button is drawn for the entry.
4515 *
4516 * ---------------------------------------------------------------------------
4517 */
4518static void
4519DrawTreeEntry(
4520 TreeView *tvPtr, /* Widget record. */
4521 TreeViewEntry *entryPtr, /* Entry to be drawn. */
4522 Drawable drawable) /* Pixmap or window to draw into. */
4523{
4524 TreeViewButton *buttonPtr = &tvPtr->button;
4525 int buttonY;
4526 int level;
4527 int width, height;
4528 int x, y;
4529 int x1, y1, x2, y2;
4530
4531 entryPtr->flags &= ~ENTRY_REDRAW;
4532
4533 x = SCREENX(tvPtr, entryPtr->worldX);
4534 y = SCREENY(tvPtr, entryPtr->worldY);
4535
4536 level = DEPTH(tvPtr, entryPtr->node);
4537 width = ICONWIDTH(level);
4538 height = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
4539 buttonPtr->height);
4540
4541 entryPtr->buttonX = (width - buttonPtr->width) / 2;
4542 entryPtr->buttonY = (height - buttonPtr->height) / 2;
4543
4544 buttonY = y + entryPtr->buttonY;
4545
4546 x1 = x + (width / 2);
4547 y1 = y2 = buttonY + (buttonPtr->height / 2);
4548 x2 = x1 + (ICONWIDTH(level) + ICONWIDTH(level + 1)) / 2;
4549
4550 if ((Blt_TreeNodeParent(entryPtr->node) != NULL) &&
4551 (tvPtr->lineWidth > 0)) {
4552 /*
4553 * For every node except root, draw a horizontal line from
4554 * the vertical bar to the middle of the icon.
4555 */
4556 XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x1, y1, x2, y2);
4557 }
4558 if (((entryPtr->flags & ENTRY_CLOSED) == 0) && (tvPtr->lineWidth > 0)) {
4559 /*
4560 * Entry is open, draw vertical line.
4561 */
4562 y2 = y1 + entryPtr->vertLineLength;
4563 if (y2 > Tk_Height(tvPtr->tkwin)) {
4564 y2 = Tk_Height(tvPtr->tkwin); /* Clip line at window border. */
4565 }
4566 XDrawLine(tvPtr->display, drawable, tvPtr->lineGC, x2, y1, x2, y2);
4567 }
4568 if ((entryPtr->flags & ENTRY_HAS_BUTTON) && (entryPtr != tvPtr->rootPtr)) {
4569 /*
4570 * Except for the root, draw a button for every entry that
4571 * needs one. The displayed button can be either an icon (Tk
4572 * image) or a line drawing (rectangle with plus or minus
4573 * sign).
4574 */
4575 Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, x + entryPtr->buttonX,
4576 y + entryPtr->buttonY);
4577 }
4578 x += ICONWIDTH(level);
4579
4580 if (!Blt_TreeViewDrawIcon(tvPtr, entryPtr, drawable, x, y)) {
4581 x -= (DEF_ICON_WIDTH * 2) / 3;
4582 }
4583 x += ICONWIDTH(level + 1) + 4;
4584
4585 /* Entry label. */
4586 DrawLabel(tvPtr, entryPtr, drawable, x, y);
4587}
4588
4589/*
4590 * ---------------------------------------------------------------------------
4591 *
4592 * Blt_TreeViewDrawValue --
4593 *
4594 * Draws a column value for the given entry.
4595 *
4596 * Results:
4597 * None.
4598 *
4599 * Side Effects:
4600 * A button is drawn for the entry.
4601 *
4602 * ---------------------------------------------------------------------------
4603 */
4604void
4605Blt_TreeViewDrawValue(
4606 TreeView *tvPtr, /* Widget record. */
4607 TreeViewEntry *entryPtr, /* Node of entry to be drawn. */
4608 TreeViewValue *valuePtr,
4609 Drawable drawable, /* Pixmap or window to draw into. */
4610 int x,
4611 int y)
4612{
4613 TreeViewStyle *stylePtr;
4614
4615 stylePtr = CHOOSE(valuePtr->columnPtr->stylePtr, valuePtr->stylePtr);
4616 (*stylePtr->classPtr->drawProc)(tvPtr, drawable, entryPtr, valuePtr,
4617 stylePtr, x, y);
4618}
4619
4620static void
4621DrawTitle(
4622 TreeView *tvPtr,
4623 TreeViewColumn *columnPtr,
4624 Drawable drawable,
4625 int x)
4626{
4627 GC gc;
4628 Tk_3DBorder border;
4629 XColor *fgColor;
4630 int columnWidth;
4631 int width;
4632 int x0, cx, xOffset;
4633
4634 columnWidth = columnPtr->width;
4635 cx = x;
4636 if (columnPtr->position == Blt_ChainGetLength(tvPtr->colChainPtr)) {
4637 /* If there's any room left over, let the last column take it. */
4638 columnWidth = Tk_Width(tvPtr->tkwin) - x;
4639 } else if (columnPtr->position == 1) {
4640 columnWidth += x;
4641 cx = 0;
4642 }
4643 x0 = x + columnPtr->borderWidth;
4644
4645 if (columnPtr == tvPtr->activeTitleColumnPtr) {
4646 border = columnPtr->activeTitleBorder;
4647 gc = columnPtr->activeTitleGC;
4648 fgColor = columnPtr->activeTitleFgColor;
4649 } else {
4650 border = columnPtr->titleBorder;
4651 gc = columnPtr->titleGC;
4652 fgColor = columnPtr->titleFgColor;
4653 }
4654 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, cx + 1,
4655 tvPtr->inset + 1, columnWidth - 2, tvPtr->titleHeight - 2, 0,
4656 TK_RELIEF_FLAT);
4657 width = columnPtr->width;
4658 xOffset = x0 + columnPtr->pad.side1 + 1;
4659
4660 if (width > columnPtr->titleWidth) {
4661 x += (width - columnPtr->titleWidth) / 2;
4662 }
4663 if (columnPtr == tvPtr->sortColumnPtr) {
4664 /* Make sure there's room for the sorting-direction triangle. */
4665 if ((x - xOffset) <= (STD_ARROW_WIDTH + 4)) {
4666 x = xOffset + STD_ARROW_WIDTH + 4;
4667 }
4668 }
4669 if (columnPtr->titleIcon != NULL) {
4670 int iconX, iconY, iconWidth, iconHeight;
4671
4672 iconHeight = TreeViewIconHeight(columnPtr->titleIcon);
4673 iconWidth = TreeViewIconWidth(columnPtr->titleIcon);
4674 iconX = x;
4675 if (columnPtr->titleTextPtr != NULL) {
4676 iconX += 2;
4677 }
4678 iconY = tvPtr->inset + (tvPtr->titleHeight - iconHeight) / 2;
4679 Tk_RedrawImage(TreeViewIconBits(columnPtr->titleIcon), 0, 0,
4680 iconWidth, iconHeight, drawable, iconX, iconY);
4681 x += iconWidth + 6;
4682 }
4683 if (columnPtr->titleTextPtr != NULL) {
4684 TextStyle ts;
4685
4686 Blt_SetDrawTextStyle(&ts, columnPtr->titleFont, gc, fgColor,
4687 SELECT_FG(tvPtr), columnPtr->titleShadow.color, 0.0, TK_ANCHOR_NW,
4688 TK_JUSTIFY_LEFT, 0, columnPtr->titleShadow.offset);
4689 Blt_DrawTextLayout(tvPtr->tkwin, drawable, columnPtr->titleTextPtr, &ts,
4690 x, tvPtr->inset + 1);
4691 }
4692 if ((columnPtr == tvPtr->sortColumnPtr) && (tvPtr->flatView)) {
4693 Blt_DrawArrow(tvPtr->display, drawable, gc,
4694 xOffset + ARROW_OFFSET,
4695 tvPtr->inset + tvPtr->titleHeight / 2, STD_ARROW_HEIGHT,
4696 (tvPtr->sortDecreasing) ? ARROW_UP : ARROW_DOWN);
4697 }
4698 Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, cx, tvPtr->inset,
4699 columnWidth, tvPtr->titleHeight, columnPtr->titleBorderWidth,
4700 columnPtr->titleRelief);
4701}
4702
4703void
4704Blt_TreeViewDrawHeadings(tvPtr, drawable)
4705 TreeView *tvPtr;
4706 Drawable drawable;
4707{
4708 Blt_ChainLink *linkPtr;
4709 TreeViewColumn *columnPtr;
4710 int x;
4711
4712 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
4713 linkPtr = Blt_ChainNextLink(linkPtr)) {
4714 columnPtr = Blt_ChainGetValue(linkPtr);
4715 if (columnPtr->hidden) {
4716 continue;
4717 }
4718 x = SCREENX(tvPtr, columnPtr->worldX);
4719 if ((x + columnPtr->width) < 0) {
4720 continue; /* Don't draw columns before the left edge. */
4721 }
4722 if (x > Tk_Width(tvPtr->tkwin)) {
4723 break; /* Discontinue when a column starts beyond
4724 * the right edge. */
4725 }
4726 DrawTitle(tvPtr, columnPtr, drawable, x);
4727 }
4728}
4729
4730static void
4731DrawTreeView(tvPtr, drawable, x)
4732 TreeView *tvPtr;
4733 Drawable drawable;
4734 int x;
4735{
4736 register TreeViewEntry **p;
4737 Tk_3DBorder selBorder;
4738
4739 /*
4740 * Draw the backgrounds of selected entries first. The vertical
4741 * lines connecting child entries will be draw on top.
4742 */
4743 selBorder = SELECT_BORDER(tvPtr);
4744 for (p = tvPtr->visibleArr; *p != NULL; p++) {
4745 if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
4746 int y;
4747
4748 y = SCREENY(tvPtr, (*p)->worldY) - 1;
4749 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, x, y,
4750 tvPtr->treeColumn.width, (*p)->height + 1,
4751 tvPtr->selBorderWidth, tvPtr->selRelief);
4752 }
4753 }
4754 if ((tvPtr->lineWidth > 0) && (tvPtr->nVisible > 0)) {
4755 /* Draw all the vertical lines from topmost node. */
4756 DrawVerticals(tvPtr, tvPtr->visibleArr[0], drawable);
4757 }
4758
4759 for (p = tvPtr->visibleArr; *p != NULL; p++) {
4760 DrawTreeEntry(tvPtr, *p, drawable);
4761 }
4762}
4763
4764static void
4765DrawFlatView(
4766 TreeView *tvPtr,
4767 Drawable drawable,
4768 int x)
4769{
4770 register TreeViewEntry **p;
4771 Tk_3DBorder selBorder;
4772 /*
4773 * Draw the backgrounds of selected entries first. The vertical
4774 * lines connecting child entries will be draw on top.
4775 */
4776 selBorder = SELECT_BORDER(tvPtr);
4777 for (p = tvPtr->visibleArr; *p != NULL; p++) {
4778 if (Blt_TreeViewEntryIsSelected(tvPtr, *p)) {
4779 int y;
4780
4781 y = SCREENY(tvPtr, (*p)->worldY) - 1;
4782 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder, x, y,
4783 tvPtr->treeColumn.width, (*p)->height + 1,
4784 tvPtr->selBorderWidth, tvPtr->selRelief);
4785 }
4786 }
4787 for (p = tvPtr->visibleArr; *p != NULL; p++) {
4788 DrawFlatEntry(tvPtr, *p, drawable);
4789 }
4790}
4791
4792void
4793Blt_TreeViewDrawOuterBorders(tvPtr, drawable)
4794 TreeView *tvPtr;
4795 Drawable drawable;
4796{
4797 /* Draw 3D border just inside of the focus highlight ring. */
4798 if ((tvPtr->borderWidth > 0) && (tvPtr->relief != TK_RELIEF_FLAT)) {
4799 Blt_Draw3DRectangle(tvPtr->tkwin, drawable, tvPtr->border,
4800 tvPtr->highlightWidth, tvPtr->highlightWidth,
4801 Tk_Width(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
4802 Tk_Height(tvPtr->tkwin) - 2 * tvPtr->highlightWidth,
4803 tvPtr->borderWidth, tvPtr->relief);
4804 }
4805 /* Draw focus highlight ring. */
4806 if (tvPtr->highlightWidth > 0) {
4807 XColor *color;
4808 GC gc;
4809
4810 color = (tvPtr->flags & TV_FOCUS)
4811 ? tvPtr->highlightColor : tvPtr->highlightBgColor;
4812 gc = Tk_GCForColor(color, drawable);
4813 Tk_DrawFocusHighlight(tvPtr->tkwin, gc, tvPtr->highlightWidth,
4814 drawable);
4815 }
4816 tvPtr->flags &= ~TV_BORDERS;
4817}
4818
4819/*
4820 * ----------------------------------------------------------------------
4821 *
4822 * DisplayTreeView --
4823 *
4824 * This procedure is invoked to display the widget.
4825 *
4826 * Recompute the layout of the text if necessary. This is
4827 * necessary if the world coordinate system has changed.
4828 * Specifically, the following may have occurred:
4829 *
4830 * 1. a text attribute has changed (font, linespacing, etc.).
4831 * 2. an entry's option changed, possibly resizing the entry.
4832 *
4833 * This is deferred to the display routine since potentially
4834 * many of these may occur.
4835 *
4836 * Set the vertical and horizontal scrollbars. This is done
4837 * here since the window width and height are needed for the
4838 * scrollbar calculations.
4839 *
4840 * Results:
4841 * None.
4842 *
4843 * Side effects:
4844 * The widget is redisplayed.
4845 *
4846 * ----------------------------------------------------------------------
4847 */
4848static void
4849DisplayTreeView(ClientData clientData) /* Information about widget. */
4850{
4851 Blt_ChainLink *linkPtr;
4852 TreeView *tvPtr = clientData;
4853 TreeViewColumn *columnPtr;
4854 Pixmap drawable;
4855 int width, height;
4856 int x;
4857
4858 tvPtr->flags &= ~TV_REDRAW;
4859 if (tvPtr->tkwin == NULL) {
4860 return; /* Window has been destroyed. */
4861 }
4862 if (tvPtr->flags & TV_LAYOUT) {
4863 /*
4864 * Recompute the layout when entries are opened/closed,
4865 * inserted/deleted, or when text attributes change (such as
4866 * font, linespacing).
4867 */
4868 Blt_TreeViewComputeLayout(tvPtr);
4869 }
4870 if (tvPtr->flags & TV_SCROLL) {
4871 /*
4872 * Scrolling means that the view port has changed and that the
4873 * visible entries need to be recomputed.
4874 */
4875 ComputeVisibleEntries(tvPtr);
4876
4877 width = VPORTWIDTH(tvPtr);
4878 height = VPORTHEIGHT(tvPtr);
4879 if (tvPtr->flags & TV_XSCROLL) {
4880 if (tvPtr->xScrollCmdPrefix != NULL) {
4881 Blt_UpdateScrollbar(tvPtr->interp, tvPtr->xScrollCmdPrefix,
4882 (double)tvPtr->xOffset / tvPtr->worldWidth,
4883 (double)(tvPtr->xOffset + width) / tvPtr->worldWidth);
4884 }
4885 }
4886 if (tvPtr->flags & TV_YSCROLL) {
4887 if (tvPtr->yScrollCmdPrefix != NULL) {
4888 Blt_UpdateScrollbar(tvPtr->interp, tvPtr->yScrollCmdPrefix,
4889 (double)tvPtr->yOffset / tvPtr->worldHeight,
4890 (double)(tvPtr->yOffset + height) / tvPtr->worldHeight);
4891 }
4892 }
4893 tvPtr->flags &= ~TV_SCROLL;
4894 }
4895 if (tvPtr->reqWidth == 0) {
4896
4897 /*
4898 * The first time through this routine, set the requested
4899 * width to the computed width. All we want is to
4900 * automatically set the width of the widget, not dynamically
4901 * grow/shrink it as attributes change.
4902 */
4903
4904 tvPtr->reqWidth = tvPtr->worldWidth + 2 * tvPtr->inset;
4905 Tk_GeometryRequest(tvPtr->tkwin, tvPtr->reqWidth, tvPtr->reqHeight);
4906 }
4907 if (!Tk_IsMapped(tvPtr->tkwin)) {
4908 return;
4909 }
4910
4911 drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin),
4912 Tk_Width(tvPtr->tkwin), Tk_Height(tvPtr->tkwin),
4913 Tk_Depth(tvPtr->tkwin));
4914 tvPtr->flags |= TV_VIEWPORT;
4915
4916 if ((tvPtr->flags & TV_RULE_ACTIVE) &&
4917 (tvPtr->resizeColumnPtr != NULL)) {
4918 Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
4919 }
4920 {
4921 register TreeViewEntry **p;
4922 Tk_3DBorder border, selBorder;
4923 int y;
4924
4925 selBorder = SELECT_BORDER(tvPtr);
4926 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4927 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4928 columnPtr = Blt_ChainGetValue(linkPtr);
4929 columnPtr->flags &= ~COLUMN_DIRTY;
4930 if (columnPtr->hidden) {
4931 continue;
4932 }
4933 x = SCREENX(tvPtr, columnPtr->worldX);
4934 if ((x + columnPtr->width) < 0) {
4935 continue; /* Don't draw columns before the left edge. */
4936 }
4937 if (x > Tk_Width(tvPtr->tkwin)) {
4938 break; /* Discontinue when a column starts beyond
4939 * the right edge. */
4940 }
4941 /* Clear the column background. */
4942 border = Blt_TreeViewGetStyleBorder(tvPtr, tvPtr->stylePtr);
4943 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
4944 columnPtr->width, Tk_Height(tvPtr->tkwin), 0, TK_RELIEF_FLAT);
4945
4946 if (columnPtr != &tvPtr->treeColumn) {
4947 TreeViewValue *valuePtr;
4948 TreeViewEntry *entryPtr;
4949
4950 for (p = tvPtr->visibleArr; *p != NULL; p++) {
4951 entryPtr = *p;
4952 y = SCREENY(tvPtr, entryPtr->worldY);
4953
4954 /* Draw the background of the value. */
4955 if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
4956 Blt_Fill3DRectangle(tvPtr->tkwin, drawable, selBorder,
4957 x, y - 1, columnPtr->width,
4958 entryPtr->height + 1, tvPtr->selBorderWidth,
4959 tvPtr->selRelief);
4960 }
4961 /* Check if there's a corresponding value in the entry. */
4962 valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
4963 if (valuePtr != NULL) {
4964 Blt_TreeViewDrawValue(tvPtr, entryPtr, valuePtr,
4965 drawable, x + columnPtr->pad.side1, y);
4966 }
4967 }
4968 } else {
4969 if (tvPtr->flatView) {
4970 DrawFlatView(tvPtr, drawable, x);
4971 } else {
4972 DrawTreeView(tvPtr, drawable, x);
4973 }
4974 }
4975 if (columnPtr->relief != TK_RELIEF_FLAT) {
4976 Blt_Draw3DRectangle(tvPtr->tkwin, drawable, border, x, 0,
4977 columnPtr->width, Tk_Height(tvPtr->tkwin),
4978 columnPtr->borderWidth, columnPtr->relief);
4979 }
4980 }
4981 }
4982 if (tvPtr->flags & TV_SHOW_COLUMN_TITLES) {
4983 Blt_TreeViewDrawHeadings(tvPtr, drawable);
4984 }
4985 Blt_TreeViewDrawOuterBorders(tvPtr, drawable);
4986 if ((tvPtr->flags & TV_RULE_NEEDED) &&
4987 (tvPtr->resizeColumnPtr != NULL)) {
4988 Blt_TreeViewDrawRule(tvPtr, tvPtr->resizeColumnPtr, drawable);
4989 }
4990 /* Now copy the new view to the window. */
4991 XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin),
4992 tvPtr->lineGC, 0, 0, Tk_Width(tvPtr->tkwin),
4993 Tk_Height(tvPtr->tkwin), 0, 0);
4994 Tk_FreePixmap(tvPtr->display, drawable);
4995 tvPtr->flags &= ~TV_VIEWPORT;
4996}
4997
4998/*
4999 *----------------------------------------------------------------------
5000 *
5001 * Blt_TreeViewSelectCmdProc --
5002 *
5003 * Invoked at the next idle point whenever the current
5004 * selection changes. Executes some application-specific code
5005 * in the -selectcommand option. This provides a way for
5006 * applications to handle selection changes.
5007 *
5008 * Results:
5009 * None.
5010 *
5011 * Side effects:
5012 * Tcl code gets executed for some application-specific task.
5013 *
5014 *----------------------------------------------------------------------
5015 */
5016void
5017Blt_TreeViewSelectCmdProc(ClientData clientData)
5018{
5019 TreeView *tvPtr = clientData;
5020
5021 Tcl_Preserve(tvPtr);
5022 if (tvPtr->selectCmd != NULL) {
5023 tvPtr->flags &= ~TV_SELECT_PENDING;
5024 if (Tcl_GlobalEval(tvPtr->interp, tvPtr->selectCmd) != TCL_OK) {
5025 Tcl_BackgroundError(tvPtr->interp);
5026 }
5027 }
5028 Tcl_Release(tvPtr);
5029}
5030
5031/*
5032 * --------------------------------------------------------------
5033 *
5034 * TreeViewObjCmd --
5035 *
5036 * This procedure is invoked to process the Tcl command that
5037 * corresponds to a widget managed by this module. See the user
5038 * documentation for details on what it does.
5039 *
5040 * Results:
5041 * A standard Tcl result.
5042 *
5043 * Side effects:
5044 * See the user documentation.
5045 *
5046 * --------------------------------------------------------------
5047 */
5048/* ARGSUSED */
5049static int
5050TreeViewObjCmd(clientData, interp, objc, objv)
5051 ClientData clientData; /* Main window associated with interpreter. */
5052 Tcl_Interp *interp; /* Current interpreter. */
5053 int objc; /* Number of arguments. */
5054 Tcl_Obj *CONST *objv; /* Argument strings. */
5055{
5056 Tcl_CmdInfo cmdInfo;
5057 Tcl_Obj *initObjv[2];
5058 TreeView *tvPtr;
5059 char *className;
5060 char *string;
5061
5062 string = Tcl_GetString(objv[0]);
5063 if (objc < 2) {
5064 Tcl_AppendResult(interp, "wrong # args: should be \"", string,
5065 " pathName ?option value?...\"", (char *)NULL);
5066 return TCL_ERROR;
5067 }
5068 className = (string[0] == 'h') ? "Hiertable" : "TreeView";
5069 tvPtr = CreateTreeView(interp, objv[1], className);
5070 if (tvPtr == NULL) {
5071 goto error;
5072 }
5073 /*
5074 * Invoke a procedure to initialize various bindings on treeview
5075 * entries. If the procedure doesn't already exist, source it
5076 * from "$blt_library/treeview.tcl". We deferred sourcing the
5077 * file until now so that the variable $blt_library could be set
5078 * within a script.
5079 */
5080 if (!Tcl_GetCommandInfo(interp, "blt::tv::Initialize", &cmdInfo)) {
5081 char cmd[200];
5082 sprintf(cmd, "set className %s\n\
5083source [file join $blt_library treeview.tcl]\n\
5084unset className\n", className);
5085 if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
5086 char info[200];
5087
5088 sprintf(info, "\n (while loading bindings for %.50s)",
5089 Tcl_GetString(objv[0]));
5090 Tcl_AddErrorInfo(interp, info);
5091 goto error;
5092 }
5093 }
5094 /*
5095 * Initialize the widget's configuration options here. The options
5096 * need to be set first, so that entry, column, and style
5097 * components can use them for their own GCs.
5098 */
5099 bltTreeViewIconsOption.clientData = tvPtr;
5100 bltTreeViewTreeOption.clientData = tvPtr;
5101 if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
5102 objc - 2, objv + 2, (char *)tvPtr, 0) != TCL_OK) {
5103 return TCL_ERROR;
5104 }
5105 if (Blt_ConfigureComponentFromObj(interp, tvPtr->tkwin, "button", "Button",
5106 bltTreeViewButtonSpecs, 0, (Tcl_Obj **)NULL, (char *)tvPtr, 0)
5107 != TCL_OK) {
5108 goto error;
5109 }
5110
5111 /*
5112 * Rebuild the widget's GC and other resources that are predicated
5113 * by the widget's configuration options. Do the same for the
5114 * default column.
5115 */
5116 if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
5117 goto error;
5118 }
5119 Blt_TreeViewUpdateColumnGCs(tvPtr, &tvPtr->treeColumn);
5120 Blt_TreeViewUpdateStyleGCs(tvPtr, tvPtr->stylePtr);
5121
5122 /*
5123 * Invoke a procedure to initialize various bindings on treeview
5124 * entries. If the procedure doesn't already exist, source it
5125 * from "$blt_library/treeview.tcl". We deferred sourcing the
5126 * file until now so that the variable $blt_library could be set
5127 * within a script.
5128 */
5129 initObjv[0] = Tcl_NewStringObj("blt::tv::Initialize", -1);
5130 initObjv[1] = objv[1];
5131 if (Tcl_EvalObjv(interp, 2, initObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
5132 goto error;
5133 }
5134 Tcl_DecrRefCount(initObjv[0]);
5135 Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tvPtr->tkwin), -1));
5136 return TCL_OK;
5137 error:
5138 Tk_DestroyWindow(tvPtr->tkwin);
5139 return TCL_ERROR;
5140}
5141
5142int
5143Blt_TreeViewInit(Tcl_Interp *interp)
5144{
5145 static Blt_ObjCmdSpec cmdSpec[] = {
5146 { "treeview", TreeViewObjCmd, },
5147 { "hiertable", TreeViewObjCmd, }
5148 };
5149
5150 if (Blt_InitObjCmd(interp, "blt", cmdSpec) == NULL) {
5151 return TCL_ERROR;
5152 }
5153 if (Blt_InitObjCmd(interp, "blt", cmdSpec + 1) == NULL) {
5154 return TCL_ERROR;
5155 }
5156 return TCL_OK;
5157}
5158
5159#endif /* NO_TREEVIEW */
Note: See TracBrowser for help on using the repository browser.