Pratt 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.exrc +61 -0
- data/.gitignore +4 -0
- data/History.txt +6 -0
- data/Manifest.txt +46 -0
- data/Pratt.gemspec +351 -0
- data/README.txt +66 -0
- data/Rakefile +85 -0
- data/TODO +54 -0
- data/VERSION +1 -0
- data/bin/pratt.rb +13 -0
- data/config.rb +34 -0
- data/lib/pratt.rb +527 -0
- data/lib/pratt/array.rb +11 -0
- data/lib/pratt/string.rb +18 -0
- data/models/app.rb +40 -0
- data/models/customer.rb +24 -0
- data/models/payment.rb +22 -0
- data/models/pratt.rb +19 -0
- data/models/project.rb +82 -0
- data/models/whence.rb +70 -0
- data/pkgs/tile-0.8.2.tar.gz +0 -0
- data/pkgs/tile-0.8.2/ANNOUNCE.txt +95 -0
- data/pkgs/tile-0.8.2/ChangeLog +4651 -0
- data/pkgs/tile-0.8.2/Makefile +250 -0
- data/pkgs/tile-0.8.2/Makefile.in +250 -0
- data/pkgs/tile-0.8.2/README.txt +86 -0
- data/pkgs/tile-0.8.2/aclocal.m4 +2 -0
- data/pkgs/tile-0.8.2/altTheme.o +0 -0
- data/pkgs/tile-0.8.2/blink.o +0 -0
- data/pkgs/tile-0.8.2/button.o +0 -0
- data/pkgs/tile-0.8.2/cache.o +0 -0
- data/pkgs/tile-0.8.2/clamTheme.o +0 -0
- data/pkgs/tile-0.8.2/classicTheme.o +0 -0
- data/pkgs/tile-0.8.2/config.log +1330 -0
- data/pkgs/tile-0.8.2/config.status +795 -0
- data/pkgs/tile-0.8.2/configure +15248 -0
- data/pkgs/tile-0.8.2/configure.in +89 -0
- data/pkgs/tile-0.8.2/demos/autocomplete.tcl +59 -0
- data/pkgs/tile-0.8.2/demos/demo.tcl +870 -0
- data/pkgs/tile-0.8.2/demos/dirbrowser.tcl +167 -0
- data/pkgs/tile-0.8.2/demos/dlgtest.tcl +97 -0
- data/pkgs/tile-0.8.2/demos/iconlib.tcl +110 -0
- data/pkgs/tile-0.8.2/demos/repeater.tcl +117 -0
- data/pkgs/tile-0.8.2/demos/toolbutton.tcl +101 -0
- data/pkgs/tile-0.8.2/doc/Geometry.3 +230 -0
- data/pkgs/tile-0.8.2/doc/INDEX.MAP +153 -0
- data/pkgs/tile-0.8.2/doc/Makefile +36 -0
- data/pkgs/tile-0.8.2/doc/TILE.XML +45 -0
- data/pkgs/tile-0.8.2/doc/Theme.3 +34 -0
- data/pkgs/tile-0.8.2/doc/button.n +75 -0
- data/pkgs/tile-0.8.2/doc/checkbutton.n +61 -0
- data/pkgs/tile-0.8.2/doc/combobox.n +98 -0
- data/pkgs/tile-0.8.2/doc/converting.txt +97 -0
- data/pkgs/tile-0.8.2/doc/dialog.n +122 -0
- data/pkgs/tile-0.8.2/doc/entry.n +438 -0
- data/pkgs/tile-0.8.2/doc/frame.n +43 -0
- data/pkgs/tile-0.8.2/doc/html/Geometry.html +304 -0
- data/pkgs/tile-0.8.2/doc/html/Theme.html +48 -0
- data/pkgs/tile-0.8.2/doc/html/button.html +120 -0
- data/pkgs/tile-0.8.2/doc/html/category-index.html +18 -0
- data/pkgs/tile-0.8.2/doc/html/checkbutton.html +94 -0
- data/pkgs/tile-0.8.2/doc/html/combobox.html +164 -0
- data/pkgs/tile-0.8.2/doc/html/converting.txt +97 -0
- data/pkgs/tile-0.8.2/doc/html/dialog.html +159 -0
- data/pkgs/tile-0.8.2/doc/html/entry.html +613 -0
- data/pkgs/tile-0.8.2/doc/html/frame.html +76 -0
- data/pkgs/tile-0.8.2/doc/html/image.html +100 -0
- data/pkgs/tile-0.8.2/doc/html/index.html +25 -0
- data/pkgs/tile-0.8.2/doc/html/keyword-index.html +228 -0
- data/pkgs/tile-0.8.2/doc/html/label.html +133 -0
- data/pkgs/tile-0.8.2/doc/html/labelframe.html +91 -0
- data/pkgs/tile-0.8.2/doc/html/manpage.css +212 -0
- data/pkgs/tile-0.8.2/doc/html/menubutton.html +63 -0
- data/pkgs/tile-0.8.2/doc/html/notebook.html +280 -0
- data/pkgs/tile-0.8.2/doc/html/paned.html +149 -0
- data/pkgs/tile-0.8.2/doc/html/progressbar.html +138 -0
- data/pkgs/tile-0.8.2/doc/html/radiobutton.html +89 -0
- data/pkgs/tile-0.8.2/doc/html/scrollbar.html +221 -0
- data/pkgs/tile-0.8.2/doc/html/separator.html +48 -0
- data/pkgs/tile-0.8.2/doc/html/sizegrip.html +62 -0
- data/pkgs/tile-0.8.2/doc/html/style.html +172 -0
- data/pkgs/tile-0.8.2/doc/html/tile-intro.html +164 -0
- data/pkgs/tile-0.8.2/doc/html/treeview.html +634 -0
- data/pkgs/tile-0.8.2/doc/html/widget.html +342 -0
- data/pkgs/tile-0.8.2/doc/image.n +81 -0
- data/pkgs/tile-0.8.2/doc/internals.txt +409 -0
- data/pkgs/tile-0.8.2/doc/label.n +75 -0
- data/pkgs/tile-0.8.2/doc/labelframe.n +64 -0
- data/pkgs/tile-0.8.2/doc/man.macros +239 -0
- data/pkgs/tile-0.8.2/doc/menubutton.n +41 -0
- data/pkgs/tile-0.8.2/doc/notebook.n +188 -0
- data/pkgs/tile-0.8.2/doc/paned.n +95 -0
- data/pkgs/tile-0.8.2/doc/progressbar.n +79 -0
- data/pkgs/tile-0.8.2/doc/radiobutton.n +57 -0
- data/pkgs/tile-0.8.2/doc/scrollbar.n +160 -0
- data/pkgs/tile-0.8.2/doc/separator.n +30 -0
- data/pkgs/tile-0.8.2/doc/sizegrip.n +53 -0
- data/pkgs/tile-0.8.2/doc/style.n +119 -0
- data/pkgs/tile-0.8.2/doc/tile-intro.n +165 -0
- data/pkgs/tile-0.8.2/doc/tmml.options +4 -0
- data/pkgs/tile-0.8.2/doc/treeview.n +415 -0
- data/pkgs/tile-0.8.2/doc/widget.n +227 -0
- data/pkgs/tile-0.8.2/doc/xml/Geometry.tmml +379 -0
- data/pkgs/tile-0.8.2/doc/xml/INDEX.MAP +153 -0
- data/pkgs/tile-0.8.2/doc/xml/Theme.tmml +63 -0
- data/pkgs/tile-0.8.2/doc/xml/button.tmml +134 -0
- data/pkgs/tile-0.8.2/doc/xml/checkbutton.tmml +119 -0
- data/pkgs/tile-0.8.2/doc/xml/combobox.tmml +184 -0
- data/pkgs/tile-0.8.2/doc/xml/dialog.tmml +195 -0
- data/pkgs/tile-0.8.2/doc/xml/entry.tmml +630 -0
- data/pkgs/tile-0.8.2/doc/xml/frame.tmml +98 -0
- data/pkgs/tile-0.8.2/doc/xml/image.tmml +101 -0
- data/pkgs/tile-0.8.2/doc/xml/label.tmml +154 -0
- data/pkgs/tile-0.8.2/doc/xml/labelframe.tmml +116 -0
- data/pkgs/tile-0.8.2/doc/xml/menubutton.tmml +80 -0
- data/pkgs/tile-0.8.2/doc/xml/notebook.tmml +306 -0
- data/pkgs/tile-0.8.2/doc/xml/paned.tmml +154 -0
- data/pkgs/tile-0.8.2/doc/xml/progressbar.tmml +151 -0
- data/pkgs/tile-0.8.2/doc/xml/radiobutton.tmml +109 -0
- data/pkgs/tile-0.8.2/doc/xml/scrollbar.tmml +233 -0
- data/pkgs/tile-0.8.2/doc/xml/separator.tmml +59 -0
- data/pkgs/tile-0.8.2/doc/xml/sizegrip.tmml +82 -0
- data/pkgs/tile-0.8.2/doc/xml/style.tmml +171 -0
- data/pkgs/tile-0.8.2/doc/xml/tile-intro.tmml +192 -0
- data/pkgs/tile-0.8.2/doc/xml/treeview.tmml +604 -0
- data/pkgs/tile-0.8.2/doc/xml/widget.tmml +372 -0
- data/pkgs/tile-0.8.2/entry.o +0 -0
- data/pkgs/tile-0.8.2/frame.o +0 -0
- data/pkgs/tile-0.8.2/generic/Makefile.in +221 -0
- data/pkgs/tile-0.8.2/generic/TODO +493 -0
- data/pkgs/tile-0.8.2/generic/altTheme.c +1172 -0
- data/pkgs/tile-0.8.2/generic/blink.c +168 -0
- data/pkgs/tile-0.8.2/generic/button.c +858 -0
- data/pkgs/tile-0.8.2/generic/cache.c +354 -0
- data/pkgs/tile-0.8.2/generic/clamTheme.c +974 -0
- data/pkgs/tile-0.8.2/generic/classicTheme.c +518 -0
- data/pkgs/tile-0.8.2/generic/configure +10334 -0
- data/pkgs/tile-0.8.2/generic/configure.in +100 -0
- data/pkgs/tile-0.8.2/generic/entry.c +1922 -0
- data/pkgs/tile-0.8.2/generic/frame.c +648 -0
- data/pkgs/tile-0.8.2/generic/gunk.h +44 -0
- data/pkgs/tile-0.8.2/generic/image.c +416 -0
- data/pkgs/tile-0.8.2/generic/label.c +663 -0
- data/pkgs/tile-0.8.2/generic/layout.c +1215 -0
- data/pkgs/tile-0.8.2/generic/manager.c +554 -0
- data/pkgs/tile-0.8.2/generic/manager.h +91 -0
- data/pkgs/tile-0.8.2/generic/notebook.c +1380 -0
- data/pkgs/tile-0.8.2/generic/paned.c +958 -0
- data/pkgs/tile-0.8.2/generic/pkgIndex.tcl.in +7 -0
- data/pkgs/tile-0.8.2/generic/progress.c +549 -0
- data/pkgs/tile-0.8.2/generic/scale.c +526 -0
- data/pkgs/tile-0.8.2/generic/scroll.c +253 -0
- data/pkgs/tile-0.8.2/generic/scrollbar.c +346 -0
- data/pkgs/tile-0.8.2/generic/separator.c +132 -0
- data/pkgs/tile-0.8.2/generic/square.c +306 -0
- data/pkgs/tile-0.8.2/generic/tagset.c +147 -0
- data/pkgs/tile-0.8.2/generic/tile.c +296 -0
- data/pkgs/tile-0.8.2/generic/tkElements.c +1280 -0
- data/pkgs/tile-0.8.2/generic/tkTheme.c +1708 -0
- data/pkgs/tile-0.8.2/generic/tkTheme.h +419 -0
- data/pkgs/tile-0.8.2/generic/tkThemeInt.h +45 -0
- data/pkgs/tile-0.8.2/generic/tkstate.c +268 -0
- data/pkgs/tile-0.8.2/generic/trace.c +145 -0
- data/pkgs/tile-0.8.2/generic/track.c +174 -0
- data/pkgs/tile-0.8.2/generic/treeview.c +3211 -0
- data/pkgs/tile-0.8.2/generic/ttk.decls +154 -0
- data/pkgs/tile-0.8.2/generic/ttkDecls.h +340 -0
- data/pkgs/tile-0.8.2/generic/ttkStubInit.c +61 -0
- data/pkgs/tile-0.8.2/generic/ttkStubLib.c +70 -0
- data/pkgs/tile-0.8.2/generic/widget.c +785 -0
- data/pkgs/tile-0.8.2/generic/widget.h +263 -0
- data/pkgs/tile-0.8.2/image.o +0 -0
- data/pkgs/tile-0.8.2/label.o +0 -0
- data/pkgs/tile-0.8.2/layout.o +0 -0
- data/pkgs/tile-0.8.2/library/altTheme.tcl +101 -0
- data/pkgs/tile-0.8.2/library/aquaTheme.tcl +62 -0
- data/pkgs/tile-0.8.2/library/button.tcl +85 -0
- data/pkgs/tile-0.8.2/library/clamTheme.tcl +139 -0
- data/pkgs/tile-0.8.2/library/classicTheme.tcl +108 -0
- data/pkgs/tile-0.8.2/library/combobox.tcl +439 -0
- data/pkgs/tile-0.8.2/library/cursors.tcl +36 -0
- data/pkgs/tile-0.8.2/library/defaults.tcl +118 -0
- data/pkgs/tile-0.8.2/library/dialog.tcl +274 -0
- data/pkgs/tile-0.8.2/library/entry.tcl +580 -0
- data/pkgs/tile-0.8.2/library/fonts.tcl +153 -0
- data/pkgs/tile-0.8.2/library/icons.tcl +105 -0
- data/pkgs/tile-0.8.2/library/keynav.tcl +192 -0
- data/pkgs/tile-0.8.2/library/menubutton.tcl +171 -0
- data/pkgs/tile-0.8.2/library/notebook.tcl +193 -0
- data/pkgs/tile-0.8.2/library/paned.tcl +87 -0
- data/pkgs/tile-0.8.2/library/progress.tcl +51 -0
- data/pkgs/tile-0.8.2/library/scale.tcl +54 -0
- data/pkgs/tile-0.8.2/library/scrollbar.tcl +125 -0
- data/pkgs/tile-0.8.2/library/sizegrip.tcl +77 -0
- data/pkgs/tile-0.8.2/library/tile.tcl +211 -0
- data/pkgs/tile-0.8.2/library/treeview.tcl +382 -0
- data/pkgs/tile-0.8.2/library/utils.tcl +254 -0
- data/pkgs/tile-0.8.2/library/winTheme.tcl +77 -0
- data/pkgs/tile-0.8.2/library/xpTheme.tcl +63 -0
- data/pkgs/tile-0.8.2/libtile0.8.2.so +0 -0
- data/pkgs/tile-0.8.2/libttkstub.a +0 -0
- data/pkgs/tile-0.8.2/license.terms +24 -0
- data/pkgs/tile-0.8.2/macosx/aquaTheme.c +1076 -0
- data/pkgs/tile-0.8.2/manager.o +0 -0
- data/pkgs/tile-0.8.2/notebook.o +0 -0
- data/pkgs/tile-0.8.2/paned.o +0 -0
- data/pkgs/tile-0.8.2/pkgIndex.tcl +3 -0
- data/pkgs/tile-0.8.2/progress.o +0 -0
- data/pkgs/tile-0.8.2/scale.o +0 -0
- data/pkgs/tile-0.8.2/scroll.o +0 -0
- data/pkgs/tile-0.8.2/scrollbar.o +0 -0
- data/pkgs/tile-0.8.2/separator.o +0 -0
- data/pkgs/tile-0.8.2/tagset.o +0 -0
- data/pkgs/tile-0.8.2/tclconfig/install-sh +119 -0
- data/pkgs/tile-0.8.2/tclconfig/tcl.m4 +4069 -0
- data/pkgs/tile-0.8.2/tclconfig/teax.m4 +109 -0
- data/pkgs/tile-0.8.2/tests/all.tcl +18 -0
- data/pkgs/tile-0.8.2/tests/bwidget.test +103 -0
- data/pkgs/tile-0.8.2/tests/cbtest.tcl +125 -0
- data/pkgs/tile-0.8.2/tests/combobox.test +51 -0
- data/pkgs/tile-0.8.2/tests/compound.tcl +92 -0
- data/pkgs/tile-0.8.2/tests/entry.test +285 -0
- data/pkgs/tile-0.8.2/tests/entrytest.tcl +78 -0
- data/pkgs/tile-0.8.2/tests/image.test +94 -0
- data/pkgs/tile-0.8.2/tests/labelframe.tcl +41 -0
- data/pkgs/tile-0.8.2/tests/labelframe.test +137 -0
- data/pkgs/tile-0.8.2/tests/layout.test +33 -0
- data/pkgs/tile-0.8.2/tests/misc.test +35 -0
- data/pkgs/tile-0.8.2/tests/nbtest.tcl +66 -0
- data/pkgs/tile-0.8.2/tests/notebook.test +500 -0
- data/pkgs/tile-0.8.2/tests/paned.test +298 -0
- data/pkgs/tile-0.8.2/tests/progress.test +92 -0
- data/pkgs/tile-0.8.2/tests/pwtest.tcl +90 -0
- data/pkgs/tile-0.8.2/tests/sbtest.tcl +79 -0
- data/pkgs/tile-0.8.2/tests/scrollbar.test +77 -0
- data/pkgs/tile-0.8.2/tests/sgtest.tcl +52 -0
- data/pkgs/tile-0.8.2/tests/testutils.tcl +20 -0
- data/pkgs/tile-0.8.2/tests/tile.test +674 -0
- data/pkgs/tile-0.8.2/tests/treetags.test +78 -0
- data/pkgs/tile-0.8.2/tests/treeview.test +563 -0
- data/pkgs/tile-0.8.2/tests/tvtest.tcl +332 -0
- data/pkgs/tile-0.8.2/tests/validate.test +278 -0
- data/pkgs/tile-0.8.2/tile.o +0 -0
- data/pkgs/tile-0.8.2/tkElements.o +0 -0
- data/pkgs/tile-0.8.2/tkTheme.o +0 -0
- data/pkgs/tile-0.8.2/tkstate.o +0 -0
- data/pkgs/tile-0.8.2/tools/genStubs.tcl +861 -0
- data/pkgs/tile-0.8.2/trace.o +0 -0
- data/pkgs/tile-0.8.2/track.o +0 -0
- data/pkgs/tile-0.8.2/treeview.o +0 -0
- data/pkgs/tile-0.8.2/ttkStubInit.o +0 -0
- data/pkgs/tile-0.8.2/ttkStubLib.o +0 -0
- data/pkgs/tile-0.8.2/widget.o +0 -0
- data/pkgs/tile-0.8.2/win/Tile.dsp +261 -0
- data/pkgs/tile-0.8.2/win/makefile.vc +527 -0
- data/pkgs/tile-0.8.2/win/monitor.c +164 -0
- data/pkgs/tile-0.8.2/win/nmakehlp.c +483 -0
- data/pkgs/tile-0.8.2/win/rules.vc +512 -0
- data/pkgs/tile-0.8.2/win/tile.rc +40 -0
- data/pkgs/tile-0.8.2/win/winTheme.c +734 -0
- data/pkgs/tile-0.8.2/win/xpTheme.c +1029 -0
- data/spec/app_spec.rb +48 -0
- data/spec/customer_spec.rb +31 -0
- data/spec/fixtures/graph.expectation +18 -0
- data/spec/payment_spec.rb +19 -0
- data/spec/pratt_spec.rb +148 -0
- data/spec/project_spec.rb +163 -0
- data/spec/rcov.opts +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/whence_spec.rb +54 -0
- data/tasks/pratt.rb +84 -0
- data/templates/model.eruby +12 -0
- data/templates/spec.eruby +8 -0
- data/views/env.rb +22 -0
- data/views/graph.eruby +20 -0
- data/views/invoice.eruby +148 -0
- data/views/main.rb +92 -0
- data/views/pid.eruby +3 -0
- data/views/pop.rb +94 -0
- data/views/pop2.rb +75 -0
- data/views/raw.eruby +11 -0
- metadata +390 -0
|
@@ -0,0 +1,3211 @@
|
|
|
1
|
+
/* treeview.c,v 1.45 2007/12/02 04:34:31 jenglish Exp
|
|
2
|
+
* Copyright (c) 2004, Joe English
|
|
3
|
+
*
|
|
4
|
+
* ttk::treeview widget implementation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include <assert.h>
|
|
8
|
+
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#include <tk.h>
|
|
11
|
+
#include "tkTheme.h"
|
|
12
|
+
#include "widget.h"
|
|
13
|
+
|
|
14
|
+
#define DEF_TREE_ROWS "10"
|
|
15
|
+
#define DEF_COLWIDTH "200"
|
|
16
|
+
#define DEF_MINWIDTH "20"
|
|
17
|
+
|
|
18
|
+
static const int DEFAULT_ROWHEIGHT = 20;
|
|
19
|
+
static const int DEFAULT_INDENT = 20;
|
|
20
|
+
static const int HALO = 4; /* separator */
|
|
21
|
+
|
|
22
|
+
#define TTK_STATE_OPEN TTK_STATE_USER1
|
|
23
|
+
#define TTK_STATE_LEAF TTK_STATE_USER2
|
|
24
|
+
|
|
25
|
+
#define STATE_CHANGED (0x100) /* item state option changed */
|
|
26
|
+
|
|
27
|
+
/*------------------------------------------------------------------------
|
|
28
|
+
* +++ Tree items.
|
|
29
|
+
*
|
|
30
|
+
* INVARIANTS:
|
|
31
|
+
* item->children ==> item->children->parent == item
|
|
32
|
+
* item->next ==> item->next->parent == item->parent
|
|
33
|
+
* item->next ==> item->next->prev == item
|
|
34
|
+
* item->prev ==> item->prev->next == item
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
typedef struct TreeItemRec TreeItem;
|
|
38
|
+
struct TreeItemRec
|
|
39
|
+
{
|
|
40
|
+
Tcl_HashEntry *entryPtr; /* Back-pointer to hash table entry */
|
|
41
|
+
TreeItem *parent; /* Parent item */
|
|
42
|
+
TreeItem *children; /* Linked list of child items */
|
|
43
|
+
TreeItem *next; /* Next sibling */
|
|
44
|
+
TreeItem *prev; /* Previous sibling */
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* Options and instance data:
|
|
48
|
+
*/
|
|
49
|
+
Ttk_State state;
|
|
50
|
+
Tcl_Obj *textObj;
|
|
51
|
+
Tcl_Obj *imageObj;
|
|
52
|
+
Tcl_Obj *valuesObj;
|
|
53
|
+
Tcl_Obj *openObj;
|
|
54
|
+
Tcl_Obj *tagsObj;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
static Tk_OptionSpec ItemOptionSpecs[] =
|
|
58
|
+
{
|
|
59
|
+
{TK_OPTION_STRING, "-text", "text", "Text",
|
|
60
|
+
"", Tk_Offset(TreeItem,textObj), -1,
|
|
61
|
+
0,0,0 },
|
|
62
|
+
{TK_OPTION_STRING, "-image", "image", "Image",
|
|
63
|
+
NULL, Tk_Offset(TreeItem,imageObj), -1,
|
|
64
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
65
|
+
{TK_OPTION_STRING, "-values", "values", "Values",
|
|
66
|
+
NULL, Tk_Offset(TreeItem,valuesObj), -1,
|
|
67
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
68
|
+
{TK_OPTION_BOOLEAN, "-open", "open", "Open",
|
|
69
|
+
"0", Tk_Offset(TreeItem,openObj), -1,
|
|
70
|
+
0,0,0 },
|
|
71
|
+
{TK_OPTION_STRING, "-tags", "tags", "Tags",
|
|
72
|
+
NULL, Tk_Offset(TreeItem,tagsObj), -1,
|
|
73
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
74
|
+
|
|
75
|
+
{TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/* + NewItem --
|
|
79
|
+
* Allocate a new, uninitialized, unlinked item
|
|
80
|
+
*/
|
|
81
|
+
static TreeItem *NewItem(void)
|
|
82
|
+
{
|
|
83
|
+
TreeItem *item = (TreeItem*)ckalloc(sizeof(*item));
|
|
84
|
+
|
|
85
|
+
item->entryPtr = 0;
|
|
86
|
+
item->parent = item->children = item->next = item->prev = NULL;
|
|
87
|
+
|
|
88
|
+
item->state = 0ul;
|
|
89
|
+
item->textObj = NULL;
|
|
90
|
+
item->imageObj = NULL;
|
|
91
|
+
item->valuesObj = NULL;
|
|
92
|
+
item->openObj = NULL;
|
|
93
|
+
item->tagsObj = NULL;
|
|
94
|
+
|
|
95
|
+
return item;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* + FreeItem --
|
|
99
|
+
* Destroy an item
|
|
100
|
+
*/
|
|
101
|
+
static void FreeItem(TreeItem *item)
|
|
102
|
+
{
|
|
103
|
+
if (item->textObj) { Tcl_DecrRefCount(item->textObj); }
|
|
104
|
+
if (item->imageObj) { Tcl_DecrRefCount(item->imageObj); }
|
|
105
|
+
if (item->valuesObj) { Tcl_DecrRefCount(item->valuesObj); }
|
|
106
|
+
if (item->openObj) { Tcl_DecrRefCount(item->openObj); }
|
|
107
|
+
if (item->tagsObj) { Tcl_DecrRefCount(item->tagsObj); }
|
|
108
|
+
ckfree((ClientData)item);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static void FreeItemCB(void *clientData) { FreeItem(clientData); }
|
|
112
|
+
|
|
113
|
+
/* + DetachItem --
|
|
114
|
+
* Unlink an item from the tree.
|
|
115
|
+
*/
|
|
116
|
+
static void DetachItem(TreeItem *item)
|
|
117
|
+
{
|
|
118
|
+
if (item->parent && item->parent->children == item)
|
|
119
|
+
item->parent->children = item->next;
|
|
120
|
+
if (item->prev)
|
|
121
|
+
item->prev->next = item->next;
|
|
122
|
+
if (item->next)
|
|
123
|
+
item->next->prev = item->prev;
|
|
124
|
+
item->next = item->prev = item->parent = NULL;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* + InsertItem --
|
|
128
|
+
* Insert an item into the tree after the specified item.
|
|
129
|
+
*
|
|
130
|
+
* Preconditions:
|
|
131
|
+
* + item is currently detached
|
|
132
|
+
* + prev != NULL ==> prev->parent == parent.
|
|
133
|
+
*/
|
|
134
|
+
static void InsertItem(TreeItem *parent, TreeItem *prev, TreeItem *item)
|
|
135
|
+
{
|
|
136
|
+
item->parent = parent;
|
|
137
|
+
item->prev = prev;
|
|
138
|
+
if (prev) {
|
|
139
|
+
item->next = prev->next;
|
|
140
|
+
prev->next = item;
|
|
141
|
+
} else {
|
|
142
|
+
item->next = parent->children;
|
|
143
|
+
parent->children = item;
|
|
144
|
+
}
|
|
145
|
+
if (item->next) {
|
|
146
|
+
item->next->prev = item;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* + NextPreorder --
|
|
151
|
+
* Return the next item in preorder traversal order.
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
static TreeItem *NextPreorder(TreeItem *item)
|
|
155
|
+
{
|
|
156
|
+
if (item->children)
|
|
157
|
+
return item->children;
|
|
158
|
+
while (!item->next) {
|
|
159
|
+
item = item->parent;
|
|
160
|
+
if (!item)
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
return item->next;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/*------------------------------------------------------------------------
|
|
167
|
+
* +++ Display items and tag options.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
typedef struct {
|
|
171
|
+
Tcl_Obj *textObj; /* taken from item / data cell */
|
|
172
|
+
Tcl_Obj *imageObj; /* taken from item */
|
|
173
|
+
Tcl_Obj *anchorObj; /* from column */
|
|
174
|
+
Tcl_Obj *backgroundObj; /* remainder from tag */
|
|
175
|
+
Tcl_Obj *foregroundObj;
|
|
176
|
+
Tcl_Obj *fontObj;
|
|
177
|
+
} DisplayItem;
|
|
178
|
+
|
|
179
|
+
static Tk_OptionSpec TagOptionSpecs[] =
|
|
180
|
+
{
|
|
181
|
+
{TK_OPTION_STRING, "-text", "text", "Text",
|
|
182
|
+
NULL, Tk_Offset(DisplayItem,textObj), -1,
|
|
183
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
184
|
+
{TK_OPTION_STRING, "-image", "image", "Image",
|
|
185
|
+
NULL, Tk_Offset(DisplayItem,imageObj), -1,
|
|
186
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
187
|
+
{TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
|
|
188
|
+
NULL, Tk_Offset(DisplayItem,anchorObj), -1,
|
|
189
|
+
TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED},
|
|
190
|
+
{TK_OPTION_STRING, "-background", "windowColor", "WindowColor", /*SB:COLOR*/
|
|
191
|
+
NULL, Tk_Offset(DisplayItem,backgroundObj), -1,
|
|
192
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
193
|
+
{TK_OPTION_STRING, "-foreground", "textColor", "TextColor", /*SB:COLOR*/
|
|
194
|
+
NULL, Tk_Offset(DisplayItem,foregroundObj), -1,
|
|
195
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
196
|
+
{TK_OPTION_STRING, "-font", "font", "Font", /* SB:FONT */
|
|
197
|
+
NULL, Tk_Offset(DisplayItem,fontObj), -1,
|
|
198
|
+
TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
|
|
199
|
+
|
|
200
|
+
{TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/*------------------------------------------------------------------------
|
|
204
|
+
* +++ Columns.
|
|
205
|
+
*
|
|
206
|
+
* There are separate option tables associated with the column record:
|
|
207
|
+
* ColumnOptionSpecs is for configuring the column,
|
|
208
|
+
* and HeadingOptionSpecs is for drawing headings.
|
|
209
|
+
*/
|
|
210
|
+
typedef struct {
|
|
211
|
+
int width; /* Column width, in pixels */
|
|
212
|
+
int minWidth; /* Minimum column width, in pixels */
|
|
213
|
+
int stretch; /* Should column stretch while resizing? */
|
|
214
|
+
Tcl_Obj *idObj; /* Column identifier, from -columns option */
|
|
215
|
+
|
|
216
|
+
Tcl_Obj *anchorObj; /* -anchor for cell data */
|
|
217
|
+
|
|
218
|
+
/* Column heading data:
|
|
219
|
+
*/
|
|
220
|
+
Tcl_Obj *headingObj; /* Heading label */
|
|
221
|
+
Tcl_Obj *headingImageObj; /* Heading image */
|
|
222
|
+
Tcl_Obj *headingAnchorObj; /* -anchor for heading label */
|
|
223
|
+
Tcl_Obj *headingCommandObj; /* Command to execute */
|
|
224
|
+
Tcl_Obj *headingStateObj; /* @@@ testing ... */
|
|
225
|
+
Ttk_State headingState; /* ... */
|
|
226
|
+
|
|
227
|
+
/* Temporary storage for cell data
|
|
228
|
+
*/
|
|
229
|
+
Tcl_Obj *data;
|
|
230
|
+
} TreeColumn;
|
|
231
|
+
|
|
232
|
+
static void InitColumn(TreeColumn *column)
|
|
233
|
+
{
|
|
234
|
+
column->width = 200;
|
|
235
|
+
column->minWidth = 20;
|
|
236
|
+
column->stretch = 1;
|
|
237
|
+
column->idObj = 0;
|
|
238
|
+
column->anchorObj = 0;
|
|
239
|
+
|
|
240
|
+
column->headingState = 0;
|
|
241
|
+
column->headingObj = 0;
|
|
242
|
+
column->headingImageObj = 0;
|
|
243
|
+
column->headingAnchorObj = 0;
|
|
244
|
+
column->headingStateObj = 0;
|
|
245
|
+
column->headingCommandObj = 0;
|
|
246
|
+
|
|
247
|
+
column->data = 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static void FreeColumn(TreeColumn *column)
|
|
251
|
+
{
|
|
252
|
+
if (column->idObj) { Tcl_DecrRefCount(column->idObj); }
|
|
253
|
+
if (column->anchorObj) { Tcl_DecrRefCount(column->anchorObj); }
|
|
254
|
+
|
|
255
|
+
if (column->headingObj) { Tcl_DecrRefCount(column->headingObj); }
|
|
256
|
+
if (column->headingImageObj) { Tcl_DecrRefCount(column->headingImageObj); }
|
|
257
|
+
if (column->headingAnchorObj) { Tcl_DecrRefCount(column->headingAnchorObj); }
|
|
258
|
+
if (column->headingStateObj) { Tcl_DecrRefCount(column->headingStateObj); }
|
|
259
|
+
if (column->headingCommandObj) { Tcl_DecrRefCount(column->headingCommandObj); }
|
|
260
|
+
|
|
261
|
+
/* Don't touch column->data, it's scratch storage */
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static Tk_OptionSpec ColumnOptionSpecs[] =
|
|
265
|
+
{
|
|
266
|
+
{TK_OPTION_INT, "-width", "width", "Width",
|
|
267
|
+
DEF_COLWIDTH, -1, Tk_Offset(TreeColumn,width),
|
|
268
|
+
0,0,GEOMETRY_CHANGED },
|
|
269
|
+
{TK_OPTION_INT, "-minwidth", "minWidth", "MinWidth",
|
|
270
|
+
DEF_MINWIDTH, -1, Tk_Offset(TreeColumn,minWidth),
|
|
271
|
+
0,0,0 },
|
|
272
|
+
{TK_OPTION_BOOLEAN, "-stretch", "stretch", "Stretch",
|
|
273
|
+
"1", -1, Tk_Offset(TreeColumn,stretch),
|
|
274
|
+
0,0,0 },
|
|
275
|
+
{TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
|
|
276
|
+
"w", Tk_Offset(TreeColumn,anchorObj), -1,
|
|
277
|
+
0,0,0 },
|
|
278
|
+
{TK_OPTION_STRING, "-id", "id", "ID",
|
|
279
|
+
NULL, Tk_Offset(TreeColumn,idObj), -1,
|
|
280
|
+
TK_OPTION_NULL_OK,0,READONLY_OPTION },
|
|
281
|
+
{TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
static Tk_OptionSpec HeadingOptionSpecs[] =
|
|
285
|
+
{
|
|
286
|
+
{TK_OPTION_STRING, "-text", "text", "Text",
|
|
287
|
+
"", Tk_Offset(TreeColumn,headingObj), -1,
|
|
288
|
+
0,0,0 },
|
|
289
|
+
{TK_OPTION_STRING, "-image", "image", "Image",
|
|
290
|
+
"", Tk_Offset(TreeColumn,headingImageObj), -1,
|
|
291
|
+
0,0,0 },
|
|
292
|
+
{TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
|
|
293
|
+
"center", Tk_Offset(TreeColumn,headingAnchorObj), -1,
|
|
294
|
+
0,0,0 },
|
|
295
|
+
{TK_OPTION_STRING, "-command", "", "",
|
|
296
|
+
"", Tk_Offset(TreeColumn,headingCommandObj), -1,
|
|
297
|
+
TK_OPTION_NULL_OK,0,0 },
|
|
298
|
+
{TK_OPTION_STRING, "state", "", "",
|
|
299
|
+
"", Tk_Offset(TreeColumn,headingStateObj), -1,
|
|
300
|
+
0,0,STATE_CHANGED },
|
|
301
|
+
{TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/*------------------------------------------------------------------------
|
|
305
|
+
* +++ -show option:
|
|
306
|
+
* TODO: Implement SHOW_BRANCHES.
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
#define SHOW_TREE (0x1) /* Show tree column? */
|
|
310
|
+
#define SHOW_HEADINGS (0x2) /* Show heading row? */
|
|
311
|
+
|
|
312
|
+
#define DEFAULT_SHOW "tree headings"
|
|
313
|
+
|
|
314
|
+
static const char *showStrings[] = {
|
|
315
|
+
"tree", "headings", NULL
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
static int GetEnumSetFromObj(
|
|
319
|
+
Tcl_Interp *interp,
|
|
320
|
+
Tcl_Obj *objPtr,
|
|
321
|
+
const char *table[],
|
|
322
|
+
unsigned *resultPtr)
|
|
323
|
+
{
|
|
324
|
+
unsigned result = 0;
|
|
325
|
+
int i, objc;
|
|
326
|
+
Tcl_Obj **objv;
|
|
327
|
+
|
|
328
|
+
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK)
|
|
329
|
+
return TCL_ERROR;
|
|
330
|
+
|
|
331
|
+
for (i = 0; i < objc; ++i) {
|
|
332
|
+
int index;
|
|
333
|
+
if (TCL_OK != Tcl_GetIndexFromObj(
|
|
334
|
+
interp, objv[i], table, "value", TCL_EXACT, &index))
|
|
335
|
+
{
|
|
336
|
+
return TCL_ERROR;
|
|
337
|
+
}
|
|
338
|
+
result |= (1 << index);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
*resultPtr = result;
|
|
342
|
+
return TCL_OK;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/*------------------------------------------------------------------------
|
|
346
|
+
* +++ Treeview widget record.
|
|
347
|
+
*
|
|
348
|
+
* Dependencies:
|
|
349
|
+
* columns, columnNames: -columns
|
|
350
|
+
* displayColumns: -columns, -displaycolumns
|
|
351
|
+
* headingHeight: [layout]
|
|
352
|
+
* rowHeight, indent: style
|
|
353
|
+
*/
|
|
354
|
+
typedef struct
|
|
355
|
+
{
|
|
356
|
+
/* Resources acquired at initialization-time:
|
|
357
|
+
*/
|
|
358
|
+
Tk_OptionTable itemOptionTable;
|
|
359
|
+
Tk_OptionTable columnOptionTable;
|
|
360
|
+
Tk_OptionTable headingOptionTable;
|
|
361
|
+
Tk_OptionTable tagOptionTable;
|
|
362
|
+
Tk_BindingTable bindingTable;
|
|
363
|
+
Ttk_TagTable tagTable;
|
|
364
|
+
|
|
365
|
+
/* Acquired in GetLayout hook:
|
|
366
|
+
*/
|
|
367
|
+
Ttk_Layout itemLayout;
|
|
368
|
+
Ttk_Layout cellLayout;
|
|
369
|
+
Ttk_Layout headingLayout;
|
|
370
|
+
Ttk_Layout rowLayout;
|
|
371
|
+
|
|
372
|
+
int headingHeight; /* Space for headings */
|
|
373
|
+
int rowHeight; /* Height of each item */
|
|
374
|
+
int indent; /* #pixels horizontal offset for child items */
|
|
375
|
+
|
|
376
|
+
/* Tree data:
|
|
377
|
+
*/
|
|
378
|
+
Tcl_HashTable items; /* Map: item name -> item */
|
|
379
|
+
int serial; /* Next item # for autogenerated names */
|
|
380
|
+
TreeItem *root; /* Root item */
|
|
381
|
+
|
|
382
|
+
TreeColumn column0; /* Column options for display column #0 */
|
|
383
|
+
TreeColumn *columns; /* Array of column options for data columns */
|
|
384
|
+
|
|
385
|
+
TreeItem *focus; /* Current focus item */
|
|
386
|
+
|
|
387
|
+
/* Widget options:
|
|
388
|
+
*/
|
|
389
|
+
Tcl_Obj *columnsObj; /* List of symbolic column names */
|
|
390
|
+
Tcl_Obj *displayColumnsObj; /* List of columns to display */
|
|
391
|
+
|
|
392
|
+
Tcl_Obj *heightObj; /* height (rows) */
|
|
393
|
+
Tcl_Obj *paddingObj; /* internal padding */
|
|
394
|
+
|
|
395
|
+
Tcl_Obj *showObj; /* -show list */
|
|
396
|
+
Tcl_Obj *selectModeObj; /* -selectmode option */
|
|
397
|
+
|
|
398
|
+
Scrollable xscroll;
|
|
399
|
+
ScrollHandle xscrollHandle;
|
|
400
|
+
Scrollable yscroll;
|
|
401
|
+
ScrollHandle yscrollHandle;
|
|
402
|
+
|
|
403
|
+
/* Derived resources:
|
|
404
|
+
*/
|
|
405
|
+
Tcl_HashTable columnNames; /* Map: column name -> column table entry */
|
|
406
|
+
int nColumns; /* #columns */
|
|
407
|
+
unsigned showFlags; /* bitmask of subparts to display */
|
|
408
|
+
|
|
409
|
+
TreeColumn **displayColumns; /* List of columns for display (incl tree) */
|
|
410
|
+
int nDisplayColumns; /* #display columns */
|
|
411
|
+
Ttk_Box headingArea; /* Display area for column headings */
|
|
412
|
+
Ttk_Box treeArea; /* Display area for tree */
|
|
413
|
+
int slack; /* Slack space (see Resizing section) */
|
|
414
|
+
|
|
415
|
+
} TreePart;
|
|
416
|
+
|
|
417
|
+
typedef struct {
|
|
418
|
+
WidgetCore core;
|
|
419
|
+
TreePart tree;
|
|
420
|
+
} Treeview;
|
|
421
|
+
|
|
422
|
+
#define USER_MASK 0x0100
|
|
423
|
+
#define COLUMNS_CHANGED (USER_MASK)
|
|
424
|
+
#define DCOLUMNS_CHANGED (USER_MASK<<1)
|
|
425
|
+
#define SCROLLCMD_CHANGED (USER_MASK<<2)
|
|
426
|
+
#define SHOW_CHANGED (USER_MASK<<3)
|
|
427
|
+
|
|
428
|
+
static const char *SelectModeStrings[] = { "none", "browse", "extended", NULL };
|
|
429
|
+
|
|
430
|
+
static Tk_OptionSpec TreeviewOptionSpecs[] =
|
|
431
|
+
{
|
|
432
|
+
WIDGET_TAKES_FOCUS,
|
|
433
|
+
|
|
434
|
+
{TK_OPTION_STRING, "-columns", "columns", "Columns",
|
|
435
|
+
"", Tk_Offset(Treeview,tree.columnsObj), -1,
|
|
436
|
+
0,0,COLUMNS_CHANGED | GEOMETRY_CHANGED /*| READONLY_OPTION*/ },
|
|
437
|
+
{TK_OPTION_STRING, "-displaycolumns","displayColumns","DisplayColumns",
|
|
438
|
+
"#all", Tk_Offset(Treeview,tree.displayColumnsObj), -1,
|
|
439
|
+
0,0,DCOLUMNS_CHANGED | GEOMETRY_CHANGED },
|
|
440
|
+
{TK_OPTION_STRING, "-show", "show", "Show",
|
|
441
|
+
DEFAULT_SHOW, Tk_Offset(Treeview,tree.showObj), -1,
|
|
442
|
+
0,0,SHOW_CHANGED | GEOMETRY_CHANGED },
|
|
443
|
+
|
|
444
|
+
{TK_OPTION_STRING_TABLE, "-selectmode", "selectMode", "SelectMode",
|
|
445
|
+
"extended", Tk_Offset(Treeview,tree.selectModeObj), -1,
|
|
446
|
+
0,(ClientData)SelectModeStrings,0 },
|
|
447
|
+
|
|
448
|
+
{TK_OPTION_PIXELS, "-height", "height", "Height",
|
|
449
|
+
DEF_TREE_ROWS, Tk_Offset(Treeview,tree.heightObj), -1,
|
|
450
|
+
0,0,GEOMETRY_CHANGED},
|
|
451
|
+
{TK_OPTION_STRING, "-padding", "padding", "Pad",
|
|
452
|
+
NULL, Tk_Offset(Treeview,tree.paddingObj), -1,
|
|
453
|
+
TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
|
|
454
|
+
|
|
455
|
+
{TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
|
|
456
|
+
NULL, -1, Tk_Offset(Treeview, tree.xscroll.scrollCmd),
|
|
457
|
+
TK_OPTION_NULL_OK, 0, SCROLLCMD_CHANGED},
|
|
458
|
+
{TK_OPTION_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
|
|
459
|
+
NULL, -1, Tk_Offset(Treeview, tree.yscroll.scrollCmd),
|
|
460
|
+
TK_OPTION_NULL_OK, 0, SCROLLCMD_CHANGED},
|
|
461
|
+
|
|
462
|
+
WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/*------------------------------------------------------------------------
|
|
466
|
+
* +++ Utilities.
|
|
467
|
+
*/
|
|
468
|
+
typedef void (*HashEntryIterator)(void *hashValue);
|
|
469
|
+
|
|
470
|
+
static void foreachHashEntry(Tcl_HashTable *ht, HashEntryIterator func)
|
|
471
|
+
{
|
|
472
|
+
Tcl_HashSearch search;
|
|
473
|
+
Tcl_HashEntry *entryPtr = Tcl_FirstHashEntry(ht, &search);
|
|
474
|
+
while (entryPtr != NULL) {
|
|
475
|
+
func(Tcl_GetHashValue(entryPtr));
|
|
476
|
+
entryPtr = Tcl_NextHashEntry(&search);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* + unshare(objPtr) --
|
|
481
|
+
* Ensure that a Tcl_Obj * has refcount 1 -- either return objPtr
|
|
482
|
+
* itself, or a duplicated copy.
|
|
483
|
+
*/
|
|
484
|
+
static Tcl_Obj *unshare(Tcl_Obj *objPtr)
|
|
485
|
+
{
|
|
486
|
+
if (Tcl_IsShared(objPtr)) {
|
|
487
|
+
Tcl_Obj *newObj = Tcl_DuplicateObj(objPtr);
|
|
488
|
+
Tcl_DecrRefCount(objPtr);
|
|
489
|
+
Tcl_IncrRefCount(newObj);
|
|
490
|
+
return newObj;
|
|
491
|
+
}
|
|
492
|
+
return objPtr;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/* DisplayLayout --
|
|
496
|
+
* Rebind, place, and draw a layout + object combination.
|
|
497
|
+
*/
|
|
498
|
+
static void DisplayLayout(
|
|
499
|
+
Ttk_Layout layout, void *recordPtr, Ttk_State state, Ttk_Box b, Drawable d)
|
|
500
|
+
{
|
|
501
|
+
Ttk_RebindSublayout(layout, recordPtr);
|
|
502
|
+
Ttk_PlaceLayout(layout, state, b);
|
|
503
|
+
Ttk_DrawLayout(layout, state, d);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* + GetColumn --
|
|
507
|
+
* Look up column by name or number.
|
|
508
|
+
* Returns: pointer to column table entry, NULL if not found.
|
|
509
|
+
* Leaves an error message in interp->result on error.
|
|
510
|
+
*/
|
|
511
|
+
static TreeColumn *GetColumn(
|
|
512
|
+
Tcl_Interp *interp, Treeview *tv, Tcl_Obj *columnIDObj)
|
|
513
|
+
{
|
|
514
|
+
Tcl_HashEntry *entryPtr;
|
|
515
|
+
int columnIndex;
|
|
516
|
+
|
|
517
|
+
/* Check for named column:
|
|
518
|
+
*/
|
|
519
|
+
entryPtr = Tcl_FindHashEntry(
|
|
520
|
+
&tv->tree.columnNames, Tcl_GetString(columnIDObj));
|
|
521
|
+
if (entryPtr) {
|
|
522
|
+
return Tcl_GetHashValue(entryPtr);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* Check for number:
|
|
526
|
+
*/
|
|
527
|
+
if (Tcl_GetIntFromObj(NULL, columnIDObj, &columnIndex) == TCL_OK) {
|
|
528
|
+
if (columnIndex < 0 || columnIndex >= tv->tree.nColumns) {
|
|
529
|
+
Tcl_ResetResult(interp);
|
|
530
|
+
Tcl_AppendResult(interp,
|
|
531
|
+
"Column index ",
|
|
532
|
+
Tcl_GetString(columnIDObj),
|
|
533
|
+
" out of bounds",
|
|
534
|
+
NULL);
|
|
535
|
+
return NULL;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return tv->tree.columns + columnIndex;
|
|
539
|
+
}
|
|
540
|
+
Tcl_ResetResult(interp);
|
|
541
|
+
Tcl_AppendResult(interp,
|
|
542
|
+
"Invalid column index ", Tcl_GetString(columnIDObj),
|
|
543
|
+
NULL);
|
|
544
|
+
return NULL;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* + FindColumn --
|
|
548
|
+
* Look up column by name, number, or display index.
|
|
549
|
+
*/
|
|
550
|
+
static TreeColumn *FindColumn(
|
|
551
|
+
Tcl_Interp *interp, Treeview *tv, Tcl_Obj *columnIDObj)
|
|
552
|
+
{
|
|
553
|
+
int colno;
|
|
554
|
+
|
|
555
|
+
if (sscanf(Tcl_GetString(columnIDObj), "#%d", &colno) == 1)
|
|
556
|
+
{ /* Display column specification, #n */
|
|
557
|
+
if (colno >= 0 && colno < tv->tree.nDisplayColumns) {
|
|
558
|
+
return tv->tree.displayColumns[colno];
|
|
559
|
+
}
|
|
560
|
+
/* else */
|
|
561
|
+
Tcl_ResetResult(interp);
|
|
562
|
+
Tcl_AppendResult(interp,
|
|
563
|
+
"Column ", Tcl_GetString(columnIDObj), " out of range",
|
|
564
|
+
NULL);
|
|
565
|
+
return NULL;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return GetColumn(interp, tv, columnIDObj);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* + FindItem --
|
|
572
|
+
* Locates the item with the specified identifier in the tree.
|
|
573
|
+
* If there is no such item, leaves an error message in interp.
|
|
574
|
+
*/
|
|
575
|
+
static TreeItem *FindItem(
|
|
576
|
+
Tcl_Interp *interp, Treeview *tv, Tcl_Obj *itemNameObj)
|
|
577
|
+
{
|
|
578
|
+
const char *itemName = Tcl_GetString(itemNameObj);
|
|
579
|
+
Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&tv->tree.items, itemName);
|
|
580
|
+
|
|
581
|
+
if (!entryPtr) {
|
|
582
|
+
Tcl_ResetResult(interp);
|
|
583
|
+
Tcl_AppendResult(interp, "Item ", itemName, " not found", NULL);
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
586
|
+
return (TreeItem*)Tcl_GetHashValue(entryPtr);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/* + GetItemListFromObj --
|
|
590
|
+
* Parse a Tcl_Obj * as a list of items.
|
|
591
|
+
* Returns a NULL-terminated array of items; result must
|
|
592
|
+
* be ckfree()d. On error, returns NULL and leaves an error
|
|
593
|
+
* message in interp.
|
|
594
|
+
*/
|
|
595
|
+
|
|
596
|
+
static TreeItem **GetItemListFromObj(
|
|
597
|
+
Tcl_Interp *interp, Treeview *tv, Tcl_Obj *objPtr)
|
|
598
|
+
{
|
|
599
|
+
TreeItem **items;
|
|
600
|
+
Tcl_Obj **elements;
|
|
601
|
+
int i, nElements;
|
|
602
|
+
|
|
603
|
+
if (Tcl_ListObjGetElements(interp,objPtr,&nElements,&elements) != TCL_OK) {
|
|
604
|
+
return NULL;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
items = (TreeItem**)ckalloc((nElements + 1)*sizeof(TreeItem*));
|
|
608
|
+
for (i = 0; i < nElements; ++i) {
|
|
609
|
+
items[i] = FindItem(interp, tv, elements[i]);
|
|
610
|
+
if (!items[i]) {
|
|
611
|
+
ckfree((ClientData)items);
|
|
612
|
+
return NULL;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
items[i] = NULL;
|
|
616
|
+
return items;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* + ItemName --
|
|
620
|
+
* Returns the item's ID.
|
|
621
|
+
*/
|
|
622
|
+
static const char *ItemName(Treeview *tv, TreeItem *item)
|
|
623
|
+
{
|
|
624
|
+
return Tcl_GetHashKey(&tv->tree.items, item->entryPtr);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/* + ItemID --
|
|
628
|
+
* Returns a fresh Tcl_Obj * (refcount 0) holding the
|
|
629
|
+
* item identifier of the specified item.
|
|
630
|
+
*/
|
|
631
|
+
static Tcl_Obj *ItemID(Treeview *tv, TreeItem *item)
|
|
632
|
+
{
|
|
633
|
+
return Tcl_NewStringObj(ItemName(tv, item), -1);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/*------------------------------------------------------------------------
|
|
637
|
+
* +++ Column configuration.
|
|
638
|
+
*/
|
|
639
|
+
|
|
640
|
+
/* + TreeviewFreeColumns --
|
|
641
|
+
* Free column data.
|
|
642
|
+
*/
|
|
643
|
+
static void TreeviewFreeColumns(Treeview *tv)
|
|
644
|
+
{
|
|
645
|
+
int i;
|
|
646
|
+
|
|
647
|
+
Tcl_DeleteHashTable(&tv->tree.columnNames);
|
|
648
|
+
Tcl_InitHashTable(&tv->tree.columnNames, TCL_STRING_KEYS);
|
|
649
|
+
|
|
650
|
+
if (tv->tree.columns) {
|
|
651
|
+
for (i = 0; i < tv->tree.nColumns; ++i)
|
|
652
|
+
FreeColumn(tv->tree.columns + i);
|
|
653
|
+
ckfree((ClientData)tv->tree.columns);
|
|
654
|
+
tv->tree.columns = 0;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* + TreeviewInitColumns --
|
|
659
|
+
* Initialize column data when -columns changes.
|
|
660
|
+
* Returns: TCL_OK or TCL_ERROR;
|
|
661
|
+
*/
|
|
662
|
+
static int TreeviewInitColumns(Tcl_Interp *interp, Treeview *tv)
|
|
663
|
+
{
|
|
664
|
+
Tcl_Obj **columns;
|
|
665
|
+
int i, ncols;
|
|
666
|
+
|
|
667
|
+
if (Tcl_ListObjGetElements(
|
|
668
|
+
interp, tv->tree.columnsObj, &ncols, &columns) != TCL_OK)
|
|
669
|
+
{
|
|
670
|
+
return TCL_ERROR;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/*
|
|
674
|
+
* Free old values:
|
|
675
|
+
*/
|
|
676
|
+
TreeviewFreeColumns(tv);
|
|
677
|
+
|
|
678
|
+
/*
|
|
679
|
+
* Initialize columns array and columnNames hash table:
|
|
680
|
+
*/
|
|
681
|
+
tv->tree.nColumns = ncols;
|
|
682
|
+
tv->tree.columns =
|
|
683
|
+
(TreeColumn*)ckalloc(tv->tree.nColumns * sizeof(TreeColumn));
|
|
684
|
+
|
|
685
|
+
for (i = 0; i < ncols; ++i) {
|
|
686
|
+
int isNew;
|
|
687
|
+
Tcl_Obj *columnName = Tcl_DuplicateObj(columns[i]);
|
|
688
|
+
|
|
689
|
+
Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(
|
|
690
|
+
&tv->tree.columnNames, Tcl_GetString(columnName), &isNew);
|
|
691
|
+
Tcl_SetHashValue(entryPtr, tv->tree.columns + i);
|
|
692
|
+
|
|
693
|
+
InitColumn(tv->tree.columns + i);
|
|
694
|
+
Tk_InitOptions(
|
|
695
|
+
interp, (ClientData)(tv->tree.columns + i),
|
|
696
|
+
tv->tree.columnOptionTable, tv->core.tkwin);
|
|
697
|
+
Tk_InitOptions(
|
|
698
|
+
interp, (ClientData)(tv->tree.columns + i),
|
|
699
|
+
tv->tree.headingOptionTable, tv->core.tkwin);
|
|
700
|
+
Tcl_IncrRefCount(columnName);
|
|
701
|
+
tv->tree.columns[i].idObj = columnName;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return TCL_OK;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* + TreeviewInitDisplayColumns --
|
|
708
|
+
* Initializes the 'displayColumns' array.
|
|
709
|
+
*
|
|
710
|
+
* Note that displayColumns[0] is always the tree column,
|
|
711
|
+
* even when SHOW_TREE is not set.
|
|
712
|
+
*
|
|
713
|
+
* @@@ TODO: disallow duplicated columns
|
|
714
|
+
*/
|
|
715
|
+
static int TreeviewInitDisplayColumns(Tcl_Interp *interp, Treeview *tv)
|
|
716
|
+
{
|
|
717
|
+
Tcl_Obj **dcolumns;
|
|
718
|
+
int index, ndcols;
|
|
719
|
+
TreeColumn **displayColumns = 0;
|
|
720
|
+
|
|
721
|
+
if (Tcl_ListObjGetElements(interp,
|
|
722
|
+
tv->tree.displayColumnsObj, &ndcols, &dcolumns) != TCL_OK) {
|
|
723
|
+
return TCL_ERROR;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (!strcmp(Tcl_GetString(tv->tree.displayColumnsObj), "#all")) {
|
|
727
|
+
ndcols = tv->tree.nColumns;
|
|
728
|
+
displayColumns = (TreeColumn**)ckalloc((ndcols+1)*sizeof(TreeColumn*));
|
|
729
|
+
for (index = 0; index < ndcols; ++index) {
|
|
730
|
+
displayColumns[index+1] = tv->tree.columns + index;
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
displayColumns = (TreeColumn**)ckalloc((ndcols+1)*sizeof(TreeColumn*));
|
|
734
|
+
for (index = 0; index < ndcols; ++index) {
|
|
735
|
+
displayColumns[index+1] = GetColumn(interp, tv, dcolumns[index]);
|
|
736
|
+
if (!displayColumns[index+1]) {
|
|
737
|
+
ckfree((ClientData)displayColumns);
|
|
738
|
+
return TCL_ERROR;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
displayColumns[0] = &tv->tree.column0;
|
|
743
|
+
|
|
744
|
+
if (tv->tree.displayColumns)
|
|
745
|
+
ckfree((ClientData)tv->tree.displayColumns);
|
|
746
|
+
tv->tree.displayColumns = displayColumns;
|
|
747
|
+
tv->tree.nDisplayColumns = ndcols + 1;
|
|
748
|
+
|
|
749
|
+
return TCL_OK;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/*------------------------------------------------------------------------
|
|
753
|
+
* +++ Resizing.
|
|
754
|
+
* Invariants: TreeWidth(tree) + slack = available space
|
|
755
|
+
*/
|
|
756
|
+
|
|
757
|
+
#define FirstColumn(tv) ((tv->tree.showFlags&SHOW_TREE) ? 0 : 1)
|
|
758
|
+
|
|
759
|
+
/* + TreeWidth --
|
|
760
|
+
* Compute the requested tree width from the sum of visible column widths.
|
|
761
|
+
*/
|
|
762
|
+
static int TreeWidth(Treeview *tv)
|
|
763
|
+
{
|
|
764
|
+
int i = FirstColumn(tv);
|
|
765
|
+
int width = 0;
|
|
766
|
+
|
|
767
|
+
while (i < tv->tree.nDisplayColumns) {
|
|
768
|
+
width += tv->tree.displayColumns[i++]->width;
|
|
769
|
+
}
|
|
770
|
+
return width;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
static int SLACKINVARIANT(Treeview *tv) {
|
|
774
|
+
return (TreeWidth(tv) + tv->tree.slack == tv->tree.treeArea.width) ;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/* + RecomputeSlack --
|
|
778
|
+
*/
|
|
779
|
+
static void RecomputeSlack(Treeview *tv)
|
|
780
|
+
{
|
|
781
|
+
tv->tree.slack = tv->tree.treeArea.width - TreeWidth(tv);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/* + PickupSlack/DepositSlack --
|
|
785
|
+
* When resizing columns, distribute extra space to 'slack' first,
|
|
786
|
+
* and only adjust column widths if 'slack' goes to zero.
|
|
787
|
+
* That is, don't bother changing column widths if the tree
|
|
788
|
+
* is already scrolled or short.
|
|
789
|
+
*/
|
|
790
|
+
static int PickupSlack(Treeview *tv, int extra)
|
|
791
|
+
{
|
|
792
|
+
int newSlack = tv->tree.slack + extra;
|
|
793
|
+
|
|
794
|
+
if ( (newSlack < 0 && 0 <= tv->tree.slack)
|
|
795
|
+
|| (newSlack > 0 && 0 >= tv->tree.slack))
|
|
796
|
+
{
|
|
797
|
+
tv->tree.slack = 0;
|
|
798
|
+
return newSlack;
|
|
799
|
+
} else {
|
|
800
|
+
tv->tree.slack = newSlack;
|
|
801
|
+
return 0;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
static void DepositSlack(Treeview *tv, int extra)
|
|
806
|
+
{
|
|
807
|
+
tv->tree.slack += extra;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/* + Stretch --
|
|
811
|
+
* Adjust width of column by N pixels, down to minimum width.
|
|
812
|
+
* Returns: #pixels actually moved.
|
|
813
|
+
*/
|
|
814
|
+
static int Stretch(TreeColumn *c, int n)
|
|
815
|
+
{
|
|
816
|
+
int newWidth = n + c->width;
|
|
817
|
+
if (newWidth < c->minWidth) {
|
|
818
|
+
n = c->minWidth - c->width;
|
|
819
|
+
c->width = c->minWidth;
|
|
820
|
+
} else {
|
|
821
|
+
c->width = newWidth;
|
|
822
|
+
}
|
|
823
|
+
return n;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/* + ShoveLeft --
|
|
827
|
+
* Adjust width of (stretchable) columns to the left by N pixels.
|
|
828
|
+
* Returns: leftover slack.
|
|
829
|
+
*/
|
|
830
|
+
static int ShoveLeft(Treeview *tv, int i, int n)
|
|
831
|
+
{
|
|
832
|
+
int first = FirstColumn(tv);
|
|
833
|
+
while (n != 0 && i >= first) {
|
|
834
|
+
TreeColumn *c = tv->tree.displayColumns[i];
|
|
835
|
+
if (c->stretch) {
|
|
836
|
+
n -= Stretch(c, n);
|
|
837
|
+
}
|
|
838
|
+
--i;
|
|
839
|
+
}
|
|
840
|
+
return n;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/* + ShoveRight --
|
|
844
|
+
* Adjust width of (stretchable) columns to the right by N pixels.
|
|
845
|
+
* Returns: leftover slack.
|
|
846
|
+
*/
|
|
847
|
+
static int ShoveRight(Treeview *tv, int i, int n)
|
|
848
|
+
{
|
|
849
|
+
while (n != 0 && i < tv->tree.nDisplayColumns) {
|
|
850
|
+
TreeColumn *c = tv->tree.displayColumns[i];
|
|
851
|
+
if (c->stretch) {
|
|
852
|
+
n -= Stretch(c, n);
|
|
853
|
+
}
|
|
854
|
+
++i;
|
|
855
|
+
}
|
|
856
|
+
return n;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/* + DistributeWidth --
|
|
860
|
+
* Distribute n pixels evenly across all stretchable display columns.
|
|
861
|
+
* Returns: leftover slack.
|
|
862
|
+
* Notes:
|
|
863
|
+
* The "((++w % m) < r)" term is there so that the remainder r = n % m
|
|
864
|
+
* is distributed round-robin.
|
|
865
|
+
*/
|
|
866
|
+
static int DistributeWidth(Treeview *tv, int n)
|
|
867
|
+
{
|
|
868
|
+
int w = TreeWidth(tv);
|
|
869
|
+
int m = 0;
|
|
870
|
+
int i, d, r;
|
|
871
|
+
|
|
872
|
+
for (i = FirstColumn(tv); i < tv->tree.nDisplayColumns; ++i) {
|
|
873
|
+
if (tv->tree.displayColumns[i]->stretch) {
|
|
874
|
+
++m;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (m == 0) {
|
|
878
|
+
return n;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
d = n / m;
|
|
882
|
+
r = n % m;
|
|
883
|
+
if (r < 0) { r += m; --d; }
|
|
884
|
+
|
|
885
|
+
for (i = FirstColumn(tv); i < tv->tree.nDisplayColumns; ++i) {
|
|
886
|
+
TreeColumn *c = tv->tree.displayColumns[i];
|
|
887
|
+
if (c->stretch) {
|
|
888
|
+
n -= Stretch(c, d + ((++w % m) < r));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return n;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/* + ResizeColumns --
|
|
895
|
+
* Recompute column widths based on available width.
|
|
896
|
+
* Pick up slack first;
|
|
897
|
+
* Distribute the remainder evenly across stretchable columns;
|
|
898
|
+
* If any is still left over due to minwidth constraints, shove left.
|
|
899
|
+
*/
|
|
900
|
+
static void ResizeColumns(Treeview *tv, int newWidth)
|
|
901
|
+
{
|
|
902
|
+
int delta = newWidth - (TreeWidth(tv) + tv->tree.slack);
|
|
903
|
+
DepositSlack(tv,
|
|
904
|
+
ShoveLeft(tv, tv->tree.nDisplayColumns - 1,
|
|
905
|
+
DistributeWidth(tv, PickupSlack(tv, delta))));
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/* + DragColumn --
|
|
909
|
+
* Move the separator to the right of specified column,
|
|
910
|
+
* adjusting other column widths as necessary.
|
|
911
|
+
*/
|
|
912
|
+
static void DragColumn(Treeview *tv, int i, int delta)
|
|
913
|
+
{
|
|
914
|
+
TreeColumn *c = tv->tree.displayColumns[i];
|
|
915
|
+
int dl = delta - ShoveLeft(tv, i-1, delta - Stretch(c, delta));
|
|
916
|
+
int dr = ShoveRight(tv, i+1, PickupSlack(tv, -dl));
|
|
917
|
+
DepositSlack(tv, dr);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/*------------------------------------------------------------------------
|
|
921
|
+
* +++ Event handlers.
|
|
922
|
+
*/
|
|
923
|
+
|
|
924
|
+
static TreeItem *IdentifyItem(Treeview *tv,int y,Ttk_Box *itemPos); /*forward*/
|
|
925
|
+
|
|
926
|
+
static const unsigned int TreeviewBindEventMask =
|
|
927
|
+
KeyPressMask|KeyReleaseMask
|
|
928
|
+
| ButtonPressMask|ButtonReleaseMask
|
|
929
|
+
| PointerMotionMask|ButtonMotionMask
|
|
930
|
+
| VirtualEventMask
|
|
931
|
+
;
|
|
932
|
+
|
|
933
|
+
static void TreeviewBindEventProc(void *clientData, XEvent *event)
|
|
934
|
+
{
|
|
935
|
+
Treeview *tv = clientData;
|
|
936
|
+
TreeItem *item = NULL;
|
|
937
|
+
Ttk_Box unused;
|
|
938
|
+
void *taglist;
|
|
939
|
+
int nTags;
|
|
940
|
+
|
|
941
|
+
/*
|
|
942
|
+
* Figure out where to deliver the event.
|
|
943
|
+
*/
|
|
944
|
+
|
|
945
|
+
switch (event->type)
|
|
946
|
+
{
|
|
947
|
+
case KeyPress:
|
|
948
|
+
case KeyRelease:
|
|
949
|
+
case VirtualEvent:
|
|
950
|
+
item = tv->tree.focus;
|
|
951
|
+
break;
|
|
952
|
+
case ButtonPress:
|
|
953
|
+
case ButtonRelease:
|
|
954
|
+
item = IdentifyItem(tv, event->xbutton.y, &unused);
|
|
955
|
+
break;
|
|
956
|
+
case MotionNotify:
|
|
957
|
+
item = IdentifyItem(tv, event->xmotion.y, &unused);
|
|
958
|
+
break;
|
|
959
|
+
default:
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if (!item) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/* ASSERT: Ttk_GetTagListFromObj returns TCL_OK. */
|
|
968
|
+
Ttk_GetTagListFromObj(NULL, tv->tree.tagTable, item->tagsObj,
|
|
969
|
+
&nTags, &taglist);
|
|
970
|
+
|
|
971
|
+
/*
|
|
972
|
+
* Fire binding:
|
|
973
|
+
*/
|
|
974
|
+
Tcl_Preserve(clientData);
|
|
975
|
+
Tk_BindEvent(tv->tree.bindingTable, event, tv->core.tkwin, nTags, taglist);
|
|
976
|
+
Tcl_Release(clientData);
|
|
977
|
+
|
|
978
|
+
Ttk_FreeTagList(taglist);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/*------------------------------------------------------------------------
|
|
982
|
+
* +++ Initialization and cleanup.
|
|
983
|
+
*/
|
|
984
|
+
|
|
985
|
+
static int TreeviewInitialize(Tcl_Interp *interp, void *recordPtr)
|
|
986
|
+
{
|
|
987
|
+
Treeview *tv = recordPtr;
|
|
988
|
+
int unused;
|
|
989
|
+
|
|
990
|
+
tv->tree.itemOptionTable =
|
|
991
|
+
Tk_CreateOptionTable(interp, ItemOptionSpecs);
|
|
992
|
+
tv->tree.columnOptionTable =
|
|
993
|
+
Tk_CreateOptionTable(interp, ColumnOptionSpecs);
|
|
994
|
+
tv->tree.headingOptionTable =
|
|
995
|
+
Tk_CreateOptionTable(interp, HeadingOptionSpecs);
|
|
996
|
+
tv->tree.tagOptionTable =
|
|
997
|
+
Tk_CreateOptionTable(interp, TagOptionSpecs);
|
|
998
|
+
|
|
999
|
+
tv->tree.tagTable = Ttk_CreateTagTable(
|
|
1000
|
+
tv->tree.tagOptionTable, sizeof(DisplayItem));
|
|
1001
|
+
tv->tree.bindingTable = Tk_CreateBindingTable(interp);
|
|
1002
|
+
Tk_CreateEventHandler(tv->core.tkwin,
|
|
1003
|
+
TreeviewBindEventMask, TreeviewBindEventProc, tv);
|
|
1004
|
+
|
|
1005
|
+
tv->tree.itemLayout
|
|
1006
|
+
= tv->tree.cellLayout
|
|
1007
|
+
= tv->tree.headingLayout
|
|
1008
|
+
= tv->tree.rowLayout
|
|
1009
|
+
= 0;
|
|
1010
|
+
tv->tree.headingHeight = tv->tree.rowHeight = DEFAULT_ROWHEIGHT;
|
|
1011
|
+
tv->tree.indent = DEFAULT_INDENT;
|
|
1012
|
+
|
|
1013
|
+
Tcl_InitHashTable(&tv->tree.columnNames, TCL_STRING_KEYS);
|
|
1014
|
+
tv->tree.nColumns = tv->tree.nDisplayColumns = 0;
|
|
1015
|
+
tv->tree.columns = NULL;
|
|
1016
|
+
tv->tree.displayColumns = NULL;
|
|
1017
|
+
tv->tree.showFlags = ~0;
|
|
1018
|
+
|
|
1019
|
+
InitColumn(&tv->tree.column0);
|
|
1020
|
+
Tk_InitOptions(
|
|
1021
|
+
interp, (ClientData)(&tv->tree.column0),
|
|
1022
|
+
tv->tree.columnOptionTable, tv->core.tkwin);
|
|
1023
|
+
Tk_InitOptions(
|
|
1024
|
+
interp, (ClientData)(&tv->tree.column0),
|
|
1025
|
+
tv->tree.headingOptionTable, tv->core.tkwin);
|
|
1026
|
+
|
|
1027
|
+
Tcl_InitHashTable(&tv->tree.items, TCL_STRING_KEYS);
|
|
1028
|
+
tv->tree.serial = 0;
|
|
1029
|
+
|
|
1030
|
+
tv->tree.focus = 0;
|
|
1031
|
+
|
|
1032
|
+
/* Create root item "":
|
|
1033
|
+
*/
|
|
1034
|
+
tv->tree.root = NewItem();
|
|
1035
|
+
Tk_InitOptions(interp, (ClientData)tv->tree.root,
|
|
1036
|
+
tv->tree.itemOptionTable, tv->core.tkwin);
|
|
1037
|
+
tv->tree.root->entryPtr = Tcl_CreateHashEntry(&tv->tree.items, "", &unused);
|
|
1038
|
+
Tcl_SetHashValue(tv->tree.root->entryPtr, tv->tree.root);
|
|
1039
|
+
|
|
1040
|
+
/* Scroll handles:
|
|
1041
|
+
*/
|
|
1042
|
+
tv->tree.xscrollHandle = TtkCreateScrollHandle(&tv->core,&tv->tree.xscroll);
|
|
1043
|
+
tv->tree.yscrollHandle = TtkCreateScrollHandle(&tv->core,&tv->tree.yscroll);
|
|
1044
|
+
|
|
1045
|
+
/* Size parameters:
|
|
1046
|
+
*/
|
|
1047
|
+
tv->tree.treeArea = tv->tree.headingArea = Ttk_MakeBox(0,0,0,0);
|
|
1048
|
+
tv->tree.slack = 0;
|
|
1049
|
+
|
|
1050
|
+
return TCL_OK;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
static void TreeviewCleanup(void *recordPtr)
|
|
1054
|
+
{
|
|
1055
|
+
Treeview *tv = recordPtr;
|
|
1056
|
+
|
|
1057
|
+
Tk_DeleteEventHandler(tv->core.tkwin,
|
|
1058
|
+
TreeviewBindEventMask, TreeviewBindEventProc, tv);
|
|
1059
|
+
Tk_DeleteBindingTable(tv->tree.bindingTable);
|
|
1060
|
+
Ttk_DeleteTagTable(tv->tree.tagTable);
|
|
1061
|
+
|
|
1062
|
+
if (tv->tree.itemLayout) Ttk_FreeLayout(tv->tree.itemLayout);
|
|
1063
|
+
if (tv->tree.cellLayout) Ttk_FreeLayout(tv->tree.cellLayout);
|
|
1064
|
+
if (tv->tree.headingLayout) Ttk_FreeLayout(tv->tree.headingLayout);
|
|
1065
|
+
if (tv->tree.rowLayout) Ttk_FreeLayout(tv->tree.rowLayout);
|
|
1066
|
+
|
|
1067
|
+
TreeviewFreeColumns(tv);
|
|
1068
|
+
|
|
1069
|
+
if (tv->tree.displayColumns)
|
|
1070
|
+
Tcl_Free((ClientData)tv->tree.displayColumns);
|
|
1071
|
+
|
|
1072
|
+
foreachHashEntry(&tv->tree.items, FreeItemCB);
|
|
1073
|
+
Tcl_DeleteHashTable(&tv->tree.items);
|
|
1074
|
+
|
|
1075
|
+
TtkFreeScrollHandle(tv->tree.xscrollHandle);
|
|
1076
|
+
TtkFreeScrollHandle(tv->tree.yscrollHandle);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/* + TreeviewConfigure --
|
|
1080
|
+
* Configuration widget hook.
|
|
1081
|
+
*
|
|
1082
|
+
* BUG: If user sets -columns and -displaycolumns, but -displaycolumns
|
|
1083
|
+
* has an error, the widget is left in an inconsistent state.
|
|
1084
|
+
*/
|
|
1085
|
+
static int
|
|
1086
|
+
TreeviewConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
|
|
1087
|
+
{
|
|
1088
|
+
Treeview *tv = recordPtr;
|
|
1089
|
+
unsigned showFlags = tv->tree.showFlags;
|
|
1090
|
+
|
|
1091
|
+
if (mask & COLUMNS_CHANGED) {
|
|
1092
|
+
if (TreeviewInitColumns(interp, tv) != TCL_OK)
|
|
1093
|
+
return TCL_ERROR;
|
|
1094
|
+
mask |= DCOLUMNS_CHANGED;
|
|
1095
|
+
}
|
|
1096
|
+
if (mask & DCOLUMNS_CHANGED) {
|
|
1097
|
+
if (TreeviewInitDisplayColumns(interp, tv) != TCL_OK)
|
|
1098
|
+
return TCL_ERROR;
|
|
1099
|
+
}
|
|
1100
|
+
if (mask & SCROLLCMD_CHANGED) {
|
|
1101
|
+
TtkScrollbarUpdateRequired(tv->tree.xscrollHandle);
|
|
1102
|
+
TtkScrollbarUpdateRequired(tv->tree.yscrollHandle);
|
|
1103
|
+
}
|
|
1104
|
+
if ( (mask & SHOW_CHANGED)
|
|
1105
|
+
&& GetEnumSetFromObj(
|
|
1106
|
+
interp,tv->tree.showObj,showStrings,&showFlags) != TCL_OK)
|
|
1107
|
+
{
|
|
1108
|
+
return TCL_ERROR;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
|
|
1112
|
+
return TCL_ERROR;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
tv->tree.showFlags = showFlags;
|
|
1116
|
+
|
|
1117
|
+
if (mask & (SHOW_CHANGED | DCOLUMNS_CHANGED)) {
|
|
1118
|
+
RecomputeSlack(tv);
|
|
1119
|
+
}
|
|
1120
|
+
return TCL_OK;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* + ConfigureItem --
|
|
1124
|
+
* Set item options.
|
|
1125
|
+
*/
|
|
1126
|
+
static int ConfigureItem(
|
|
1127
|
+
Tcl_Interp *interp, Treeview *tv, TreeItem *item,
|
|
1128
|
+
int objc, Tcl_Obj *const objv[])
|
|
1129
|
+
{
|
|
1130
|
+
Tk_SavedOptions savedOptions;
|
|
1131
|
+
|
|
1132
|
+
if (Tk_SetOptions(interp, (ClientData)item, tv->tree.itemOptionTable,
|
|
1133
|
+
objc, objv, tv->core.tkwin,&savedOptions,0) != TCL_OK)
|
|
1134
|
+
{
|
|
1135
|
+
return TCL_ERROR;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/* Make sure that -values is a valid list:
|
|
1139
|
+
*/
|
|
1140
|
+
if (item->valuesObj) {
|
|
1141
|
+
int unused;
|
|
1142
|
+
if (Tcl_ListObjLength(interp, item->valuesObj, &unused) != TCL_OK)
|
|
1143
|
+
goto error;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Validate -image option.
|
|
1147
|
+
*/
|
|
1148
|
+
if (item->imageObj) {
|
|
1149
|
+
Ttk_ImageSpec *imageSpec =
|
|
1150
|
+
TtkGetImageSpec(interp, tv->core.tkwin, item->imageObj);
|
|
1151
|
+
if (!imageSpec) {
|
|
1152
|
+
goto error;
|
|
1153
|
+
}
|
|
1154
|
+
TtkFreeImageSpec(imageSpec); /* @@@TODO: Keep this around */
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/* Keep TTK_STATE_OPEN flag in sync with item->openObj.
|
|
1158
|
+
* We use both a state flag and a Tcl_Obj* resource so elements
|
|
1159
|
+
* can access the value in either way.
|
|
1160
|
+
*/
|
|
1161
|
+
if (item->openObj) {
|
|
1162
|
+
int isOpen;
|
|
1163
|
+
if (Tcl_GetBooleanFromObj(interp, item->openObj, &isOpen) != TCL_OK)
|
|
1164
|
+
goto error;
|
|
1165
|
+
if (isOpen)
|
|
1166
|
+
item->state |= TTK_STATE_OPEN;
|
|
1167
|
+
else
|
|
1168
|
+
item->state &= ~TTK_STATE_OPEN;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* Make sure -tags is a valid list
|
|
1172
|
+
* (side effect: may create new tags)
|
|
1173
|
+
*/
|
|
1174
|
+
if (item->tagsObj) {
|
|
1175
|
+
void *taglist;
|
|
1176
|
+
int nTags;
|
|
1177
|
+
if (Ttk_GetTagListFromObj(interp, tv->tree.tagTable, item->tagsObj,
|
|
1178
|
+
&nTags, &taglist) != TCL_OK)
|
|
1179
|
+
{
|
|
1180
|
+
goto error;
|
|
1181
|
+
}
|
|
1182
|
+
Ttk_FreeTagList(taglist);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/* All OK.
|
|
1186
|
+
*/
|
|
1187
|
+
Tk_FreeSavedOptions(&savedOptions);
|
|
1188
|
+
TtkRedisplayWidget(&tv->core);
|
|
1189
|
+
return TCL_OK;
|
|
1190
|
+
|
|
1191
|
+
error:
|
|
1192
|
+
Tk_RestoreSavedOptions(&savedOptions);
|
|
1193
|
+
return TCL_ERROR;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/* + ConfigureColumn --
|
|
1197
|
+
* Set column options.
|
|
1198
|
+
*/
|
|
1199
|
+
static int ConfigureColumn(
|
|
1200
|
+
Tcl_Interp *interp, Treeview *tv, TreeColumn *column,
|
|
1201
|
+
int objc, Tcl_Obj *const objv[])
|
|
1202
|
+
{
|
|
1203
|
+
Tk_SavedOptions savedOptions;
|
|
1204
|
+
int mask;
|
|
1205
|
+
|
|
1206
|
+
if (Tk_SetOptions(interp, (ClientData)column,
|
|
1207
|
+
tv->tree.columnOptionTable, objc, objv, tv->core.tkwin,
|
|
1208
|
+
&savedOptions,&mask) != TCL_OK)
|
|
1209
|
+
{
|
|
1210
|
+
return TCL_ERROR;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
if (mask & READONLY_OPTION) {
|
|
1214
|
+
Tcl_ResetResult(interp);
|
|
1215
|
+
Tcl_AppendResult(interp, "Attempt to change read-only option", NULL);
|
|
1216
|
+
goto error;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
/* Propagate column width changes to overall widget request width,
|
|
1220
|
+
* but only if the widget is currently unmapped, in order to prevent
|
|
1221
|
+
* geometry jumping during interactive column resize.
|
|
1222
|
+
*/
|
|
1223
|
+
if (mask & GEOMETRY_CHANGED) {
|
|
1224
|
+
if (!Tk_IsMapped(tv->core.tkwin)) {
|
|
1225
|
+
TtkResizeWidget(&tv->core);
|
|
1226
|
+
}
|
|
1227
|
+
RecomputeSlack(tv);
|
|
1228
|
+
}
|
|
1229
|
+
TtkRedisplayWidget(&tv->core);
|
|
1230
|
+
|
|
1231
|
+
assert(SLACKINVARIANT(tv));
|
|
1232
|
+
|
|
1233
|
+
Tk_FreeSavedOptions(&savedOptions);
|
|
1234
|
+
return TCL_OK;
|
|
1235
|
+
|
|
1236
|
+
error:
|
|
1237
|
+
Tk_RestoreSavedOptions(&savedOptions);
|
|
1238
|
+
return TCL_ERROR;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/* + ConfigureHeading --
|
|
1242
|
+
* Set heading options.
|
|
1243
|
+
*/
|
|
1244
|
+
static int ConfigureHeading(
|
|
1245
|
+
Tcl_Interp *interp, Treeview *tv, TreeColumn *column,
|
|
1246
|
+
int objc, Tcl_Obj *const objv[])
|
|
1247
|
+
{
|
|
1248
|
+
Tk_SavedOptions savedOptions;
|
|
1249
|
+
int mask;
|
|
1250
|
+
|
|
1251
|
+
if (Tk_SetOptions(interp, (ClientData)column,
|
|
1252
|
+
tv->tree.headingOptionTable, objc, objv, tv->core.tkwin,
|
|
1253
|
+
&savedOptions,&mask) != TCL_OK)
|
|
1254
|
+
{
|
|
1255
|
+
return TCL_ERROR;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/* @@@ testing ... */
|
|
1259
|
+
if ((mask & STATE_CHANGED) && column->headingStateObj) {
|
|
1260
|
+
Ttk_StateSpec stateSpec;
|
|
1261
|
+
if (Ttk_GetStateSpecFromObj(
|
|
1262
|
+
interp, column->headingStateObj, &stateSpec) != TCL_OK)
|
|
1263
|
+
{
|
|
1264
|
+
goto error;
|
|
1265
|
+
}
|
|
1266
|
+
column->headingState = Ttk_ModifyState(column->headingState,&stateSpec);
|
|
1267
|
+
Tcl_DecrRefCount(column->headingStateObj);
|
|
1268
|
+
column->headingStateObj = Ttk_NewStateSpecObj(column->headingState,0);
|
|
1269
|
+
Tcl_IncrRefCount(column->headingStateObj);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
TtkRedisplayWidget(&tv->core);
|
|
1273
|
+
Tk_FreeSavedOptions(&savedOptions);
|
|
1274
|
+
return TCL_OK;
|
|
1275
|
+
|
|
1276
|
+
error:
|
|
1277
|
+
Tk_RestoreSavedOptions(&savedOptions);
|
|
1278
|
+
return TCL_ERROR;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/*------------------------------------------------------------------------
|
|
1282
|
+
* +++ Geometry routines.
|
|
1283
|
+
*/
|
|
1284
|
+
|
|
1285
|
+
/* + CountRows --
|
|
1286
|
+
* Returns the number of viewable rows rooted at item
|
|
1287
|
+
*/
|
|
1288
|
+
static int CountRows(TreeItem *item)
|
|
1289
|
+
{
|
|
1290
|
+
int rows = 1;
|
|
1291
|
+
|
|
1292
|
+
if (item->state & TTK_STATE_OPEN) {
|
|
1293
|
+
TreeItem *child = item->children;
|
|
1294
|
+
while (child) {
|
|
1295
|
+
rows += CountRows(child);
|
|
1296
|
+
child = child->next;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
return rows;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
/* + IdentifyRow --
|
|
1303
|
+
* Recursive search for item at specified y position.
|
|
1304
|
+
* Main work routine for IdentifyItem()
|
|
1305
|
+
*/
|
|
1306
|
+
static TreeItem *IdentifyRow(
|
|
1307
|
+
Treeview *tv, /* Widget record */
|
|
1308
|
+
TreeItem *item, /* Where to start search */
|
|
1309
|
+
Ttk_Box *bp, /* Scan position */
|
|
1310
|
+
int y) /* Target y coordinate */
|
|
1311
|
+
{
|
|
1312
|
+
while (item) {
|
|
1313
|
+
int next_ypos = bp->y + tv->tree.rowHeight;
|
|
1314
|
+
if (bp->y <= y && y <= next_ypos) {
|
|
1315
|
+
bp->height = tv->tree.rowHeight;
|
|
1316
|
+
return item;
|
|
1317
|
+
}
|
|
1318
|
+
bp->y = next_ypos;
|
|
1319
|
+
if (item->state & TTK_STATE_OPEN) {
|
|
1320
|
+
TreeItem *subitem = IdentifyRow(tv, item->children, bp, y);
|
|
1321
|
+
if (subitem) {
|
|
1322
|
+
bp->x += tv->tree.indent;
|
|
1323
|
+
bp->width -= tv->tree.indent;
|
|
1324
|
+
return subitem;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
item = item->next;
|
|
1328
|
+
}
|
|
1329
|
+
return 0;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/* + IdentifyItem --
|
|
1333
|
+
* Locate the item at the specified y position, if any.
|
|
1334
|
+
* On return, *itemPos holds the parcel of the tree item.
|
|
1335
|
+
*/
|
|
1336
|
+
static TreeItem *IdentifyItem(Treeview *tv, int y, Ttk_Box *itemPos)
|
|
1337
|
+
{
|
|
1338
|
+
int rowHeight = tv->tree.rowHeight;
|
|
1339
|
+
*itemPos = Ttk_MakeBox(
|
|
1340
|
+
tv->tree.treeArea.x,
|
|
1341
|
+
tv->tree.treeArea.y - tv->tree.yscroll.first * rowHeight,
|
|
1342
|
+
tv->tree.column0.width,
|
|
1343
|
+
rowHeight);
|
|
1344
|
+
return IdentifyRow(tv, tv->tree.root->children, itemPos, y);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/* + IdentifyDisplayColumn --
|
|
1348
|
+
* Returns the display column number at the specified x position,
|
|
1349
|
+
* or -1 if x is outside any columns.
|
|
1350
|
+
*/
|
|
1351
|
+
static int IdentifyDisplayColumn(Treeview *tv, int x, int *x1)
|
|
1352
|
+
{
|
|
1353
|
+
int colno = FirstColumn(tv);
|
|
1354
|
+
int xpos = tv->tree.treeArea.x;
|
|
1355
|
+
|
|
1356
|
+
while (colno < tv->tree.nDisplayColumns) {
|
|
1357
|
+
TreeColumn *column = tv->tree.displayColumns[colno];
|
|
1358
|
+
int next_xpos = xpos + column->width;
|
|
1359
|
+
if (xpos <= x && x <= next_xpos + HALO) {
|
|
1360
|
+
*x1 = next_xpos;
|
|
1361
|
+
return colno;
|
|
1362
|
+
}
|
|
1363
|
+
++colno;
|
|
1364
|
+
xpos = next_xpos;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return -1;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/* + ItemRow --
|
|
1371
|
+
* Returns row number of specified item relative to root,
|
|
1372
|
+
* -1 if item is not viewable.
|
|
1373
|
+
*/
|
|
1374
|
+
static int ItemRow(Treeview *tv, TreeItem *p)
|
|
1375
|
+
{
|
|
1376
|
+
TreeItem *root = tv->tree.root;
|
|
1377
|
+
int rowNumber = 0;
|
|
1378
|
+
|
|
1379
|
+
for (;;) {
|
|
1380
|
+
if (p->prev) {
|
|
1381
|
+
p = p->prev;
|
|
1382
|
+
rowNumber += CountRows(p);
|
|
1383
|
+
} else {
|
|
1384
|
+
p = p->parent;
|
|
1385
|
+
if (!(p && (p->state & TTK_STATE_OPEN))) {
|
|
1386
|
+
/* detached or closed ancestor */
|
|
1387
|
+
return -1;
|
|
1388
|
+
}
|
|
1389
|
+
if (p == root) {
|
|
1390
|
+
return rowNumber;
|
|
1391
|
+
}
|
|
1392
|
+
++rowNumber;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/*------------------------------------------------------------------------
|
|
1398
|
+
* +++ Display routines.
|
|
1399
|
+
*/
|
|
1400
|
+
|
|
1401
|
+
/* + GetSublayout --
|
|
1402
|
+
* Utility routine; acquires a sublayout for items, cells, etc.
|
|
1403
|
+
*/
|
|
1404
|
+
static Ttk_Layout GetSublayout(
|
|
1405
|
+
Tcl_Interp *interp,
|
|
1406
|
+
Ttk_Theme themePtr,
|
|
1407
|
+
Ttk_Layout parentLayout,
|
|
1408
|
+
const char *layoutName,
|
|
1409
|
+
Tk_OptionTable optionTable,
|
|
1410
|
+
Ttk_Layout *layoutPtr)
|
|
1411
|
+
{
|
|
1412
|
+
Ttk_Layout newLayout = Ttk_CreateSublayout(
|
|
1413
|
+
interp, themePtr, parentLayout, layoutName, optionTable);
|
|
1414
|
+
|
|
1415
|
+
if (newLayout) {
|
|
1416
|
+
if (*layoutPtr)
|
|
1417
|
+
Ttk_FreeLayout(*layoutPtr);
|
|
1418
|
+
*layoutPtr = newLayout;
|
|
1419
|
+
}
|
|
1420
|
+
return newLayout;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/* + TreeviewGetLayout --
|
|
1424
|
+
* GetLayout() widget hook.
|
|
1425
|
+
*/
|
|
1426
|
+
static Ttk_Layout TreeviewGetLayout(
|
|
1427
|
+
Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr)
|
|
1428
|
+
{
|
|
1429
|
+
Treeview *tv = recordPtr;
|
|
1430
|
+
Ttk_Layout treeLayout = TtkWidgetGetLayout(interp, themePtr, recordPtr);
|
|
1431
|
+
Tcl_Obj *objPtr;
|
|
1432
|
+
int unused;
|
|
1433
|
+
|
|
1434
|
+
if (!(
|
|
1435
|
+
treeLayout
|
|
1436
|
+
&& GetSublayout(interp, themePtr, treeLayout, ".Item",
|
|
1437
|
+
tv->tree.tagOptionTable, &tv->tree.itemLayout)
|
|
1438
|
+
&& GetSublayout(interp, themePtr, treeLayout, ".Cell",
|
|
1439
|
+
tv->tree.tagOptionTable, &tv->tree.cellLayout)
|
|
1440
|
+
&& GetSublayout(interp, themePtr, treeLayout, ".Heading",
|
|
1441
|
+
tv->tree.headingOptionTable, &tv->tree.headingLayout)
|
|
1442
|
+
&& GetSublayout(interp, themePtr, treeLayout, ".Row",
|
|
1443
|
+
tv->tree.tagOptionTable, &tv->tree.rowLayout)
|
|
1444
|
+
)) {
|
|
1445
|
+
return 0;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/* Compute heading height.
|
|
1449
|
+
*/
|
|
1450
|
+
Ttk_RebindSublayout(tv->tree.headingLayout, &tv->tree.column0);
|
|
1451
|
+
Ttk_LayoutSize(tv->tree.headingLayout, 0, &unused, &tv->tree.headingHeight);
|
|
1452
|
+
|
|
1453
|
+
/* Get item height, indent from style:
|
|
1454
|
+
* @@@ TODO: sanity-check.
|
|
1455
|
+
*/
|
|
1456
|
+
tv->tree.rowHeight = DEFAULT_ROWHEIGHT;
|
|
1457
|
+
tv->tree.indent = DEFAULT_INDENT;
|
|
1458
|
+
if ((objPtr = Ttk_QueryOption(treeLayout, "-rowheight", 0))) {
|
|
1459
|
+
(void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.rowHeight);
|
|
1460
|
+
}
|
|
1461
|
+
if ((objPtr = Ttk_QueryOption(treeLayout, "-indent", 0))) {
|
|
1462
|
+
(void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.indent);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
return treeLayout;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/* + TreeviewDoLayout --
|
|
1469
|
+
* DoLayout() widget hook. Computes widget layout.
|
|
1470
|
+
*
|
|
1471
|
+
* Side effects:
|
|
1472
|
+
* Computes headingArea and treeArea.
|
|
1473
|
+
* Computes subtree height.
|
|
1474
|
+
* Invokes scroll callbacks.
|
|
1475
|
+
*/
|
|
1476
|
+
static void TreeviewDoLayout(void *clientData)
|
|
1477
|
+
{
|
|
1478
|
+
Treeview *tv = clientData;
|
|
1479
|
+
Ttk_LayoutNode *clientNode = Ttk_LayoutFindNode(tv->core.layout,"treearea");
|
|
1480
|
+
int visibleRows;
|
|
1481
|
+
|
|
1482
|
+
assert(SLACKINVARIANT(tv));
|
|
1483
|
+
|
|
1484
|
+
Ttk_PlaceLayout(tv->core.layout,tv->core.state,Ttk_WinBox(tv->core.tkwin));
|
|
1485
|
+
tv->tree.treeArea = clientNode
|
|
1486
|
+
? Ttk_LayoutNodeInternalParcel(tv->core.layout,clientNode)
|
|
1487
|
+
: Ttk_WinBox(tv->core.tkwin) ;
|
|
1488
|
+
|
|
1489
|
+
ResizeColumns(tv, tv->tree.treeArea.width);
|
|
1490
|
+
assert(SLACKINVARIANT(tv));
|
|
1491
|
+
|
|
1492
|
+
TtkScrolled(tv->tree.xscrollHandle,
|
|
1493
|
+
tv->tree.xscroll.first,
|
|
1494
|
+
tv->tree.xscroll.first + tv->tree.treeArea.width,
|
|
1495
|
+
TreeWidth(tv));
|
|
1496
|
+
|
|
1497
|
+
tv->tree.treeArea.x -= tv->tree.xscroll.first;
|
|
1498
|
+
if (tv->tree.showFlags & SHOW_HEADINGS) {
|
|
1499
|
+
tv->tree.headingArea = Ttk_PackBox(
|
|
1500
|
+
&tv->tree.treeArea, 1, tv->tree.headingHeight, TTK_SIDE_TOP);
|
|
1501
|
+
} else {
|
|
1502
|
+
tv->tree.headingArea = Ttk_MakeBox(0,0,0,0);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
visibleRows = tv->tree.treeArea.height / tv->tree.rowHeight;
|
|
1506
|
+
tv->tree.root->state |= TTK_STATE_OPEN;
|
|
1507
|
+
TtkScrolled(tv->tree.yscrollHandle,
|
|
1508
|
+
tv->tree.yscroll.first,
|
|
1509
|
+
tv->tree.yscroll.first + visibleRows,
|
|
1510
|
+
CountRows(tv->tree.root) - 1);
|
|
1511
|
+
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
/* + TreeviewSize --
|
|
1515
|
+
* SizeProc() widget hook. Size is determined by
|
|
1516
|
+
* -height option and column widths.
|
|
1517
|
+
*/
|
|
1518
|
+
static int TreeviewSize(void *clientData, int *widthPtr, int *heightPtr)
|
|
1519
|
+
{
|
|
1520
|
+
Treeview *tv = clientData;
|
|
1521
|
+
int nRows, padHeight, padWidth;
|
|
1522
|
+
|
|
1523
|
+
Ttk_LayoutSize(tv->core.layout, tv->core.state, &padWidth, &padHeight);
|
|
1524
|
+
Tcl_GetIntFromObj(NULL, tv->tree.heightObj, &nRows);
|
|
1525
|
+
|
|
1526
|
+
*widthPtr = padWidth + TreeWidth(tv);
|
|
1527
|
+
*heightPtr = padHeight + tv->tree.rowHeight * nRows;
|
|
1528
|
+
|
|
1529
|
+
if (tv->tree.showFlags & SHOW_HEADINGS) {
|
|
1530
|
+
*heightPtr += tv->tree.headingHeight;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
return 1;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
/* + ItemState --
|
|
1537
|
+
* Returns the state of the specified item, based
|
|
1538
|
+
* on widget state, item state, and other information.
|
|
1539
|
+
*/
|
|
1540
|
+
static Ttk_State ItemState(Treeview *tv, TreeItem *item)
|
|
1541
|
+
{
|
|
1542
|
+
Ttk_State state = tv->core.state | item->state;
|
|
1543
|
+
if (!item->children)
|
|
1544
|
+
state |= TTK_STATE_LEAF;
|
|
1545
|
+
if (item != tv->tree.focus)
|
|
1546
|
+
state &= ~TTK_STATE_FOCUS;
|
|
1547
|
+
return state;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/* + DrawHeadings --
|
|
1551
|
+
* Draw tree headings.
|
|
1552
|
+
*/
|
|
1553
|
+
static void DrawHeadings(Treeview *tv, Drawable d, Ttk_Box b)
|
|
1554
|
+
{
|
|
1555
|
+
int i = FirstColumn(tv);
|
|
1556
|
+
int x = 0;
|
|
1557
|
+
|
|
1558
|
+
while (i < tv->tree.nDisplayColumns) {
|
|
1559
|
+
TreeColumn *column = tv->tree.displayColumns[i];
|
|
1560
|
+
Ttk_Box parcel = Ttk_MakeBox(b.x+x, b.y, column->width, b.height);
|
|
1561
|
+
DisplayLayout(tv->tree.headingLayout,
|
|
1562
|
+
column, column->headingState, parcel, d);
|
|
1563
|
+
x += column->width;
|
|
1564
|
+
++i;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/* + PrepareItem --
|
|
1569
|
+
* Fill in a displayItem record from tag settings.
|
|
1570
|
+
*/
|
|
1571
|
+
static void PrepareItem(Treeview *tv, TreeItem *item, DisplayItem *displayItem)
|
|
1572
|
+
{
|
|
1573
|
+
const int nOptions = sizeof(*displayItem)/sizeof(Tcl_Obj*);
|
|
1574
|
+
Tcl_Obj **dest = (Tcl_Obj**)displayItem;
|
|
1575
|
+
Tcl_Obj **objv = NULL;
|
|
1576
|
+
int objc = 0;
|
|
1577
|
+
|
|
1578
|
+
memset(displayItem, 0, sizeof(*displayItem));
|
|
1579
|
+
|
|
1580
|
+
if ( item->tagsObj
|
|
1581
|
+
&& Tcl_ListObjGetElements(NULL, item->tagsObj, &objc, &objv) == TCL_OK)
|
|
1582
|
+
{
|
|
1583
|
+
int i, j;
|
|
1584
|
+
for (i=0; i<objc; ++i) {
|
|
1585
|
+
Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[i]);
|
|
1586
|
+
Tcl_Obj **tagRecord = Ttk_TagRecord(tag);
|
|
1587
|
+
|
|
1588
|
+
if (tagRecord) {
|
|
1589
|
+
for (j=0; j<nOptions; ++j) {
|
|
1590
|
+
if (tagRecord[j] != 0) {
|
|
1591
|
+
dest[j] = tagRecord[j];
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
/* + DrawCells --
|
|
1600
|
+
* Draw data cells for specified item.
|
|
1601
|
+
*/
|
|
1602
|
+
static void DrawCells(
|
|
1603
|
+
Treeview *tv, TreeItem *item, DisplayItem *displayItem,
|
|
1604
|
+
Drawable d, Ttk_Box b, int x, int y)
|
|
1605
|
+
{
|
|
1606
|
+
Ttk_Layout layout = tv->tree.cellLayout;
|
|
1607
|
+
Ttk_State state = ItemState(tv, item);
|
|
1608
|
+
Ttk_Padding cellPadding = {4, 0, 4, 0};
|
|
1609
|
+
int rowHeight = tv->tree.rowHeight;
|
|
1610
|
+
int nValues = 0;
|
|
1611
|
+
Tcl_Obj **values = 0;
|
|
1612
|
+
int i;
|
|
1613
|
+
|
|
1614
|
+
if (!item->valuesObj) {
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
Tcl_ListObjGetElements(NULL, item->valuesObj, &nValues, &values);
|
|
1619
|
+
for (i = 0; i < tv->tree.nColumns; ++i) {
|
|
1620
|
+
tv->tree.columns[i].data = (i < nValues) ? values[i] : 0;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
for (i = 1; i < tv->tree.nDisplayColumns; ++i) {
|
|
1624
|
+
TreeColumn *column = tv->tree.displayColumns[i];
|
|
1625
|
+
Ttk_Box parcel = Ttk_PadBox(
|
|
1626
|
+
Ttk_MakeBox(b.x+x, b.y+y, column->width, rowHeight), cellPadding);
|
|
1627
|
+
|
|
1628
|
+
displayItem->textObj = column->data;
|
|
1629
|
+
displayItem->anchorObj = column->anchorObj;
|
|
1630
|
+
|
|
1631
|
+
DisplayLayout(layout, displayItem, state, parcel, d);
|
|
1632
|
+
x += column->width;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/* + DrawItem --
|
|
1637
|
+
* Draw an item (row background, tree label, and cells).
|
|
1638
|
+
*/
|
|
1639
|
+
static void DrawItem(
|
|
1640
|
+
Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row)
|
|
1641
|
+
{
|
|
1642
|
+
Ttk_State state = ItemState(tv, item);
|
|
1643
|
+
DisplayItem displayItem;
|
|
1644
|
+
int rowHeight = tv->tree.rowHeight;
|
|
1645
|
+
int x = depth * tv->tree.indent;
|
|
1646
|
+
int y = (row - tv->tree.yscroll.first) * tv->tree.rowHeight;
|
|
1647
|
+
|
|
1648
|
+
if (row % 2) state |= TTK_STATE_ALTERNATE;
|
|
1649
|
+
|
|
1650
|
+
PrepareItem(tv, item, &displayItem);
|
|
1651
|
+
|
|
1652
|
+
/* Draw row background:
|
|
1653
|
+
*/
|
|
1654
|
+
{
|
|
1655
|
+
Ttk_Box rowBox = Ttk_MakeBox(b.x, b.y+y, TreeWidth(tv), rowHeight);
|
|
1656
|
+
DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/* Draw tree label:
|
|
1660
|
+
*/
|
|
1661
|
+
if (tv->tree.showFlags & SHOW_TREE) {
|
|
1662
|
+
int colwidth = tv->tree.column0.width;
|
|
1663
|
+
Ttk_Box parcel = Ttk_MakeBox(b.x + x, b.y + y, colwidth - x, rowHeight);
|
|
1664
|
+
displayItem.textObj = item->textObj;
|
|
1665
|
+
displayItem.imageObj = item->imageObj;
|
|
1666
|
+
displayItem.anchorObj = 0;
|
|
1667
|
+
DisplayLayout(tv->tree.itemLayout, &displayItem, state, parcel, d);
|
|
1668
|
+
x = colwidth;
|
|
1669
|
+
} else {
|
|
1670
|
+
x = 0;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/* Draw data cells:
|
|
1674
|
+
*/
|
|
1675
|
+
DrawCells(tv, item, &displayItem, d, b, x, y);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/* + DrawSubtree --
|
|
1679
|
+
* Draw an item and all of its (viewable) descendants.
|
|
1680
|
+
*
|
|
1681
|
+
* Returns:
|
|
1682
|
+
* Row number of the last item drawn.
|
|
1683
|
+
*/
|
|
1684
|
+
|
|
1685
|
+
static int DrawForest( /* forward */
|
|
1686
|
+
Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row);
|
|
1687
|
+
|
|
1688
|
+
static int DrawSubtree(
|
|
1689
|
+
Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row)
|
|
1690
|
+
{
|
|
1691
|
+
if (row >= tv->tree.yscroll.first) {
|
|
1692
|
+
DrawItem(tv, item, d, b, depth, row);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
if (item->state & TTK_STATE_OPEN) {
|
|
1696
|
+
return DrawForest(tv, item->children, d, b, depth + 1, row + 1);
|
|
1697
|
+
} else {
|
|
1698
|
+
return row + 1;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
/* + DrawForest --
|
|
1703
|
+
* Draw a sequence of items and their visible descendants.
|
|
1704
|
+
*
|
|
1705
|
+
* Returns:
|
|
1706
|
+
* Row number of the last item drawn.
|
|
1707
|
+
*/
|
|
1708
|
+
static int DrawForest(
|
|
1709
|
+
Treeview *tv, TreeItem *item, Drawable d, Ttk_Box b, int depth, int row)
|
|
1710
|
+
{
|
|
1711
|
+
while (item && row <= tv->tree.yscroll.last) {
|
|
1712
|
+
row = DrawSubtree(tv, item, d, b, depth, row);
|
|
1713
|
+
item = item->next;
|
|
1714
|
+
}
|
|
1715
|
+
return row;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/* + TreeviewDisplay --
|
|
1719
|
+
* Display() widget hook. Draw the widget contents.
|
|
1720
|
+
*/
|
|
1721
|
+
static void TreeviewDisplay(void *clientData, Drawable d)
|
|
1722
|
+
{
|
|
1723
|
+
Treeview *tv = clientData;
|
|
1724
|
+
|
|
1725
|
+
Ttk_DrawLayout(tv->core.layout, tv->core.state, d);
|
|
1726
|
+
if (tv->tree.showFlags & SHOW_HEADINGS) {
|
|
1727
|
+
DrawHeadings(tv, d, tv->tree.headingArea);
|
|
1728
|
+
}
|
|
1729
|
+
DrawForest(tv, tv->tree.root->children, d, tv->tree.treeArea, 0,0);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/*------------------------------------------------------------------------
|
|
1733
|
+
* +++ Utilities for widget commands
|
|
1734
|
+
*/
|
|
1735
|
+
|
|
1736
|
+
/* + InsertPosition --
|
|
1737
|
+
* Locate the previous sibling for [$tree insert].
|
|
1738
|
+
*
|
|
1739
|
+
* Returns a pointer to the item just before the specified index,
|
|
1740
|
+
* or 0 if the item is to be inserted at the beginning.
|
|
1741
|
+
*/
|
|
1742
|
+
static TreeItem *InsertPosition(TreeItem *parent, int index)
|
|
1743
|
+
{
|
|
1744
|
+
TreeItem *prev = 0, *next = parent->children;
|
|
1745
|
+
|
|
1746
|
+
while (next != 0 && index > 0) {
|
|
1747
|
+
--index;
|
|
1748
|
+
prev = next;
|
|
1749
|
+
next = prev->next;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
return prev;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/* + EndPosition --
|
|
1756
|
+
* Locate the last child of the specified node.
|
|
1757
|
+
*/
|
|
1758
|
+
static TreeItem *EndPosition(TreeItem *parent)
|
|
1759
|
+
{
|
|
1760
|
+
TreeItem *sibling = parent->children;
|
|
1761
|
+
if (sibling) {
|
|
1762
|
+
while (sibling->next) {
|
|
1763
|
+
sibling = sibling->next;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
return sibling;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
/* + AncestryCheck --
|
|
1770
|
+
* Verify that specified item is not an ancestor of the specified parent;
|
|
1771
|
+
* returns 1 if OK, 0 and leaves an error message in interp otherwise.
|
|
1772
|
+
*/
|
|
1773
|
+
static int AncestryCheck(
|
|
1774
|
+
Tcl_Interp *interp, Treeview *tv, TreeItem *item, TreeItem *parent)
|
|
1775
|
+
{
|
|
1776
|
+
TreeItem *p = parent;
|
|
1777
|
+
while (p) {
|
|
1778
|
+
if (p == item) {
|
|
1779
|
+
Tcl_ResetResult(interp);
|
|
1780
|
+
Tcl_AppendResult(interp,
|
|
1781
|
+
"Cannot insert ", ItemName(tv, item),
|
|
1782
|
+
" as a descendant of ", ItemName(tv, parent),
|
|
1783
|
+
NULL);
|
|
1784
|
+
return 0;
|
|
1785
|
+
}
|
|
1786
|
+
p = p->parent;
|
|
1787
|
+
}
|
|
1788
|
+
return 1;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/* + DeleteItems --
|
|
1792
|
+
* Remove an item and all of its descendants from the hash table
|
|
1793
|
+
* and detach them from the tree; returns a linked list (chained
|
|
1794
|
+
* along the ->next pointer) of deleted items.
|
|
1795
|
+
*/
|
|
1796
|
+
static TreeItem *DeleteItems(TreeItem *item, TreeItem *delq)
|
|
1797
|
+
{
|
|
1798
|
+
if (item->entryPtr) {
|
|
1799
|
+
DetachItem(item);
|
|
1800
|
+
while (item->children) {
|
|
1801
|
+
delq = DeleteItems(item->children, delq);
|
|
1802
|
+
}
|
|
1803
|
+
Tcl_DeleteHashEntry(item->entryPtr);
|
|
1804
|
+
item->entryPtr = 0;
|
|
1805
|
+
item->next = delq;
|
|
1806
|
+
delq = item;
|
|
1807
|
+
} /* else -- item has already been unlinked */
|
|
1808
|
+
return delq;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
/* + RowNumber --
|
|
1812
|
+
* Calculate which row the specified item appears on;
|
|
1813
|
+
* returns -1 if the item is not viewable.
|
|
1814
|
+
* Xref: DrawForest, IdentifyItem.
|
|
1815
|
+
*/
|
|
1816
|
+
static int RowNumber(Treeview *tv, TreeItem *item)
|
|
1817
|
+
{
|
|
1818
|
+
TreeItem *p = tv->tree.root->children;
|
|
1819
|
+
int n = 0;
|
|
1820
|
+
|
|
1821
|
+
while (p) {
|
|
1822
|
+
if (p == item)
|
|
1823
|
+
return n;
|
|
1824
|
+
|
|
1825
|
+
++n;
|
|
1826
|
+
|
|
1827
|
+
/* Find next viewable item in preorder traversal order
|
|
1828
|
+
*/
|
|
1829
|
+
if (p->children && (p->state & TTK_STATE_OPEN)) {
|
|
1830
|
+
p = p->children;
|
|
1831
|
+
} else {
|
|
1832
|
+
while (!p->next && p && p->parent)
|
|
1833
|
+
p = p->parent;
|
|
1834
|
+
if (p)
|
|
1835
|
+
p = p->next;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return -1;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/* + ItemDepth -- return the depth of a tree item.
|
|
1843
|
+
* The depth of an item is equal to the number of proper ancestors,
|
|
1844
|
+
* not counting the root node.
|
|
1845
|
+
*/
|
|
1846
|
+
static int ItemDepth(TreeItem *item)
|
|
1847
|
+
{
|
|
1848
|
+
int depth = 0;
|
|
1849
|
+
while (item->parent) {
|
|
1850
|
+
++depth;
|
|
1851
|
+
item = item->parent;
|
|
1852
|
+
}
|
|
1853
|
+
return depth-1;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
/*------------------------------------------------------------------------
|
|
1857
|
+
* +++ Widget commands -- item inquiry.
|
|
1858
|
+
*/
|
|
1859
|
+
|
|
1860
|
+
/* + $tv children $item ?newchildren? --
|
|
1861
|
+
* Return the list of children associated with $item
|
|
1862
|
+
*/
|
|
1863
|
+
static int TreeviewChildrenCommand(
|
|
1864
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
1865
|
+
{
|
|
1866
|
+
Treeview *tv = recordPtr;
|
|
1867
|
+
TreeItem *item;
|
|
1868
|
+
Tcl_Obj *result;
|
|
1869
|
+
|
|
1870
|
+
if (objc < 3 || objc > 4) {
|
|
1871
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item ?newchildren?");
|
|
1872
|
+
return TCL_ERROR;
|
|
1873
|
+
}
|
|
1874
|
+
item = FindItem(interp, tv, objv[2]);
|
|
1875
|
+
if (!item) {
|
|
1876
|
+
return TCL_ERROR;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
if (objc == 3) {
|
|
1880
|
+
result = Tcl_NewListObj(0,0);
|
|
1881
|
+
for (item = item->children; item; item = item->next) {
|
|
1882
|
+
Tcl_ListObjAppendElement(interp, result, ItemID(tv, item));
|
|
1883
|
+
}
|
|
1884
|
+
Tcl_SetObjResult(interp, result);
|
|
1885
|
+
} else {
|
|
1886
|
+
TreeItem **newChildren = GetItemListFromObj(interp, tv, objv[3]);
|
|
1887
|
+
TreeItem *child;
|
|
1888
|
+
int i;
|
|
1889
|
+
|
|
1890
|
+
if (!newChildren)
|
|
1891
|
+
return TCL_ERROR;
|
|
1892
|
+
|
|
1893
|
+
/* Sanity-check:
|
|
1894
|
+
*/
|
|
1895
|
+
for (i=0; newChildren[i]; ++i) {
|
|
1896
|
+
if (!AncestryCheck(interp, tv, newChildren[i], item)) {
|
|
1897
|
+
ckfree((ClientData)newChildren);
|
|
1898
|
+
return TCL_ERROR;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
/* Detach old children:
|
|
1903
|
+
*/
|
|
1904
|
+
child = item->children;
|
|
1905
|
+
while (child) {
|
|
1906
|
+
TreeItem *next = child->next;
|
|
1907
|
+
DetachItem(child);
|
|
1908
|
+
child = next;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
/* Detach new children from their current locations:
|
|
1912
|
+
*/
|
|
1913
|
+
for (i=0; newChildren[i]; ++i) {
|
|
1914
|
+
DetachItem(newChildren[i]);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/* Reinsert new children:
|
|
1918
|
+
* Note: it is not an error for an item to be listed more than once,
|
|
1919
|
+
* though it probably should be...
|
|
1920
|
+
*/
|
|
1921
|
+
child = 0;
|
|
1922
|
+
for (i=0; newChildren[i]; ++i) {
|
|
1923
|
+
if (newChildren[i]->parent) {
|
|
1924
|
+
/* This is a duplicate element which has already been
|
|
1925
|
+
* inserted. Ignore it.
|
|
1926
|
+
*/
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
InsertItem(item, child, newChildren[i]);
|
|
1930
|
+
child = newChildren[i];
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
ckfree((ClientData)newChildren);
|
|
1934
|
+
TtkRedisplayWidget(&tv->core);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
return TCL_OK;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/* + $tv parent $item --
|
|
1941
|
+
* Return the item ID of $item's parent.
|
|
1942
|
+
*/
|
|
1943
|
+
static int TreeviewParentCommand(
|
|
1944
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
1945
|
+
{
|
|
1946
|
+
Treeview *tv = recordPtr;
|
|
1947
|
+
TreeItem *item;
|
|
1948
|
+
|
|
1949
|
+
if (objc != 3) {
|
|
1950
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
1951
|
+
return TCL_ERROR;
|
|
1952
|
+
}
|
|
1953
|
+
item = FindItem(interp, tv, objv[2]);
|
|
1954
|
+
if (!item) {
|
|
1955
|
+
return TCL_ERROR;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
if (item->parent) {
|
|
1959
|
+
Tcl_SetObjResult(interp, ItemID(tv, item->parent));
|
|
1960
|
+
} else {
|
|
1961
|
+
/* This is the root item. @@@ Return an error? */
|
|
1962
|
+
Tcl_ResetResult(interp);
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
return TCL_OK;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/* + $tv next $item
|
|
1969
|
+
* Return the ID of $item's next sibling.
|
|
1970
|
+
*/
|
|
1971
|
+
static int TreeviewNextCommand(
|
|
1972
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
1973
|
+
{
|
|
1974
|
+
Treeview *tv = recordPtr;
|
|
1975
|
+
TreeItem *item;
|
|
1976
|
+
|
|
1977
|
+
if (objc != 3) {
|
|
1978
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
1979
|
+
return TCL_ERROR;
|
|
1980
|
+
}
|
|
1981
|
+
item = FindItem(interp, tv, objv[2]);
|
|
1982
|
+
if (!item) {
|
|
1983
|
+
return TCL_ERROR;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
if (item->next) {
|
|
1987
|
+
Tcl_SetObjResult(interp, ItemID(tv, item->next));
|
|
1988
|
+
} /* else -- leave interp-result empty */
|
|
1989
|
+
|
|
1990
|
+
return TCL_OK;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
/* + $tv prev $item
|
|
1994
|
+
* Return the ID of $item's previous sibling.
|
|
1995
|
+
*/
|
|
1996
|
+
static int TreeviewPrevCommand(
|
|
1997
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
1998
|
+
{
|
|
1999
|
+
Treeview *tv = recordPtr;
|
|
2000
|
+
TreeItem *item;
|
|
2001
|
+
|
|
2002
|
+
if (objc != 3) {
|
|
2003
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
2004
|
+
return TCL_ERROR;
|
|
2005
|
+
}
|
|
2006
|
+
item = FindItem(interp, tv, objv[2]);
|
|
2007
|
+
if (!item) {
|
|
2008
|
+
return TCL_ERROR;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
if (item->prev) {
|
|
2012
|
+
Tcl_SetObjResult(interp, ItemID(tv, item->prev));
|
|
2013
|
+
} /* else -- leave interp-result empty */
|
|
2014
|
+
|
|
2015
|
+
return TCL_OK;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
/* + $tv index $item --
|
|
2019
|
+
* Return the index of $item within its parent.
|
|
2020
|
+
*/
|
|
2021
|
+
static int TreeviewIndexCommand(
|
|
2022
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2023
|
+
{
|
|
2024
|
+
Treeview *tv = recordPtr;
|
|
2025
|
+
TreeItem *item;
|
|
2026
|
+
int index = 0;
|
|
2027
|
+
|
|
2028
|
+
if (objc != 3) {
|
|
2029
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
2030
|
+
return TCL_ERROR;
|
|
2031
|
+
}
|
|
2032
|
+
item = FindItem(interp, tv, objv[2]);
|
|
2033
|
+
if (!item) {
|
|
2034
|
+
return TCL_ERROR;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
while (item->prev) {
|
|
2038
|
+
++index;
|
|
2039
|
+
item = item->prev;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
|
|
2043
|
+
return TCL_OK;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
/* + $tv exists $itemid --
|
|
2047
|
+
* Test if the specified item id is present in the tree.
|
|
2048
|
+
*/
|
|
2049
|
+
static int TreeviewExistsCommand(
|
|
2050
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2051
|
+
{
|
|
2052
|
+
Treeview *tv = recordPtr;
|
|
2053
|
+
Tcl_HashEntry *entryPtr;
|
|
2054
|
+
|
|
2055
|
+
if (objc != 3) {
|
|
2056
|
+
Tcl_WrongNumArgs(interp, 2, objv, "itemid");
|
|
2057
|
+
return TCL_ERROR;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
entryPtr = Tcl_FindHashEntry(&tv->tree.items, Tcl_GetString(objv[2]));
|
|
2061
|
+
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(entryPtr != 0));
|
|
2062
|
+
return TCL_OK;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
/* + $tv bbox $itemid ?$column? --
|
|
2066
|
+
* Return bounding box [x y width height] of specified item.
|
|
2067
|
+
*/
|
|
2068
|
+
static int TreeviewBBoxCommand(
|
|
2069
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2070
|
+
{
|
|
2071
|
+
Treeview *tv = recordPtr;
|
|
2072
|
+
TreeItem *item = 0;
|
|
2073
|
+
TreeColumn *column = 0;
|
|
2074
|
+
int row;
|
|
2075
|
+
Ttk_Box bbox;
|
|
2076
|
+
|
|
2077
|
+
if (objc < 3 || objc > 4) {
|
|
2078
|
+
Tcl_WrongNumArgs(interp, 2, objv, "itemid ?column");
|
|
2079
|
+
return TCL_ERROR;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
item = FindItem(interp, tv, objv[2]);
|
|
2083
|
+
if (!item) {
|
|
2084
|
+
return TCL_ERROR;
|
|
2085
|
+
}
|
|
2086
|
+
if (objc >=4 && (column = FindColumn(interp,tv,objv[3])) == NULL) {
|
|
2087
|
+
return TCL_ERROR;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
/* Compute bounding box of item:
|
|
2091
|
+
*/
|
|
2092
|
+
row = ItemRow(tv, item);
|
|
2093
|
+
if (row < tv->tree.yscroll.first || row > tv->tree.yscroll.last) {
|
|
2094
|
+
/* not viewable, or off-screen */
|
|
2095
|
+
return TCL_OK;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
bbox = tv->tree.treeArea;
|
|
2099
|
+
bbox.y += (row - tv->tree.yscroll.first) * tv->tree.rowHeight;
|
|
2100
|
+
bbox.height = tv->tree.rowHeight;
|
|
2101
|
+
|
|
2102
|
+
/* If column has been specified, compute bounding box of cell
|
|
2103
|
+
*/
|
|
2104
|
+
if (column) {
|
|
2105
|
+
int xpos = 0, i = FirstColumn(tv);
|
|
2106
|
+
while (i < tv->tree.nDisplayColumns) {
|
|
2107
|
+
if (tv->tree.displayColumns[i] == column) {
|
|
2108
|
+
break;
|
|
2109
|
+
}
|
|
2110
|
+
xpos += tv->tree.displayColumns[i]->width;
|
|
2111
|
+
++i;
|
|
2112
|
+
}
|
|
2113
|
+
if (i == tv->tree.nDisplayColumns) { /* specified column unviewable */
|
|
2114
|
+
return TCL_OK;
|
|
2115
|
+
}
|
|
2116
|
+
bbox.x += xpos;
|
|
2117
|
+
bbox.width = column->width;
|
|
2118
|
+
|
|
2119
|
+
/* Special case for tree column -- account for indentation:
|
|
2120
|
+
* (@@@ NOTE: doesn't account for tree indicator or image;
|
|
2121
|
+
* @@@ this may or may not be the right thing.)
|
|
2122
|
+
*/
|
|
2123
|
+
if (column == &tv->tree.column0) {
|
|
2124
|
+
int indent = tv->tree.indent * ItemDepth(item);
|
|
2125
|
+
bbox.x += indent;
|
|
2126
|
+
bbox.width -= indent;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
Tcl_SetObjResult(interp, Ttk_NewBoxObj(bbox));
|
|
2131
|
+
return TCL_OK;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
/* + $tv identify $x $y -- (obsolescent)
|
|
2135
|
+
* Implements the old, horrible, 2-argument form of [$tv identify].
|
|
2136
|
+
*
|
|
2137
|
+
* Returns: one of
|
|
2138
|
+
* heading #n
|
|
2139
|
+
* cell itemid #n
|
|
2140
|
+
* item itemid element
|
|
2141
|
+
* row itemid
|
|
2142
|
+
*/
|
|
2143
|
+
static int TreeviewHorribleIdentify(
|
|
2144
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], Treeview *tv)
|
|
2145
|
+
{
|
|
2146
|
+
const char *what = "nothing", *detail = NULL;
|
|
2147
|
+
TreeItem *item = 0;
|
|
2148
|
+
Tcl_Obj *result;
|
|
2149
|
+
int dColumnNumber;
|
|
2150
|
+
char dcolbuf[16];
|
|
2151
|
+
int x, y, x1;
|
|
2152
|
+
|
|
2153
|
+
/* ASSERT: objc == 4 */
|
|
2154
|
+
|
|
2155
|
+
if ( Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK
|
|
2156
|
+
|| Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK
|
|
2157
|
+
) {
|
|
2158
|
+
return TCL_ERROR;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
dColumnNumber = IdentifyDisplayColumn(tv, x, &x1);
|
|
2162
|
+
if (dColumnNumber < 0) {
|
|
2163
|
+
goto done;
|
|
2164
|
+
}
|
|
2165
|
+
sprintf(dcolbuf, "#%d", dColumnNumber);
|
|
2166
|
+
|
|
2167
|
+
if (Ttk_BoxContains(tv->tree.headingArea,x,y)) {
|
|
2168
|
+
if (-HALO <= x1 - x && x1 - x <= HALO) {
|
|
2169
|
+
what = "separator";
|
|
2170
|
+
} else {
|
|
2171
|
+
what = "heading";
|
|
2172
|
+
}
|
|
2173
|
+
detail = dcolbuf;
|
|
2174
|
+
} else if (Ttk_BoxContains(tv->tree.treeArea,x,y)) {
|
|
2175
|
+
Ttk_Box itemBox;
|
|
2176
|
+
item = IdentifyItem(tv, y, &itemBox);
|
|
2177
|
+
if (item && dColumnNumber > 0) {
|
|
2178
|
+
what = "cell";
|
|
2179
|
+
detail = dcolbuf;
|
|
2180
|
+
} else if (item) {
|
|
2181
|
+
Ttk_Layout layout = tv->tree.itemLayout;
|
|
2182
|
+
DisplayItem displayItem;
|
|
2183
|
+
Ttk_LayoutNode *element;
|
|
2184
|
+
|
|
2185
|
+
PrepareItem(tv, item, &displayItem); /*@@@ FIX: -text, etc*/
|
|
2186
|
+
Ttk_RebindSublayout(layout, &displayItem);
|
|
2187
|
+
Ttk_PlaceLayout(layout, ItemState(tv,item), itemBox);
|
|
2188
|
+
element = Ttk_LayoutIdentify(layout, x, y);
|
|
2189
|
+
|
|
2190
|
+
if (element) {
|
|
2191
|
+
what = "item";
|
|
2192
|
+
detail = Ttk_LayoutNodeName(element);
|
|
2193
|
+
} else {
|
|
2194
|
+
what = "row";
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
done:
|
|
2200
|
+
result = Tcl_NewListObj(0,0);
|
|
2201
|
+
Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(what, -1));
|
|
2202
|
+
if (item)
|
|
2203
|
+
Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item));
|
|
2204
|
+
if (detail)
|
|
2205
|
+
Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(detail, -1));
|
|
2206
|
+
|
|
2207
|
+
Tcl_SetObjResult(interp, result);
|
|
2208
|
+
return TCL_OK;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
/* + $tv identify $component $x $y --
|
|
2212
|
+
* Identify the component at position x,y.
|
|
2213
|
+
*/
|
|
2214
|
+
|
|
2215
|
+
static int TreeviewIdentifyCommand(
|
|
2216
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2217
|
+
{
|
|
2218
|
+
static const char *componentStrings[] =
|
|
2219
|
+
{ "row", "column", NULL };
|
|
2220
|
+
enum { I_ROW, I_COLUMN };
|
|
2221
|
+
|
|
2222
|
+
Treeview *tv = recordPtr;
|
|
2223
|
+
int component, x, y;
|
|
2224
|
+
|
|
2225
|
+
if (objc == 4) { /* Old form */
|
|
2226
|
+
return TreeviewHorribleIdentify(interp, objc, objv, tv);
|
|
2227
|
+
} else if (objc != 5) {
|
|
2228
|
+
Tcl_WrongNumArgs(interp, 2, objv, "component x y");
|
|
2229
|
+
return TCL_ERROR;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
if ( Tcl_GetIndexFromObj(interp, objv[2],
|
|
2233
|
+
componentStrings, "component", TCL_EXACT, &component) != TCL_OK
|
|
2234
|
+
|| Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK
|
|
2235
|
+
|| Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK
|
|
2236
|
+
) {
|
|
2237
|
+
return TCL_ERROR;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
switch (component)
|
|
2241
|
+
{
|
|
2242
|
+
case I_ROW :
|
|
2243
|
+
{
|
|
2244
|
+
Ttk_Box itemBox;
|
|
2245
|
+
TreeItem *item = IdentifyItem(tv, y, &itemBox);
|
|
2246
|
+
if (item) {
|
|
2247
|
+
Tcl_SetObjResult(interp, ItemID(tv, item));
|
|
2248
|
+
}
|
|
2249
|
+
break;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
case I_COLUMN :
|
|
2253
|
+
{
|
|
2254
|
+
int x1;
|
|
2255
|
+
int column = IdentifyDisplayColumn(tv, x, &x1);
|
|
2256
|
+
|
|
2257
|
+
if (column >= 0) {
|
|
2258
|
+
char dcolbuf[16];
|
|
2259
|
+
sprintf(dcolbuf, "#%d", column);
|
|
2260
|
+
Tcl_SetObjResult(interp, Tcl_NewStringObj(dcolbuf, -1));
|
|
2261
|
+
}
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return TCL_OK;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/*------------------------------------------------------------------------
|
|
2269
|
+
* +++ Widget commands -- item and column configuration.
|
|
2270
|
+
*/
|
|
2271
|
+
|
|
2272
|
+
/* + $tv item $item ?options ....?
|
|
2273
|
+
* Query or configure item options.
|
|
2274
|
+
*/
|
|
2275
|
+
static int TreeviewItemCommand(
|
|
2276
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2277
|
+
{
|
|
2278
|
+
Treeview *tv = recordPtr;
|
|
2279
|
+
TreeItem *item;
|
|
2280
|
+
|
|
2281
|
+
if (objc < 3) {
|
|
2282
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item ?option ?value??...");
|
|
2283
|
+
return TCL_ERROR;
|
|
2284
|
+
}
|
|
2285
|
+
if (!(item = FindItem(interp, tv, objv[2]))) {
|
|
2286
|
+
return TCL_ERROR;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
if (objc == 3) {
|
|
2290
|
+
return TtkEnumerateOptions(interp, item, ItemOptionSpecs,
|
|
2291
|
+
tv->tree.itemOptionTable, tv->core.tkwin);
|
|
2292
|
+
} else if (objc == 4) {
|
|
2293
|
+
return TtkGetOptionValue(interp, item, objv[3],
|
|
2294
|
+
tv->tree.itemOptionTable, tv->core.tkwin);
|
|
2295
|
+
} else {
|
|
2296
|
+
return ConfigureItem(interp, tv, item, objc-3, objv+3);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
/* + $tv column column ?options ....?
|
|
2301
|
+
* Column data accessor
|
|
2302
|
+
*/
|
|
2303
|
+
static int TreeviewColumnCommand(
|
|
2304
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2305
|
+
{
|
|
2306
|
+
Treeview *tv = recordPtr;
|
|
2307
|
+
TreeColumn *column;
|
|
2308
|
+
|
|
2309
|
+
if (objc < 3) {
|
|
2310
|
+
Tcl_WrongNumArgs(interp, 2, objv, "column -option value...");
|
|
2311
|
+
return TCL_ERROR;
|
|
2312
|
+
}
|
|
2313
|
+
if (!(column = FindColumn(interp, tv, objv[2]))) {
|
|
2314
|
+
return TCL_ERROR;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
if (objc == 3) {
|
|
2318
|
+
return TtkEnumerateOptions(interp, column, ColumnOptionSpecs,
|
|
2319
|
+
tv->tree.columnOptionTable, tv->core.tkwin);
|
|
2320
|
+
} else if (objc == 4) {
|
|
2321
|
+
return TtkGetOptionValue(interp, column, objv[3],
|
|
2322
|
+
tv->tree.columnOptionTable, tv->core.tkwin);
|
|
2323
|
+
} else {
|
|
2324
|
+
return ConfigureColumn(interp, tv, column, objc-3, objv+3);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
/* + $tv heading column ?options ....?
|
|
2329
|
+
* Heading data accessor
|
|
2330
|
+
*/
|
|
2331
|
+
static int TreeviewHeadingCommand(
|
|
2332
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2333
|
+
{
|
|
2334
|
+
Treeview *tv = recordPtr;
|
|
2335
|
+
Tk_OptionTable optionTable = tv->tree.headingOptionTable;
|
|
2336
|
+
Tk_Window tkwin = tv->core.tkwin;
|
|
2337
|
+
TreeColumn *column;
|
|
2338
|
+
|
|
2339
|
+
if (objc < 3) {
|
|
2340
|
+
Tcl_WrongNumArgs(interp, 2, objv, "column -option value...");
|
|
2341
|
+
return TCL_ERROR;
|
|
2342
|
+
}
|
|
2343
|
+
if (!(column = FindColumn(interp, tv, objv[2]))) {
|
|
2344
|
+
return TCL_ERROR;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
if (objc == 3) {
|
|
2348
|
+
return TtkEnumerateOptions(
|
|
2349
|
+
interp, column, HeadingOptionSpecs, optionTable, tkwin);
|
|
2350
|
+
} else if (objc == 4) {
|
|
2351
|
+
return TtkGetOptionValue(
|
|
2352
|
+
interp, column, objv[3], optionTable, tkwin);
|
|
2353
|
+
} else {
|
|
2354
|
+
return ConfigureHeading(interp, tv, column, objc-3,objv+3);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
/* + $tv set $item ?$column ?value??
|
|
2359
|
+
* Query or configure cell values
|
|
2360
|
+
*/
|
|
2361
|
+
static int TreeviewSetCommand(
|
|
2362
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2363
|
+
{
|
|
2364
|
+
Treeview *tv = recordPtr;
|
|
2365
|
+
TreeItem *item;
|
|
2366
|
+
TreeColumn *column;
|
|
2367
|
+
int columnNumber;
|
|
2368
|
+
|
|
2369
|
+
if (objc < 3 || objc > 5) {
|
|
2370
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item ?column ?value??");
|
|
2371
|
+
return TCL_ERROR;
|
|
2372
|
+
}
|
|
2373
|
+
if (!(item = FindItem(interp, tv, objv[2])))
|
|
2374
|
+
return TCL_ERROR;
|
|
2375
|
+
|
|
2376
|
+
/* Make sure -values exists:
|
|
2377
|
+
*/
|
|
2378
|
+
if (!item->valuesObj) {
|
|
2379
|
+
item->valuesObj = Tcl_NewListObj(0,0);
|
|
2380
|
+
Tcl_IncrRefCount(item->valuesObj);
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
if (objc == 3) {
|
|
2384
|
+
/* Return dictionary:
|
|
2385
|
+
*/
|
|
2386
|
+
Tcl_Obj *result = Tcl_NewListObj(0,0);
|
|
2387
|
+
Tcl_Obj *value;
|
|
2388
|
+
for (columnNumber=0; columnNumber<tv->tree.nColumns; ++columnNumber) {
|
|
2389
|
+
Tcl_ListObjIndex(interp, item->valuesObj, columnNumber, &value);
|
|
2390
|
+
if (value) {
|
|
2391
|
+
Tcl_ListObjAppendElement(interp, result,
|
|
2392
|
+
tv->tree.columns[columnNumber].idObj);
|
|
2393
|
+
Tcl_ListObjAppendElement(interp, result, value);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
Tcl_SetObjResult(interp, result);
|
|
2397
|
+
return TCL_OK;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
/* else -- get or set column
|
|
2401
|
+
*/
|
|
2402
|
+
if (!(column = FindColumn(interp, tv, objv[3])))
|
|
2403
|
+
return TCL_ERROR;
|
|
2404
|
+
|
|
2405
|
+
if (column == &tv->tree.column0) {
|
|
2406
|
+
/* @@@ Maybe set -text here instead? */
|
|
2407
|
+
Tcl_AppendResult(interp, "Display column #0 cannot be set", NULL);
|
|
2408
|
+
return TCL_ERROR;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
/* Note: we don't do any error checking in the list operations,
|
|
2412
|
+
* since item->valuesObj is guaranteed to be a list.
|
|
2413
|
+
*/
|
|
2414
|
+
columnNumber = column - tv->tree.columns;
|
|
2415
|
+
|
|
2416
|
+
if (objc == 4) { /* get column */
|
|
2417
|
+
Tcl_Obj *result = 0;
|
|
2418
|
+
Tcl_ListObjIndex(interp, item->valuesObj, columnNumber, &result);
|
|
2419
|
+
if (!result) {
|
|
2420
|
+
result = Tcl_NewStringObj("",0);
|
|
2421
|
+
}
|
|
2422
|
+
Tcl_SetObjResult(interp, result);
|
|
2423
|
+
return TCL_OK;
|
|
2424
|
+
} else { /* set column */
|
|
2425
|
+
int length;
|
|
2426
|
+
|
|
2427
|
+
item->valuesObj = unshare(item->valuesObj);
|
|
2428
|
+
|
|
2429
|
+
/* Make sure -values is fully populated:
|
|
2430
|
+
*/
|
|
2431
|
+
Tcl_ListObjLength(interp, item->valuesObj, &length);
|
|
2432
|
+
while (length < tv->tree.nColumns) {
|
|
2433
|
+
Tcl_Obj *empty = Tcl_NewStringObj("",0);
|
|
2434
|
+
Tcl_ListObjAppendElement(interp, item->valuesObj, empty);
|
|
2435
|
+
++length;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/* Set value:
|
|
2439
|
+
*/
|
|
2440
|
+
Tcl_ListObjReplace(interp,item->valuesObj,columnNumber,1,1,objv+4);
|
|
2441
|
+
TtkRedisplayWidget(&tv->core);
|
|
2442
|
+
return TCL_OK;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
/*------------------------------------------------------------------------
|
|
2447
|
+
* +++ Widget commands -- tree modification.
|
|
2448
|
+
*/
|
|
2449
|
+
|
|
2450
|
+
/* + $tv insert $parent $index ?-id id? ?-option value...?
|
|
2451
|
+
* Insert a new item.
|
|
2452
|
+
*/
|
|
2453
|
+
static int TreeviewInsertCommand(
|
|
2454
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2455
|
+
{
|
|
2456
|
+
Treeview *tv = recordPtr;
|
|
2457
|
+
TreeItem *parent, *sibling, *newItem;
|
|
2458
|
+
Tcl_HashEntry *entryPtr;
|
|
2459
|
+
int isNew;
|
|
2460
|
+
|
|
2461
|
+
if (objc < 4) {
|
|
2462
|
+
Tcl_WrongNumArgs(interp, 2, objv, "parent index ?-id id? -options...");
|
|
2463
|
+
return TCL_ERROR;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
/* Get parent node:
|
|
2467
|
+
*/
|
|
2468
|
+
if ((parent = FindItem(interp, tv, objv[2])) == NULL) {
|
|
2469
|
+
return TCL_ERROR;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
/* Locate previous sibling based on $index:
|
|
2473
|
+
*/
|
|
2474
|
+
if (!strcmp(Tcl_GetString(objv[3]), "end")) {
|
|
2475
|
+
sibling = EndPosition(parent);
|
|
2476
|
+
} else {
|
|
2477
|
+
int index;
|
|
2478
|
+
if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK)
|
|
2479
|
+
return TCL_ERROR;
|
|
2480
|
+
sibling = InsertPosition(parent, index);
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
/* Get node name:
|
|
2484
|
+
* If -id supplied and does not already exist, use that;
|
|
2485
|
+
* Otherwise autogenerate new one.
|
|
2486
|
+
*/
|
|
2487
|
+
objc -= 4; objv += 4;
|
|
2488
|
+
if (objc >= 2 && !strcmp("-id", Tcl_GetString(objv[0]))) {
|
|
2489
|
+
const char *itemName = Tcl_GetString(objv[1]);
|
|
2490
|
+
entryPtr = Tcl_CreateHashEntry(&tv->tree.items, itemName, &isNew);
|
|
2491
|
+
if (!isNew) {
|
|
2492
|
+
Tcl_AppendResult(interp, "Item ",itemName," already exists",NULL);
|
|
2493
|
+
return TCL_ERROR;
|
|
2494
|
+
}
|
|
2495
|
+
objc -= 2; objv += 2;
|
|
2496
|
+
} else {
|
|
2497
|
+
char idbuf[16];
|
|
2498
|
+
do {
|
|
2499
|
+
++tv->tree.serial;
|
|
2500
|
+
sprintf(idbuf, "I%03X", tv->tree.serial);
|
|
2501
|
+
entryPtr = Tcl_CreateHashEntry(&tv->tree.items, idbuf, &isNew);
|
|
2502
|
+
} while (!isNew);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
/* Create and configure new item:
|
|
2506
|
+
*/
|
|
2507
|
+
newItem = NewItem();
|
|
2508
|
+
Tk_InitOptions(
|
|
2509
|
+
interp, (ClientData)newItem, tv->tree.itemOptionTable, tv->core.tkwin);
|
|
2510
|
+
if (ConfigureItem(interp, tv, newItem, objc, objv) != TCL_OK) {
|
|
2511
|
+
Tcl_DeleteHashEntry(entryPtr);
|
|
2512
|
+
FreeItem(newItem);
|
|
2513
|
+
return TCL_ERROR;
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
/* Store in hash table, link into tree:
|
|
2517
|
+
*/
|
|
2518
|
+
Tcl_SetHashValue(entryPtr, newItem);
|
|
2519
|
+
newItem->entryPtr = entryPtr;
|
|
2520
|
+
InsertItem(parent, sibling, newItem);
|
|
2521
|
+
TtkRedisplayWidget(&tv->core);
|
|
2522
|
+
|
|
2523
|
+
Tcl_SetObjResult(interp, ItemID(tv, newItem));
|
|
2524
|
+
return TCL_OK;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/* + $tv detach $item --
|
|
2528
|
+
* Unlink $item from the tree.
|
|
2529
|
+
*/
|
|
2530
|
+
static int TreeviewDetachCommand(
|
|
2531
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2532
|
+
{
|
|
2533
|
+
Treeview *tv = recordPtr;
|
|
2534
|
+
TreeItem **items;
|
|
2535
|
+
int i;
|
|
2536
|
+
|
|
2537
|
+
if (objc != 3) {
|
|
2538
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
2539
|
+
return TCL_ERROR;
|
|
2540
|
+
}
|
|
2541
|
+
if (!(items = GetItemListFromObj(interp, tv, objv[2]))) {
|
|
2542
|
+
return TCL_ERROR;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
/* Sanity-check */
|
|
2546
|
+
for (i = 0; items[i]; ++i) {
|
|
2547
|
+
if (items[i] == tv->tree.root) {
|
|
2548
|
+
Tcl_AppendResult(interp, "Cannot detach root item", NULL);
|
|
2549
|
+
ckfree((ClientData)items);
|
|
2550
|
+
return TCL_ERROR;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
for (i = 0; items[i]; ++i) {
|
|
2555
|
+
DetachItem(items[i]);
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
TtkRedisplayWidget(&tv->core);
|
|
2559
|
+
ckfree((ClientData)items);
|
|
2560
|
+
return TCL_OK;
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
/* + $tv delete $items --
|
|
2564
|
+
* Delete each item in $items.
|
|
2565
|
+
*
|
|
2566
|
+
* Do this in two passes:
|
|
2567
|
+
* First detach the item and all its descendants and remove them
|
|
2568
|
+
* from the hash table. Free the items themselves in a second pass.
|
|
2569
|
+
*
|
|
2570
|
+
* It's done this way because an item may appear more than once
|
|
2571
|
+
* in the list of items to delete (either directly or as a descendant
|
|
2572
|
+
* of a previously deleted item.)
|
|
2573
|
+
*/
|
|
2574
|
+
|
|
2575
|
+
static int TreeviewDeleteCommand(
|
|
2576
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2577
|
+
{
|
|
2578
|
+
Treeview *tv = recordPtr;
|
|
2579
|
+
TreeItem **items, *delq;
|
|
2580
|
+
int i;
|
|
2581
|
+
|
|
2582
|
+
if (objc != 3) {
|
|
2583
|
+
Tcl_WrongNumArgs(interp, 2, objv, "items");
|
|
2584
|
+
return TCL_ERROR;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
if (!(items = GetItemListFromObj(interp, tv, objv[2]))) {
|
|
2588
|
+
return TCL_ERROR;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
/* Sanity-check:
|
|
2592
|
+
*/
|
|
2593
|
+
for (i=0; items[i]; ++i) {
|
|
2594
|
+
if (items[i] == tv->tree.root) {
|
|
2595
|
+
ckfree((ClientData)items);
|
|
2596
|
+
Tcl_AppendResult(interp, "Cannot delete root item", NULL);
|
|
2597
|
+
return TCL_ERROR;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
/* Remove items from hash table.
|
|
2602
|
+
*/
|
|
2603
|
+
delq = 0;
|
|
2604
|
+
for (i=0; items[i]; ++i) {
|
|
2605
|
+
delq = DeleteItems(items[i], delq);
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
/* Free items:
|
|
2609
|
+
*/
|
|
2610
|
+
while (delq) {
|
|
2611
|
+
TreeItem *next = delq->next;
|
|
2612
|
+
if (tv->tree.focus == delq)
|
|
2613
|
+
tv->tree.focus = 0;
|
|
2614
|
+
FreeItem(delq);
|
|
2615
|
+
delq = next;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
ckfree((ClientData)items);
|
|
2619
|
+
TtkRedisplayWidget(&tv->core);
|
|
2620
|
+
return TCL_OK;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
/* + $tv move $item $parent $index
|
|
2624
|
+
* Move $item to the specified $index in $parent's child list.
|
|
2625
|
+
*/
|
|
2626
|
+
static int TreeviewMoveCommand(
|
|
2627
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2628
|
+
{
|
|
2629
|
+
Treeview *tv = recordPtr;
|
|
2630
|
+
TreeItem *item, *parent;
|
|
2631
|
+
TreeItem *sibling;
|
|
2632
|
+
|
|
2633
|
+
if (objc != 5) {
|
|
2634
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item parent index");
|
|
2635
|
+
return TCL_ERROR;
|
|
2636
|
+
}
|
|
2637
|
+
if ( (item = FindItem(interp, tv, objv[2])) == 0
|
|
2638
|
+
|| (parent = FindItem(interp, tv, objv[3])) == 0)
|
|
2639
|
+
{
|
|
2640
|
+
return TCL_ERROR;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
/* Locate previous sibling based on $index:
|
|
2644
|
+
*/
|
|
2645
|
+
if (!strcmp(Tcl_GetString(objv[4]), "end")) {
|
|
2646
|
+
sibling = EndPosition(parent);
|
|
2647
|
+
} else {
|
|
2648
|
+
TreeItem *p;
|
|
2649
|
+
int index;
|
|
2650
|
+
|
|
2651
|
+
if (Tcl_GetIntFromObj(interp, objv[4], &index) != TCL_OK) {
|
|
2652
|
+
return TCL_ERROR;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
sibling = 0;
|
|
2656
|
+
for (p = parent->children; p != NULL && index > 0; p = p->next) {
|
|
2657
|
+
if (p != item) {
|
|
2658
|
+
--index;
|
|
2659
|
+
} /* else -- moving node forward, count index+1 nodes */
|
|
2660
|
+
sibling = p;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
/* Check ancestry:
|
|
2665
|
+
*/
|
|
2666
|
+
if (!AncestryCheck(interp, tv, item, parent)) {
|
|
2667
|
+
return TCL_ERROR;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
/* Moving an item after itself is a no-op:
|
|
2671
|
+
*/
|
|
2672
|
+
if (item == sibling) {
|
|
2673
|
+
return TCL_OK;
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
/* Move item:
|
|
2677
|
+
*/
|
|
2678
|
+
DetachItem(item);
|
|
2679
|
+
InsertItem(parent, sibling, item);
|
|
2680
|
+
|
|
2681
|
+
TtkRedisplayWidget(&tv->core);
|
|
2682
|
+
return TCL_OK;
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
/*------------------------------------------------------------------------
|
|
2686
|
+
* +++ Widget commands -- scrolling
|
|
2687
|
+
*/
|
|
2688
|
+
|
|
2689
|
+
static int TreeviewXViewCommand(
|
|
2690
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2691
|
+
{
|
|
2692
|
+
Treeview *tv = recordPtr;
|
|
2693
|
+
return TtkScrollviewCommand(interp, objc, objv, tv->tree.xscrollHandle);
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
static int TreeviewYViewCommand(
|
|
2697
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2698
|
+
{
|
|
2699
|
+
Treeview *tv = recordPtr;
|
|
2700
|
+
return TtkScrollviewCommand(interp, objc, objv, tv->tree.yscrollHandle);
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
/* $tree see $item --
|
|
2704
|
+
* Ensure that $item is visible.
|
|
2705
|
+
*/
|
|
2706
|
+
static int TreeviewSeeCommand(
|
|
2707
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2708
|
+
{
|
|
2709
|
+
Treeview *tv = recordPtr;
|
|
2710
|
+
TreeItem *item, *parent;
|
|
2711
|
+
int rowNumber;
|
|
2712
|
+
|
|
2713
|
+
if (objc != 3) {
|
|
2714
|
+
Tcl_WrongNumArgs(interp, 2, objv, "item");
|
|
2715
|
+
return TCL_ERROR;
|
|
2716
|
+
}
|
|
2717
|
+
if (!(item = FindItem(interp, tv, objv[2]))) {
|
|
2718
|
+
return TCL_ERROR;
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
/* Make sure all ancestors are open:
|
|
2722
|
+
*/
|
|
2723
|
+
for (parent = item->parent; parent; parent = parent->parent) {
|
|
2724
|
+
if (!(parent->state & TTK_STATE_OPEN)) {
|
|
2725
|
+
parent->openObj = unshare(parent->openObj);
|
|
2726
|
+
Tcl_SetBooleanObj(parent->openObj, 1);
|
|
2727
|
+
parent->state |= TTK_STATE_OPEN;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
/* Make sure item is visible:
|
|
2732
|
+
* @@@ DOUBLE-CHECK THIS:
|
|
2733
|
+
*/
|
|
2734
|
+
rowNumber = RowNumber(tv, item);
|
|
2735
|
+
if (rowNumber < tv->tree.yscroll.first) {
|
|
2736
|
+
TtkScrollTo(tv->tree.yscrollHandle, rowNumber);
|
|
2737
|
+
} else if (rowNumber >= tv->tree.yscroll.last) {
|
|
2738
|
+
TtkScrollTo(tv->tree.yscrollHandle,
|
|
2739
|
+
tv->tree.yscroll.first + (1+rowNumber - tv->tree.yscroll.last));
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
return TCL_OK;
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
/*------------------------------------------------------------------------
|
|
2746
|
+
* +++ Widget commands -- interactive column resize
|
|
2747
|
+
*/
|
|
2748
|
+
|
|
2749
|
+
/* + $tree drag $column $newX --
|
|
2750
|
+
* Set right edge of display column $column to x position $X
|
|
2751
|
+
*/
|
|
2752
|
+
static int TreeviewDragCommand(
|
|
2753
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2754
|
+
{
|
|
2755
|
+
Treeview *tv = recordPtr;
|
|
2756
|
+
int left = tv->tree.treeArea.x;
|
|
2757
|
+
int i = FirstColumn(tv);
|
|
2758
|
+
TreeColumn *column;
|
|
2759
|
+
int newx;
|
|
2760
|
+
|
|
2761
|
+
if (objc != 4) {
|
|
2762
|
+
Tcl_WrongNumArgs(interp, 2, objv, "column xposition");
|
|
2763
|
+
return TCL_ERROR;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
if ( (column = FindColumn(interp, tv, objv[2])) == 0
|
|
2767
|
+
|| Tcl_GetIntFromObj(interp, objv[3], &newx) != TCL_OK)
|
|
2768
|
+
{
|
|
2769
|
+
return TCL_ERROR;
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
for (;i < tv->tree.nDisplayColumns; ++i) {
|
|
2773
|
+
TreeColumn *c = tv->tree.displayColumns[i];
|
|
2774
|
+
int right = left + c->width;
|
|
2775
|
+
if (c == column) {
|
|
2776
|
+
DragColumn(tv, i, newx - right);
|
|
2777
|
+
assert(SLACKINVARIANT(tv));
|
|
2778
|
+
TtkRedisplayWidget(&tv->core);
|
|
2779
|
+
return TCL_OK;
|
|
2780
|
+
}
|
|
2781
|
+
left = right;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
Tcl_ResetResult(interp);
|
|
2785
|
+
Tcl_AppendResult(interp,
|
|
2786
|
+
"column ", Tcl_GetString(objv[2]), " is not displayed",
|
|
2787
|
+
NULL);
|
|
2788
|
+
return TCL_ERROR;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
/*------------------------------------------------------------------------
|
|
2792
|
+
* +++ Widget commands -- focus and selection
|
|
2793
|
+
*/
|
|
2794
|
+
|
|
2795
|
+
/* + $tree focus ?item?
|
|
2796
|
+
*/
|
|
2797
|
+
static int TreeviewFocusCommand(
|
|
2798
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2799
|
+
{
|
|
2800
|
+
Treeview *tv = recordPtr;
|
|
2801
|
+
|
|
2802
|
+
if (objc == 2) {
|
|
2803
|
+
if (tv->tree.focus) {
|
|
2804
|
+
Tcl_SetObjResult(interp, ItemID(tv, tv->tree.focus));
|
|
2805
|
+
}
|
|
2806
|
+
return TCL_OK;
|
|
2807
|
+
} else if (objc == 3) {
|
|
2808
|
+
TreeItem *newFocus = FindItem(interp, tv, objv[2]);
|
|
2809
|
+
if (!newFocus)
|
|
2810
|
+
return TCL_ERROR;
|
|
2811
|
+
tv->tree.focus = newFocus;
|
|
2812
|
+
TtkRedisplayWidget(&tv->core);
|
|
2813
|
+
return TCL_OK;
|
|
2814
|
+
} else {
|
|
2815
|
+
Tcl_WrongNumArgs(interp, 2, objv, "?newFocus?");
|
|
2816
|
+
return TCL_ERROR;
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
/* + $tree selection ?add|remove|set|toggle $items?
|
|
2821
|
+
*/
|
|
2822
|
+
static int TreeviewSelectionCommand(
|
|
2823
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2824
|
+
{
|
|
2825
|
+
enum {
|
|
2826
|
+
SELECTION_SET, SELECTION_ADD, SELECTION_REMOVE, SELECTION_TOGGLE
|
|
2827
|
+
};
|
|
2828
|
+
static const char *selopStrings[] = {
|
|
2829
|
+
"set", "add", "remove", "toggle", NULL
|
|
2830
|
+
};
|
|
2831
|
+
|
|
2832
|
+
Treeview *tv = recordPtr;
|
|
2833
|
+
int selop, i;
|
|
2834
|
+
TreeItem *item, **items;
|
|
2835
|
+
|
|
2836
|
+
if (objc == 2) {
|
|
2837
|
+
Tcl_Obj *result = Tcl_NewListObj(0,0);
|
|
2838
|
+
for (item = tv->tree.root->children; item; item=NextPreorder(item)) {
|
|
2839
|
+
if (item->state & TTK_STATE_SELECTED)
|
|
2840
|
+
Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item));
|
|
2841
|
+
}
|
|
2842
|
+
Tcl_SetObjResult(interp, result);
|
|
2843
|
+
return TCL_OK;
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
if (objc != 4) {
|
|
2847
|
+
Tcl_WrongNumArgs(interp, 2, objv, "?add|remove|set|toggle items?");
|
|
2848
|
+
return TCL_ERROR;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
if (Tcl_GetIndexFromObj(interp, objv[2], selopStrings,
|
|
2852
|
+
"selection operation", 0, &selop) != TCL_OK)
|
|
2853
|
+
{
|
|
2854
|
+
return TCL_ERROR;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
items = GetItemListFromObj(interp, tv, objv[3]);
|
|
2858
|
+
if (!items) {
|
|
2859
|
+
return TCL_ERROR;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
switch (selop)
|
|
2863
|
+
{
|
|
2864
|
+
case SELECTION_SET:
|
|
2865
|
+
for (item=tv->tree.root; item; item=NextPreorder(item)) {
|
|
2866
|
+
item->state &= ~TTK_STATE_SELECTED;
|
|
2867
|
+
}
|
|
2868
|
+
/*FALLTHRU*/
|
|
2869
|
+
case SELECTION_ADD:
|
|
2870
|
+
for (i=0; items[i]; ++i) {
|
|
2871
|
+
items[i]->state |= TTK_STATE_SELECTED;
|
|
2872
|
+
}
|
|
2873
|
+
break;
|
|
2874
|
+
case SELECTION_REMOVE:
|
|
2875
|
+
for (i=0; items[i]; ++i) {
|
|
2876
|
+
items[i]->state &= ~TTK_STATE_SELECTED;
|
|
2877
|
+
}
|
|
2878
|
+
break;
|
|
2879
|
+
case SELECTION_TOGGLE:
|
|
2880
|
+
for (i=0; items[i]; ++i) {
|
|
2881
|
+
items[i]->state ^= TTK_STATE_SELECTED;
|
|
2882
|
+
}
|
|
2883
|
+
break;
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
ckfree((ClientData)items);
|
|
2887
|
+
TtkSendVirtualEvent(tv->core.tkwin, "TreeviewSelect");
|
|
2888
|
+
TtkRedisplayWidget(&tv->core);
|
|
2889
|
+
|
|
2890
|
+
return TCL_OK;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
/*------------------------------------------------------------------------
|
|
2894
|
+
* +++ Widget commands -- tags and bindings.
|
|
2895
|
+
*/
|
|
2896
|
+
|
|
2897
|
+
/* + $tv tag bind $tag ?$sequence ?$script??
|
|
2898
|
+
*/
|
|
2899
|
+
static int TreeviewTagBindCommand(
|
|
2900
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2901
|
+
{
|
|
2902
|
+
Treeview *tv = recordPtr;
|
|
2903
|
+
Ttk_Tag tag;
|
|
2904
|
+
|
|
2905
|
+
if (objc < 4 || objc > 6) {
|
|
2906
|
+
Tcl_WrongNumArgs(interp, 3, objv, "tagName ?sequence? ?script?");
|
|
2907
|
+
return TCL_ERROR;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
|
|
2911
|
+
if (!tag) { return TCL_ERROR; }
|
|
2912
|
+
|
|
2913
|
+
if (objc == 4) { /* $tv tag bind $tag */
|
|
2914
|
+
Tk_GetAllBindings(interp, tv->tree.bindingTable, tag);
|
|
2915
|
+
} else if (objc == 5) { /* $tv tag bind $tag $sequence */
|
|
2916
|
+
/* TODO: distinguish "no such binding" (OK) from "bad pattern" (ERROR)
|
|
2917
|
+
*/
|
|
2918
|
+
const char *script = Tk_GetBinding(interp,
|
|
2919
|
+
tv->tree.bindingTable, tag, Tcl_GetString(objv[4]));
|
|
2920
|
+
if (script != NULL) {
|
|
2921
|
+
Tcl_SetObjResult(interp, Tcl_NewStringObj(script,-1));
|
|
2922
|
+
}
|
|
2923
|
+
} else if (objc == 6) { /* $tv tag bind $tag $sequence $script */
|
|
2924
|
+
CONST char *sequence = Tcl_GetString(objv[4]);
|
|
2925
|
+
CONST char *script = Tcl_GetString(objv[5]);
|
|
2926
|
+
unsigned long mask = Tk_CreateBinding(interp,
|
|
2927
|
+
tv->tree.bindingTable, tag, sequence, script, 0);
|
|
2928
|
+
|
|
2929
|
+
/* Test mask to make sure event is supported:
|
|
2930
|
+
*/
|
|
2931
|
+
if (mask & (~TreeviewBindEventMask)) {
|
|
2932
|
+
Tk_DeleteBinding(interp, tv->tree.bindingTable, tag, sequence);
|
|
2933
|
+
Tcl_ResetResult(interp);
|
|
2934
|
+
Tcl_AppendResult(interp, "unsupported event ", sequence,
|
|
2935
|
+
"\nonly key, button, motion, and virtual events supported",
|
|
2936
|
+
NULL);
|
|
2937
|
+
return TCL_ERROR;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
return mask ? TCL_OK : TCL_ERROR;
|
|
2941
|
+
}
|
|
2942
|
+
return TCL_OK;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
/* + $tv tag configure $tag ?-option ?value -option value...??
|
|
2946
|
+
*/
|
|
2947
|
+
static int TreeviewTagConfigureCommand(
|
|
2948
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2949
|
+
{
|
|
2950
|
+
Treeview *tv = recordPtr;
|
|
2951
|
+
void *tagRecord;
|
|
2952
|
+
Ttk_Tag tag;
|
|
2953
|
+
|
|
2954
|
+
if (objc < 4) {
|
|
2955
|
+
Tcl_WrongNumArgs(interp, 3, objv, "tagName ?-option ?value ...??");
|
|
2956
|
+
return TCL_ERROR;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
|
|
2960
|
+
tagRecord = Ttk_TagRecord(tag);
|
|
2961
|
+
|
|
2962
|
+
if (objc == 4) {
|
|
2963
|
+
return TtkEnumerateOptions(interp, tagRecord, TagOptionSpecs,
|
|
2964
|
+
tv->tree.tagOptionTable, tv->core.tkwin);
|
|
2965
|
+
} else if (objc == 5) {
|
|
2966
|
+
return TtkGetOptionValue(interp, tagRecord, objv[4],
|
|
2967
|
+
tv->tree.tagOptionTable, tv->core.tkwin);
|
|
2968
|
+
}
|
|
2969
|
+
/* else */
|
|
2970
|
+
TtkRedisplayWidget(&tv->core);
|
|
2971
|
+
return Tk_SetOptions(
|
|
2972
|
+
interp, tagRecord, tv->tree.tagOptionTable,
|
|
2973
|
+
objc - 4, objv + 4, tv->core.tkwin,
|
|
2974
|
+
NULL/*savedOptions*/, NULL/*mask*/);
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
/* + $tv tag option args...
|
|
2978
|
+
*/
|
|
2979
|
+
static int TreeviewTagCommand(
|
|
2980
|
+
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
|
|
2981
|
+
{
|
|
2982
|
+
static WidgetCommandSpec TreeviewTagCommands[] = {
|
|
2983
|
+
{ "bind", TreeviewTagBindCommand },
|
|
2984
|
+
{ "configure", TreeviewTagConfigureCommand },
|
|
2985
|
+
{0,0}
|
|
2986
|
+
};
|
|
2987
|
+
return TtkWidgetEnsembleCommand(
|
|
2988
|
+
TreeviewTagCommands, 2, interp, objc, objv, recordPtr);
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/*------------------------------------------------------------------------
|
|
2992
|
+
* +++ Widget commands record.
|
|
2993
|
+
*/
|
|
2994
|
+
static WidgetCommandSpec TreeviewCommands[] =
|
|
2995
|
+
{
|
|
2996
|
+
{ "bbox", TreeviewBBoxCommand },
|
|
2997
|
+
{ "children", TreeviewChildrenCommand },
|
|
2998
|
+
{ "cget", TtkWidgetCgetCommand },
|
|
2999
|
+
{ "column", TreeviewColumnCommand },
|
|
3000
|
+
{ "configure", TtkWidgetConfigureCommand },
|
|
3001
|
+
{ "delete", TreeviewDeleteCommand },
|
|
3002
|
+
{ "detach", TreeviewDetachCommand },
|
|
3003
|
+
{ "drag", TreeviewDragCommand },
|
|
3004
|
+
{ "exists", TreeviewExistsCommand },
|
|
3005
|
+
{ "focus", TreeviewFocusCommand },
|
|
3006
|
+
{ "heading", TreeviewHeadingCommand },
|
|
3007
|
+
{ "identify", TreeviewIdentifyCommand },
|
|
3008
|
+
{ "index", TreeviewIndexCommand },
|
|
3009
|
+
{ "instate", TtkWidgetInstateCommand },
|
|
3010
|
+
{ "insert", TreeviewInsertCommand },
|
|
3011
|
+
{ "item", TreeviewItemCommand },
|
|
3012
|
+
{ "move", TreeviewMoveCommand },
|
|
3013
|
+
{ "next", TreeviewNextCommand },
|
|
3014
|
+
{ "parent", TreeviewParentCommand },
|
|
3015
|
+
{ "prev", TreeviewPrevCommand },
|
|
3016
|
+
{ "see", TreeviewSeeCommand },
|
|
3017
|
+
{ "selection" , TreeviewSelectionCommand },
|
|
3018
|
+
{ "set", TreeviewSetCommand },
|
|
3019
|
+
{ "state", TtkWidgetStateCommand },
|
|
3020
|
+
{ "tag", TreeviewTagCommand },
|
|
3021
|
+
{ "xview", TreeviewXViewCommand },
|
|
3022
|
+
{ "yview", TreeviewYViewCommand },
|
|
3023
|
+
{ NULL, NULL }
|
|
3024
|
+
};
|
|
3025
|
+
|
|
3026
|
+
/*------------------------------------------------------------------------
|
|
3027
|
+
* +++ Widget definition.
|
|
3028
|
+
*/
|
|
3029
|
+
|
|
3030
|
+
static WidgetSpec TreeviewWidgetSpec =
|
|
3031
|
+
{
|
|
3032
|
+
"Treeview", /* className */
|
|
3033
|
+
sizeof(Treeview), /* recordSize */
|
|
3034
|
+
TreeviewOptionSpecs, /* optionSpecs */
|
|
3035
|
+
TreeviewCommands, /* subcommands */
|
|
3036
|
+
TreeviewInitialize, /* initializeProc */
|
|
3037
|
+
TreeviewCleanup, /* cleanupProc */
|
|
3038
|
+
TreeviewConfigure, /* configureProc */
|
|
3039
|
+
TtkNullPostConfigure, /* postConfigureProc */
|
|
3040
|
+
TreeviewGetLayout, /* getLayoutProc */
|
|
3041
|
+
TreeviewSize, /* sizeProc */
|
|
3042
|
+
TreeviewDoLayout, /* layoutProc */
|
|
3043
|
+
TreeviewDisplay /* displayProc */
|
|
3044
|
+
};
|
|
3045
|
+
|
|
3046
|
+
/*------------------------------------------------------------------------
|
|
3047
|
+
* +++ Layout specifications.
|
|
3048
|
+
*/
|
|
3049
|
+
|
|
3050
|
+
TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
|
|
3051
|
+
|
|
3052
|
+
TTK_LAYOUT("Treeview",
|
|
3053
|
+
TTK_GROUP("Treeview.field", TTK_FILL_BOTH|TTK_BORDER,
|
|
3054
|
+
TTK_GROUP("Treeview.padding", TTK_FILL_BOTH,
|
|
3055
|
+
TTK_NODE("Treeview.treearea", TTK_FILL_BOTH))))
|
|
3056
|
+
|
|
3057
|
+
TTK_LAYOUT("Item",
|
|
3058
|
+
TTK_GROUP("Treeitem.padding", TTK_FILL_BOTH,
|
|
3059
|
+
TTK_NODE("Treeitem.indicator", TTK_PACK_LEFT)
|
|
3060
|
+
TTK_NODE("Treeitem.image", TTK_PACK_LEFT)
|
|
3061
|
+
TTK_GROUP("Treeitem.focus", TTK_PACK_LEFT,
|
|
3062
|
+
TTK_NODE("Treeitem.text", TTK_PACK_LEFT))))
|
|
3063
|
+
|
|
3064
|
+
TTK_LAYOUT("Cell",
|
|
3065
|
+
TTK_GROUP("Treedata.padding", TTK_FILL_BOTH,
|
|
3066
|
+
TTK_NODE("Treeitem.text", TTK_FILL_BOTH)))
|
|
3067
|
+
|
|
3068
|
+
TTK_LAYOUT("Heading",
|
|
3069
|
+
TTK_NODE("Treeheading.cell", TTK_FILL_BOTH)
|
|
3070
|
+
TTK_GROUP("Treeheading.border", TTK_FILL_BOTH,
|
|
3071
|
+
TTK_GROUP("Treeheading.padding", TTK_FILL_BOTH,
|
|
3072
|
+
TTK_NODE("Treeheading.image", TTK_PACK_RIGHT)
|
|
3073
|
+
TTK_NODE("Treeheading.text", TTK_FILL_X))))
|
|
3074
|
+
|
|
3075
|
+
TTK_LAYOUT("Row",
|
|
3076
|
+
TTK_NODE("Treeitem.row", TTK_FILL_BOTH))
|
|
3077
|
+
|
|
3078
|
+
TTK_END_LAYOUT_TABLE
|
|
3079
|
+
|
|
3080
|
+
/*------------------------------------------------------------------------
|
|
3081
|
+
* +++ Tree indicator element.
|
|
3082
|
+
*/
|
|
3083
|
+
|
|
3084
|
+
typedef struct
|
|
3085
|
+
{
|
|
3086
|
+
Tcl_Obj *colorObj;
|
|
3087
|
+
Tcl_Obj *sizeObj;
|
|
3088
|
+
Tcl_Obj *marginsObj;
|
|
3089
|
+
} TreeitemIndicator;
|
|
3090
|
+
|
|
3091
|
+
static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] =
|
|
3092
|
+
{
|
|
3093
|
+
{ "-foreground", TK_OPTION_COLOR,
|
|
3094
|
+
Tk_Offset(TreeitemIndicator,colorObj), DEFAULT_FOREGROUND },
|
|
3095
|
+
{ "-indicatorsize", TK_OPTION_PIXELS,
|
|
3096
|
+
Tk_Offset(TreeitemIndicator,sizeObj), "12" },
|
|
3097
|
+
{ "-indicatormargins", TK_OPTION_STRING,
|
|
3098
|
+
Tk_Offset(TreeitemIndicator,marginsObj), "2 2 4 2" },
|
|
3099
|
+
{NULL}
|
|
3100
|
+
};
|
|
3101
|
+
|
|
3102
|
+
static void TreeitemIndicatorSize(
|
|
3103
|
+
void *clientData, void *elementRecord, Tk_Window tkwin,
|
|
3104
|
+
int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
|
|
3105
|
+
{
|
|
3106
|
+
TreeitemIndicator *indicator = elementRecord;
|
|
3107
|
+
Ttk_Padding margins;
|
|
3108
|
+
int size = 0;
|
|
3109
|
+
|
|
3110
|
+
Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginsObj, &margins);
|
|
3111
|
+
Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
|
|
3112
|
+
|
|
3113
|
+
*widthPtr = size + Ttk_PaddingWidth(margins);
|
|
3114
|
+
*heightPtr = size + Ttk_PaddingHeight(margins);
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
static void TreeitemIndicatorDraw(
|
|
3118
|
+
void *clientData, void *elementRecord, Tk_Window tkwin,
|
|
3119
|
+
Drawable d, Ttk_Box b, Ttk_State state)
|
|
3120
|
+
{
|
|
3121
|
+
TreeitemIndicator *indicator = elementRecord;
|
|
3122
|
+
ArrowDirection direction =
|
|
3123
|
+
(state & TTK_STATE_OPEN) ? ARROW_DOWN : ARROW_RIGHT;
|
|
3124
|
+
Ttk_Padding margins;
|
|
3125
|
+
XColor *borderColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
|
|
3126
|
+
XGCValues gcvalues; GC gc; unsigned mask;
|
|
3127
|
+
|
|
3128
|
+
if (state & TTK_STATE_LEAF) /* don't draw anything */
|
|
3129
|
+
return;
|
|
3130
|
+
|
|
3131
|
+
Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginsObj,&margins);
|
|
3132
|
+
b = Ttk_PadBox(b, margins);
|
|
3133
|
+
|
|
3134
|
+
gcvalues.foreground = borderColor->pixel;
|
|
3135
|
+
gcvalues.line_width = 1;
|
|
3136
|
+
mask = GCForeground | GCLineWidth;
|
|
3137
|
+
gc = Tk_GetGC(tkwin, mask, &gcvalues);
|
|
3138
|
+
|
|
3139
|
+
TtkDrawArrow(Tk_Display(tkwin), d, gc, b, direction);
|
|
3140
|
+
|
|
3141
|
+
Tk_FreeGC(Tk_Display(tkwin), gc);
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
static Ttk_ElementSpec TreeitemIndicatorElementSpec =
|
|
3145
|
+
{
|
|
3146
|
+
TK_STYLE_VERSION_2,
|
|
3147
|
+
sizeof(TreeitemIndicator),
|
|
3148
|
+
TreeitemIndicatorOptions,
|
|
3149
|
+
TreeitemIndicatorSize,
|
|
3150
|
+
TreeitemIndicatorDraw
|
|
3151
|
+
};
|
|
3152
|
+
|
|
3153
|
+
/*------------------------------------------------------------------------
|
|
3154
|
+
* +++ Row element.
|
|
3155
|
+
*/
|
|
3156
|
+
|
|
3157
|
+
typedef struct
|
|
3158
|
+
{
|
|
3159
|
+
Tcl_Obj *backgroundObj;
|
|
3160
|
+
Tcl_Obj *rowNumberObj;
|
|
3161
|
+
} RowElement;
|
|
3162
|
+
|
|
3163
|
+
static Ttk_ElementOptionSpec RowElementOptions[] =
|
|
3164
|
+
{
|
|
3165
|
+
{ "-background", TK_OPTION_COLOR,
|
|
3166
|
+
Tk_Offset(RowElement,backgroundObj), DEFAULT_BACKGROUND },
|
|
3167
|
+
{ "-rownumber", TK_OPTION_INT,
|
|
3168
|
+
Tk_Offset(RowElement,rowNumberObj), "0" },
|
|
3169
|
+
{NULL}
|
|
3170
|
+
};
|
|
3171
|
+
|
|
3172
|
+
static void RowElementDraw(
|
|
3173
|
+
void *clientData, void *elementRecord, Tk_Window tkwin,
|
|
3174
|
+
Drawable d, Ttk_Box b, Ttk_State state)
|
|
3175
|
+
{
|
|
3176
|
+
RowElement *row = elementRecord;
|
|
3177
|
+
XColor *color = Tk_GetColorFromObj(tkwin, row->backgroundObj);
|
|
3178
|
+
GC gc = Tk_GCForColor(color, d);
|
|
3179
|
+
XFillRectangle(Tk_Display(tkwin), d, gc,
|
|
3180
|
+
b.x, b.y, b.width, b.height);
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
static Ttk_ElementSpec RowElementSpec =
|
|
3184
|
+
{
|
|
3185
|
+
TK_STYLE_VERSION_2,
|
|
3186
|
+
sizeof(RowElement),
|
|
3187
|
+
RowElementOptions,
|
|
3188
|
+
TtkNullElementSize,
|
|
3189
|
+
RowElementDraw
|
|
3190
|
+
};
|
|
3191
|
+
|
|
3192
|
+
/*------------------------------------------------------------------------
|
|
3193
|
+
* +++ Initialisation.
|
|
3194
|
+
*/
|
|
3195
|
+
|
|
3196
|
+
void TtkTreeview_Init(Tcl_Interp *interp)
|
|
3197
|
+
{
|
|
3198
|
+
Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
|
|
3199
|
+
|
|
3200
|
+
RegisterWidget(interp, "ttk::treeview", &TreeviewWidgetSpec);
|
|
3201
|
+
|
|
3202
|
+
Ttk_RegisterElement(interp, theme, "Treeitem.indicator",
|
|
3203
|
+
&TreeitemIndicatorElementSpec, 0);
|
|
3204
|
+
Ttk_RegisterElement(interp, theme, "Treeitem.row", &RowElementSpec, 0);
|
|
3205
|
+
Ttk_RegisterElement(interp, theme, "Treeheading.cell", &RowElementSpec, 0);
|
|
3206
|
+
Ttk_RegisterElement(interp, theme, "treearea", &ttkNullElementSpec, 0);
|
|
3207
|
+
|
|
3208
|
+
Ttk_RegisterLayouts(theme, LayoutTable);
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
/*EOF*/
|