Pratt 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. data/.exrc +61 -0
  2. data/.gitignore +4 -0
  3. data/History.txt +6 -0
  4. data/Manifest.txt +46 -0
  5. data/Pratt.gemspec +351 -0
  6. data/README.txt +66 -0
  7. data/Rakefile +85 -0
  8. data/TODO +54 -0
  9. data/VERSION +1 -0
  10. data/bin/pratt.rb +13 -0
  11. data/config.rb +34 -0
  12. data/lib/pratt.rb +527 -0
  13. data/lib/pratt/array.rb +11 -0
  14. data/lib/pratt/string.rb +18 -0
  15. data/models/app.rb +40 -0
  16. data/models/customer.rb +24 -0
  17. data/models/payment.rb +22 -0
  18. data/models/pratt.rb +19 -0
  19. data/models/project.rb +82 -0
  20. data/models/whence.rb +70 -0
  21. data/pkgs/tile-0.8.2.tar.gz +0 -0
  22. data/pkgs/tile-0.8.2/ANNOUNCE.txt +95 -0
  23. data/pkgs/tile-0.8.2/ChangeLog +4651 -0
  24. data/pkgs/tile-0.8.2/Makefile +250 -0
  25. data/pkgs/tile-0.8.2/Makefile.in +250 -0
  26. data/pkgs/tile-0.8.2/README.txt +86 -0
  27. data/pkgs/tile-0.8.2/aclocal.m4 +2 -0
  28. data/pkgs/tile-0.8.2/altTheme.o +0 -0
  29. data/pkgs/tile-0.8.2/blink.o +0 -0
  30. data/pkgs/tile-0.8.2/button.o +0 -0
  31. data/pkgs/tile-0.8.2/cache.o +0 -0
  32. data/pkgs/tile-0.8.2/clamTheme.o +0 -0
  33. data/pkgs/tile-0.8.2/classicTheme.o +0 -0
  34. data/pkgs/tile-0.8.2/config.log +1330 -0
  35. data/pkgs/tile-0.8.2/config.status +795 -0
  36. data/pkgs/tile-0.8.2/configure +15248 -0
  37. data/pkgs/tile-0.8.2/configure.in +89 -0
  38. data/pkgs/tile-0.8.2/demos/autocomplete.tcl +59 -0
  39. data/pkgs/tile-0.8.2/demos/demo.tcl +870 -0
  40. data/pkgs/tile-0.8.2/demos/dirbrowser.tcl +167 -0
  41. data/pkgs/tile-0.8.2/demos/dlgtest.tcl +97 -0
  42. data/pkgs/tile-0.8.2/demos/iconlib.tcl +110 -0
  43. data/pkgs/tile-0.8.2/demos/repeater.tcl +117 -0
  44. data/pkgs/tile-0.8.2/demos/toolbutton.tcl +101 -0
  45. data/pkgs/tile-0.8.2/doc/Geometry.3 +230 -0
  46. data/pkgs/tile-0.8.2/doc/INDEX.MAP +153 -0
  47. data/pkgs/tile-0.8.2/doc/Makefile +36 -0
  48. data/pkgs/tile-0.8.2/doc/TILE.XML +45 -0
  49. data/pkgs/tile-0.8.2/doc/Theme.3 +34 -0
  50. data/pkgs/tile-0.8.2/doc/button.n +75 -0
  51. data/pkgs/tile-0.8.2/doc/checkbutton.n +61 -0
  52. data/pkgs/tile-0.8.2/doc/combobox.n +98 -0
  53. data/pkgs/tile-0.8.2/doc/converting.txt +97 -0
  54. data/pkgs/tile-0.8.2/doc/dialog.n +122 -0
  55. data/pkgs/tile-0.8.2/doc/entry.n +438 -0
  56. data/pkgs/tile-0.8.2/doc/frame.n +43 -0
  57. data/pkgs/tile-0.8.2/doc/html/Geometry.html +304 -0
  58. data/pkgs/tile-0.8.2/doc/html/Theme.html +48 -0
  59. data/pkgs/tile-0.8.2/doc/html/button.html +120 -0
  60. data/pkgs/tile-0.8.2/doc/html/category-index.html +18 -0
  61. data/pkgs/tile-0.8.2/doc/html/checkbutton.html +94 -0
  62. data/pkgs/tile-0.8.2/doc/html/combobox.html +164 -0
  63. data/pkgs/tile-0.8.2/doc/html/converting.txt +97 -0
  64. data/pkgs/tile-0.8.2/doc/html/dialog.html +159 -0
  65. data/pkgs/tile-0.8.2/doc/html/entry.html +613 -0
  66. data/pkgs/tile-0.8.2/doc/html/frame.html +76 -0
  67. data/pkgs/tile-0.8.2/doc/html/image.html +100 -0
  68. data/pkgs/tile-0.8.2/doc/html/index.html +25 -0
  69. data/pkgs/tile-0.8.2/doc/html/keyword-index.html +228 -0
  70. data/pkgs/tile-0.8.2/doc/html/label.html +133 -0
  71. data/pkgs/tile-0.8.2/doc/html/labelframe.html +91 -0
  72. data/pkgs/tile-0.8.2/doc/html/manpage.css +212 -0
  73. data/pkgs/tile-0.8.2/doc/html/menubutton.html +63 -0
  74. data/pkgs/tile-0.8.2/doc/html/notebook.html +280 -0
  75. data/pkgs/tile-0.8.2/doc/html/paned.html +149 -0
  76. data/pkgs/tile-0.8.2/doc/html/progressbar.html +138 -0
  77. data/pkgs/tile-0.8.2/doc/html/radiobutton.html +89 -0
  78. data/pkgs/tile-0.8.2/doc/html/scrollbar.html +221 -0
  79. data/pkgs/tile-0.8.2/doc/html/separator.html +48 -0
  80. data/pkgs/tile-0.8.2/doc/html/sizegrip.html +62 -0
  81. data/pkgs/tile-0.8.2/doc/html/style.html +172 -0
  82. data/pkgs/tile-0.8.2/doc/html/tile-intro.html +164 -0
  83. data/pkgs/tile-0.8.2/doc/html/treeview.html +634 -0
  84. data/pkgs/tile-0.8.2/doc/html/widget.html +342 -0
  85. data/pkgs/tile-0.8.2/doc/image.n +81 -0
  86. data/pkgs/tile-0.8.2/doc/internals.txt +409 -0
  87. data/pkgs/tile-0.8.2/doc/label.n +75 -0
  88. data/pkgs/tile-0.8.2/doc/labelframe.n +64 -0
  89. data/pkgs/tile-0.8.2/doc/man.macros +239 -0
  90. data/pkgs/tile-0.8.2/doc/menubutton.n +41 -0
  91. data/pkgs/tile-0.8.2/doc/notebook.n +188 -0
  92. data/pkgs/tile-0.8.2/doc/paned.n +95 -0
  93. data/pkgs/tile-0.8.2/doc/progressbar.n +79 -0
  94. data/pkgs/tile-0.8.2/doc/radiobutton.n +57 -0
  95. data/pkgs/tile-0.8.2/doc/scrollbar.n +160 -0
  96. data/pkgs/tile-0.8.2/doc/separator.n +30 -0
  97. data/pkgs/tile-0.8.2/doc/sizegrip.n +53 -0
  98. data/pkgs/tile-0.8.2/doc/style.n +119 -0
  99. data/pkgs/tile-0.8.2/doc/tile-intro.n +165 -0
  100. data/pkgs/tile-0.8.2/doc/tmml.options +4 -0
  101. data/pkgs/tile-0.8.2/doc/treeview.n +415 -0
  102. data/pkgs/tile-0.8.2/doc/widget.n +227 -0
  103. data/pkgs/tile-0.8.2/doc/xml/Geometry.tmml +379 -0
  104. data/pkgs/tile-0.8.2/doc/xml/INDEX.MAP +153 -0
  105. data/pkgs/tile-0.8.2/doc/xml/Theme.tmml +63 -0
  106. data/pkgs/tile-0.8.2/doc/xml/button.tmml +134 -0
  107. data/pkgs/tile-0.8.2/doc/xml/checkbutton.tmml +119 -0
  108. data/pkgs/tile-0.8.2/doc/xml/combobox.tmml +184 -0
  109. data/pkgs/tile-0.8.2/doc/xml/dialog.tmml +195 -0
  110. data/pkgs/tile-0.8.2/doc/xml/entry.tmml +630 -0
  111. data/pkgs/tile-0.8.2/doc/xml/frame.tmml +98 -0
  112. data/pkgs/tile-0.8.2/doc/xml/image.tmml +101 -0
  113. data/pkgs/tile-0.8.2/doc/xml/label.tmml +154 -0
  114. data/pkgs/tile-0.8.2/doc/xml/labelframe.tmml +116 -0
  115. data/pkgs/tile-0.8.2/doc/xml/menubutton.tmml +80 -0
  116. data/pkgs/tile-0.8.2/doc/xml/notebook.tmml +306 -0
  117. data/pkgs/tile-0.8.2/doc/xml/paned.tmml +154 -0
  118. data/pkgs/tile-0.8.2/doc/xml/progressbar.tmml +151 -0
  119. data/pkgs/tile-0.8.2/doc/xml/radiobutton.tmml +109 -0
  120. data/pkgs/tile-0.8.2/doc/xml/scrollbar.tmml +233 -0
  121. data/pkgs/tile-0.8.2/doc/xml/separator.tmml +59 -0
  122. data/pkgs/tile-0.8.2/doc/xml/sizegrip.tmml +82 -0
  123. data/pkgs/tile-0.8.2/doc/xml/style.tmml +171 -0
  124. data/pkgs/tile-0.8.2/doc/xml/tile-intro.tmml +192 -0
  125. data/pkgs/tile-0.8.2/doc/xml/treeview.tmml +604 -0
  126. data/pkgs/tile-0.8.2/doc/xml/widget.tmml +372 -0
  127. data/pkgs/tile-0.8.2/entry.o +0 -0
  128. data/pkgs/tile-0.8.2/frame.o +0 -0
  129. data/pkgs/tile-0.8.2/generic/Makefile.in +221 -0
  130. data/pkgs/tile-0.8.2/generic/TODO +493 -0
  131. data/pkgs/tile-0.8.2/generic/altTheme.c +1172 -0
  132. data/pkgs/tile-0.8.2/generic/blink.c +168 -0
  133. data/pkgs/tile-0.8.2/generic/button.c +858 -0
  134. data/pkgs/tile-0.8.2/generic/cache.c +354 -0
  135. data/pkgs/tile-0.8.2/generic/clamTheme.c +974 -0
  136. data/pkgs/tile-0.8.2/generic/classicTheme.c +518 -0
  137. data/pkgs/tile-0.8.2/generic/configure +10334 -0
  138. data/pkgs/tile-0.8.2/generic/configure.in +100 -0
  139. data/pkgs/tile-0.8.2/generic/entry.c +1922 -0
  140. data/pkgs/tile-0.8.2/generic/frame.c +648 -0
  141. data/pkgs/tile-0.8.2/generic/gunk.h +44 -0
  142. data/pkgs/tile-0.8.2/generic/image.c +416 -0
  143. data/pkgs/tile-0.8.2/generic/label.c +663 -0
  144. data/pkgs/tile-0.8.2/generic/layout.c +1215 -0
  145. data/pkgs/tile-0.8.2/generic/manager.c +554 -0
  146. data/pkgs/tile-0.8.2/generic/manager.h +91 -0
  147. data/pkgs/tile-0.8.2/generic/notebook.c +1380 -0
  148. data/pkgs/tile-0.8.2/generic/paned.c +958 -0
  149. data/pkgs/tile-0.8.2/generic/pkgIndex.tcl.in +7 -0
  150. data/pkgs/tile-0.8.2/generic/progress.c +549 -0
  151. data/pkgs/tile-0.8.2/generic/scale.c +526 -0
  152. data/pkgs/tile-0.8.2/generic/scroll.c +253 -0
  153. data/pkgs/tile-0.8.2/generic/scrollbar.c +346 -0
  154. data/pkgs/tile-0.8.2/generic/separator.c +132 -0
  155. data/pkgs/tile-0.8.2/generic/square.c +306 -0
  156. data/pkgs/tile-0.8.2/generic/tagset.c +147 -0
  157. data/pkgs/tile-0.8.2/generic/tile.c +296 -0
  158. data/pkgs/tile-0.8.2/generic/tkElements.c +1280 -0
  159. data/pkgs/tile-0.8.2/generic/tkTheme.c +1708 -0
  160. data/pkgs/tile-0.8.2/generic/tkTheme.h +419 -0
  161. data/pkgs/tile-0.8.2/generic/tkThemeInt.h +45 -0
  162. data/pkgs/tile-0.8.2/generic/tkstate.c +268 -0
  163. data/pkgs/tile-0.8.2/generic/trace.c +145 -0
  164. data/pkgs/tile-0.8.2/generic/track.c +174 -0
  165. data/pkgs/tile-0.8.2/generic/treeview.c +3211 -0
  166. data/pkgs/tile-0.8.2/generic/ttk.decls +154 -0
  167. data/pkgs/tile-0.8.2/generic/ttkDecls.h +340 -0
  168. data/pkgs/tile-0.8.2/generic/ttkStubInit.c +61 -0
  169. data/pkgs/tile-0.8.2/generic/ttkStubLib.c +70 -0
  170. data/pkgs/tile-0.8.2/generic/widget.c +785 -0
  171. data/pkgs/tile-0.8.2/generic/widget.h +263 -0
  172. data/pkgs/tile-0.8.2/image.o +0 -0
  173. data/pkgs/tile-0.8.2/label.o +0 -0
  174. data/pkgs/tile-0.8.2/layout.o +0 -0
  175. data/pkgs/tile-0.8.2/library/altTheme.tcl +101 -0
  176. data/pkgs/tile-0.8.2/library/aquaTheme.tcl +62 -0
  177. data/pkgs/tile-0.8.2/library/button.tcl +85 -0
  178. data/pkgs/tile-0.8.2/library/clamTheme.tcl +139 -0
  179. data/pkgs/tile-0.8.2/library/classicTheme.tcl +108 -0
  180. data/pkgs/tile-0.8.2/library/combobox.tcl +439 -0
  181. data/pkgs/tile-0.8.2/library/cursors.tcl +36 -0
  182. data/pkgs/tile-0.8.2/library/defaults.tcl +118 -0
  183. data/pkgs/tile-0.8.2/library/dialog.tcl +274 -0
  184. data/pkgs/tile-0.8.2/library/entry.tcl +580 -0
  185. data/pkgs/tile-0.8.2/library/fonts.tcl +153 -0
  186. data/pkgs/tile-0.8.2/library/icons.tcl +105 -0
  187. data/pkgs/tile-0.8.2/library/keynav.tcl +192 -0
  188. data/pkgs/tile-0.8.2/library/menubutton.tcl +171 -0
  189. data/pkgs/tile-0.8.2/library/notebook.tcl +193 -0
  190. data/pkgs/tile-0.8.2/library/paned.tcl +87 -0
  191. data/pkgs/tile-0.8.2/library/progress.tcl +51 -0
  192. data/pkgs/tile-0.8.2/library/scale.tcl +54 -0
  193. data/pkgs/tile-0.8.2/library/scrollbar.tcl +125 -0
  194. data/pkgs/tile-0.8.2/library/sizegrip.tcl +77 -0
  195. data/pkgs/tile-0.8.2/library/tile.tcl +211 -0
  196. data/pkgs/tile-0.8.2/library/treeview.tcl +382 -0
  197. data/pkgs/tile-0.8.2/library/utils.tcl +254 -0
  198. data/pkgs/tile-0.8.2/library/winTheme.tcl +77 -0
  199. data/pkgs/tile-0.8.2/library/xpTheme.tcl +63 -0
  200. data/pkgs/tile-0.8.2/libtile0.8.2.so +0 -0
  201. data/pkgs/tile-0.8.2/libttkstub.a +0 -0
  202. data/pkgs/tile-0.8.2/license.terms +24 -0
  203. data/pkgs/tile-0.8.2/macosx/aquaTheme.c +1076 -0
  204. data/pkgs/tile-0.8.2/manager.o +0 -0
  205. data/pkgs/tile-0.8.2/notebook.o +0 -0
  206. data/pkgs/tile-0.8.2/paned.o +0 -0
  207. data/pkgs/tile-0.8.2/pkgIndex.tcl +3 -0
  208. data/pkgs/tile-0.8.2/progress.o +0 -0
  209. data/pkgs/tile-0.8.2/scale.o +0 -0
  210. data/pkgs/tile-0.8.2/scroll.o +0 -0
  211. data/pkgs/tile-0.8.2/scrollbar.o +0 -0
  212. data/pkgs/tile-0.8.2/separator.o +0 -0
  213. data/pkgs/tile-0.8.2/tagset.o +0 -0
  214. data/pkgs/tile-0.8.2/tclconfig/install-sh +119 -0
  215. data/pkgs/tile-0.8.2/tclconfig/tcl.m4 +4069 -0
  216. data/pkgs/tile-0.8.2/tclconfig/teax.m4 +109 -0
  217. data/pkgs/tile-0.8.2/tests/all.tcl +18 -0
  218. data/pkgs/tile-0.8.2/tests/bwidget.test +103 -0
  219. data/pkgs/tile-0.8.2/tests/cbtest.tcl +125 -0
  220. data/pkgs/tile-0.8.2/tests/combobox.test +51 -0
  221. data/pkgs/tile-0.8.2/tests/compound.tcl +92 -0
  222. data/pkgs/tile-0.8.2/tests/entry.test +285 -0
  223. data/pkgs/tile-0.8.2/tests/entrytest.tcl +78 -0
  224. data/pkgs/tile-0.8.2/tests/image.test +94 -0
  225. data/pkgs/tile-0.8.2/tests/labelframe.tcl +41 -0
  226. data/pkgs/tile-0.8.2/tests/labelframe.test +137 -0
  227. data/pkgs/tile-0.8.2/tests/layout.test +33 -0
  228. data/pkgs/tile-0.8.2/tests/misc.test +35 -0
  229. data/pkgs/tile-0.8.2/tests/nbtest.tcl +66 -0
  230. data/pkgs/tile-0.8.2/tests/notebook.test +500 -0
  231. data/pkgs/tile-0.8.2/tests/paned.test +298 -0
  232. data/pkgs/tile-0.8.2/tests/progress.test +92 -0
  233. data/pkgs/tile-0.8.2/tests/pwtest.tcl +90 -0
  234. data/pkgs/tile-0.8.2/tests/sbtest.tcl +79 -0
  235. data/pkgs/tile-0.8.2/tests/scrollbar.test +77 -0
  236. data/pkgs/tile-0.8.2/tests/sgtest.tcl +52 -0
  237. data/pkgs/tile-0.8.2/tests/testutils.tcl +20 -0
  238. data/pkgs/tile-0.8.2/tests/tile.test +674 -0
  239. data/pkgs/tile-0.8.2/tests/treetags.test +78 -0
  240. data/pkgs/tile-0.8.2/tests/treeview.test +563 -0
  241. data/pkgs/tile-0.8.2/tests/tvtest.tcl +332 -0
  242. data/pkgs/tile-0.8.2/tests/validate.test +278 -0
  243. data/pkgs/tile-0.8.2/tile.o +0 -0
  244. data/pkgs/tile-0.8.2/tkElements.o +0 -0
  245. data/pkgs/tile-0.8.2/tkTheme.o +0 -0
  246. data/pkgs/tile-0.8.2/tkstate.o +0 -0
  247. data/pkgs/tile-0.8.2/tools/genStubs.tcl +861 -0
  248. data/pkgs/tile-0.8.2/trace.o +0 -0
  249. data/pkgs/tile-0.8.2/track.o +0 -0
  250. data/pkgs/tile-0.8.2/treeview.o +0 -0
  251. data/pkgs/tile-0.8.2/ttkStubInit.o +0 -0
  252. data/pkgs/tile-0.8.2/ttkStubLib.o +0 -0
  253. data/pkgs/tile-0.8.2/widget.o +0 -0
  254. data/pkgs/tile-0.8.2/win/Tile.dsp +261 -0
  255. data/pkgs/tile-0.8.2/win/makefile.vc +527 -0
  256. data/pkgs/tile-0.8.2/win/monitor.c +164 -0
  257. data/pkgs/tile-0.8.2/win/nmakehlp.c +483 -0
  258. data/pkgs/tile-0.8.2/win/rules.vc +512 -0
  259. data/pkgs/tile-0.8.2/win/tile.rc +40 -0
  260. data/pkgs/tile-0.8.2/win/winTheme.c +734 -0
  261. data/pkgs/tile-0.8.2/win/xpTheme.c +1029 -0
  262. data/spec/app_spec.rb +48 -0
  263. data/spec/customer_spec.rb +31 -0
  264. data/spec/fixtures/graph.expectation +18 -0
  265. data/spec/payment_spec.rb +19 -0
  266. data/spec/pratt_spec.rb +148 -0
  267. data/spec/project_spec.rb +163 -0
  268. data/spec/rcov.opts +0 -0
  269. data/spec/spec.opts +1 -0
  270. data/spec/spec_helper.rb +21 -0
  271. data/spec/whence_spec.rb +54 -0
  272. data/tasks/pratt.rb +84 -0
  273. data/templates/model.eruby +12 -0
  274. data/templates/spec.eruby +8 -0
  275. data/views/env.rb +22 -0
  276. data/views/graph.eruby +20 -0
  277. data/views/invoice.eruby +148 -0
  278. data/views/main.rb +92 -0
  279. data/views/pid.eruby +3 -0
  280. data/views/pop.rb +94 -0
  281. data/views/pop2.rb +75 -0
  282. data/views/raw.eruby +11 -0
  283. 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*/