Pratt 1.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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*/