treeview.tcl 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. #
  2. # ttk::treeview widget bindings and utilities.
  3. #
  4. namespace eval ttk::treeview {
  5. variable State
  6. # Enter/Leave/Motion
  7. #
  8. set State(activeWidget) {}
  9. set State(activeHeading) {}
  10. # Press/drag/release:
  11. #
  12. set State(pressMode) none
  13. set State(pressX) 0
  14. # For pressMode == "resize"
  15. set State(resizeColumn) #0
  16. # For pressmode == "heading"
  17. set State(heading) {}
  18. }
  19. ### Widget bindings.
  20. #
  21. bind Treeview <Motion> { ttk::treeview::Motion %W %x %y }
  22. bind Treeview <B1-Leave> { #nothing }
  23. bind Treeview <Leave> { ttk::treeview::ActivateHeading {} {}}
  24. bind Treeview <Button-1> { ttk::treeview::Press %W %x %y }
  25. bind Treeview <Double-Button-1> { ttk::treeview::DoubleClick %W %x %y }
  26. bind Treeview <ButtonRelease-1> { ttk::treeview::Release %W %x %y }
  27. bind Treeview <B1-Motion> { ttk::treeview::Drag %W %x %y }
  28. bind Treeview <Up> { ttk::treeview::Keynav %W up }
  29. bind Treeview <Down> { ttk::treeview::Keynav %W down }
  30. bind Treeview <Right> { ttk::treeview::Keynav %W right }
  31. bind Treeview <Left> { ttk::treeview::Keynav %W left }
  32. bind Treeview <Prior> { %W yview scroll -1 pages }
  33. bind Treeview <Next> { %W yview scroll 1 pages }
  34. bind Treeview <Return> { ttk::treeview::ToggleFocus %W }
  35. bind Treeview <space> { ttk::treeview::ToggleFocus %W }
  36. bind Treeview <Shift-Button-1> \
  37. { ttk::treeview::Select %W %x %y extend }
  38. bind Treeview <<ToggleSelection>> \
  39. { ttk::treeview::Select %W %x %y toggle }
  40. ttk::copyBindings TtkScrollable Treeview
  41. ### Binding procedures.
  42. #
  43. ## Keynav -- Keyboard navigation
  44. #
  45. # @@@ TODO: verify/rewrite up and down code.
  46. #
  47. proc ttk::treeview::Keynav {w dir} {
  48. set focus [$w focus]
  49. if {$focus eq ""} { return }
  50. switch -- $dir {
  51. up {
  52. if {[set up [$w prev $focus]] eq ""} {
  53. set focus [$w parent $focus]
  54. } else {
  55. while {[$w item $up -open] && [llength [$w children $up]]} {
  56. set up [lindex [$w children $up] end]
  57. }
  58. set focus $up
  59. }
  60. }
  61. down {
  62. if {[$w item $focus -open] && [llength [$w children $focus]]} {
  63. set focus [lindex [$w children $focus] 0]
  64. } else {
  65. set up $focus
  66. while {$up ne "" && [set down [$w next $up]] eq ""} {
  67. set up [$w parent $up]
  68. }
  69. set focus $down
  70. }
  71. }
  72. left {
  73. if {[$w item $focus -open] && [llength [$w children $focus]]} {
  74. CloseItem $w $focus
  75. } else {
  76. set focus [$w parent $focus]
  77. }
  78. }
  79. right {
  80. OpenItem $w $focus
  81. }
  82. }
  83. if {$focus != {}} {
  84. SelectOp $w $focus choose
  85. }
  86. }
  87. ## Motion -- pointer motion binding.
  88. # Sets cursor, active element ...
  89. #
  90. proc ttk::treeview::Motion {w x y} {
  91. variable State
  92. ttk::saveCursor $w State(userConfCursor) [ttk::cursor hresize]
  93. set cursor $State(userConfCursor)
  94. set activeHeading {}
  95. switch -- [$w identify region $x $y] {
  96. separator { set cursor hresize }
  97. heading { set activeHeading [$w identify column $x $y] }
  98. }
  99. ttk::setCursor $w $cursor
  100. ActivateHeading $w $activeHeading
  101. }
  102. ## ActivateHeading -- track active heading element
  103. #
  104. proc ttk::treeview::ActivateHeading {w heading} {
  105. variable State
  106. if {$w != $State(activeWidget) || $heading != $State(activeHeading)} {
  107. if {[winfo exists $State(activeWidget)] && $State(activeHeading) != {}} {
  108. # It may happen that $State(activeHeading) no longer corresponds
  109. # to an existing display column. This happens for instance when
  110. # changing -displaycolumns in a bound script when this change
  111. # triggers a <Leave> event. A proc checking if the display column
  112. # $State(activeHeading) is really still present or not could be
  113. # written but it would need to check several special cases:
  114. # a. -displaycolumns "#all" or being an explicit columns list
  115. # b. column #0 display is not governed by the -displaycolumn
  116. # list but by the value of the -show option
  117. # --> Let's rather catch the following line.
  118. catch {$State(activeWidget) heading $State(activeHeading) state !active}
  119. }
  120. if {$heading != {}} {
  121. $w heading $heading state active
  122. }
  123. set State(activeHeading) $heading
  124. set State(activeWidget) $w
  125. }
  126. }
  127. ## Select $w $x $y $selectop
  128. # Binding procedure for selection operations.
  129. # See "Selection modes", below.
  130. #
  131. proc ttk::treeview::Select {w x y op} {
  132. if {[set item [$w identify row $x $y]] ne "" } {
  133. SelectOp $w $item $op
  134. }
  135. }
  136. ## DoubleClick -- Double-Button-1 binding.
  137. #
  138. proc ttk::treeview::DoubleClick {w x y} {
  139. if {[set row [$w identify row $x $y]] ne ""} {
  140. Toggle $w $row
  141. } else {
  142. Press $w $x $y ;# perform single-click action
  143. }
  144. }
  145. ## Press -- Button binding.
  146. #
  147. proc ttk::treeview::Press {w x y} {
  148. focus $w
  149. switch -- [$w identify region $x $y] {
  150. nothing { }
  151. heading { heading.press $w $x $y }
  152. separator { resize.press $w $x $y }
  153. tree -
  154. cell {
  155. set item [$w identify item $x $y]
  156. SelectOp $w $item choose
  157. switch -glob -- [$w identify element $x $y] {
  158. *indicator -
  159. *disclosure { Toggle $w $item }
  160. }
  161. }
  162. }
  163. }
  164. ## Drag -- B1-Motion binding
  165. #
  166. proc ttk::treeview::Drag {w x y} {
  167. variable State
  168. switch $State(pressMode) {
  169. resize { resize.drag $w $x }
  170. heading { heading.drag $w $x $y }
  171. }
  172. }
  173. proc ttk::treeview::Release {w x y} {
  174. variable State
  175. switch $State(pressMode) {
  176. resize { resize.release $w $x }
  177. heading { heading.release $w }
  178. }
  179. set State(pressMode) none
  180. Motion $w $x $y
  181. }
  182. ### Interactive column resizing.
  183. #
  184. proc ttk::treeview::resize.press {w x y} {
  185. variable State
  186. set State(pressMode) "resize"
  187. set State(resizeColumn) [$w identify column $x $y]
  188. }
  189. proc ttk::treeview::resize.drag {w x} {
  190. variable State
  191. $w drag $State(resizeColumn) $x
  192. }
  193. proc ttk::treeview::resize.release {w x} {
  194. $w drop
  195. }
  196. ### Heading activation.
  197. #
  198. proc ttk::treeview::heading.press {w x y} {
  199. variable State
  200. set column [$w identify column $x $y]
  201. set State(pressMode) "heading"
  202. set State(heading) $column
  203. $w heading $column state pressed
  204. }
  205. proc ttk::treeview::heading.drag {w x y} {
  206. variable State
  207. if { [$w identify region $x $y] eq "heading"
  208. && [$w identify column $x $y] eq $State(heading)
  209. } {
  210. $w heading $State(heading) state pressed
  211. } else {
  212. $w heading $State(heading) state !pressed
  213. }
  214. }
  215. proc ttk::treeview::heading.release {w} {
  216. variable State
  217. if {[lsearch -exact [$w heading $State(heading) state] pressed] >= 0} {
  218. after 0 [$w heading $State(heading) -command]
  219. }
  220. $w heading $State(heading) state !pressed
  221. }
  222. ### Selection modes.
  223. #
  224. ## SelectOp $w $item [ choose | extend | toggle ] --
  225. # Dispatch to appropriate selection operation
  226. # depending on current value of -selectmode.
  227. #
  228. proc ttk::treeview::SelectOp {w item op} {
  229. select.$op.[$w cget -selectmode] $w $item
  230. }
  231. ## -selectmode none:
  232. #
  233. proc ttk::treeview::select.choose.none {w item} { $w focus $item; $w see $item }
  234. proc ttk::treeview::select.toggle.none {w item} { $w focus $item; $w see $item }
  235. proc ttk::treeview::select.extend.none {w item} { $w focus $item; $w see $item }
  236. ## -selectmode browse:
  237. #
  238. proc ttk::treeview::select.choose.browse {w item} { BrowseTo $w $item }
  239. proc ttk::treeview::select.toggle.browse {w item} { BrowseTo $w $item }
  240. proc ttk::treeview::select.extend.browse {w item} { BrowseTo $w $item }
  241. ## -selectmode multiple:
  242. #
  243. proc ttk::treeview::select.choose.extended {w item} {
  244. BrowseTo $w $item
  245. }
  246. proc ttk::treeview::select.toggle.extended {w item} {
  247. $w selection toggle [list $item]
  248. }
  249. proc ttk::treeview::select.extend.extended {w item} {
  250. if {[set anchor [$w focus]] ne ""} {
  251. $w selection set [between $w $anchor $item]
  252. } else {
  253. BrowseTo $w $item
  254. }
  255. }
  256. ### Tree structure utilities.
  257. #
  258. ## between $tv $item1 $item2 --
  259. # Returns a list of all items between $item1 and $item2,
  260. # in preorder traversal order. $item1 and $item2 may be
  261. # in either order.
  262. #
  263. # NOTES:
  264. # This routine is O(N) in the size of the tree.
  265. # There's probably a way to do this that's O(N) in the number
  266. # of items returned, but I'm not clever enough to figure it out.
  267. #
  268. proc ttk::treeview::between {tv item1 item2} {
  269. variable between [list]
  270. variable selectingBetween 0
  271. ScanBetween $tv $item1 $item2 {}
  272. return $between
  273. }
  274. ## ScanBetween --
  275. # Recursive worker routine for ttk::treeview::between
  276. #
  277. proc ttk::treeview::ScanBetween {tv item1 item2 item} {
  278. variable between
  279. variable selectingBetween
  280. if {$item eq $item1 || $item eq $item2} {
  281. lappend between $item
  282. set selectingBetween [expr {!$selectingBetween}]
  283. } elseif {$selectingBetween} {
  284. lappend between $item
  285. }
  286. foreach child [$tv children $item] {
  287. ScanBetween $tv $item1 $item2 $child
  288. }
  289. }
  290. ### User interaction utilities.
  291. #
  292. ## OpenItem, CloseItem -- Set the open state of an item, generate event
  293. #
  294. proc ttk::treeview::OpenItem {w item} {
  295. $w focus $item
  296. event generate $w <<TreeviewOpen>>
  297. $w item $item -open true
  298. }
  299. proc ttk::treeview::CloseItem {w item} {
  300. $w item $item -open false
  301. $w focus $item
  302. event generate $w <<TreeviewClose>>
  303. }
  304. ## Toggle -- toggle opened/closed state of item
  305. #
  306. proc ttk::treeview::Toggle {w item} {
  307. # don't allow toggling on indicators that
  308. # are not present in front of leaf items
  309. if {[$w children $item] == {}} {
  310. return
  311. }
  312. # not a leaf, toggle!
  313. if {[$w item $item -open]} {
  314. CloseItem $w $item
  315. } else {
  316. OpenItem $w $item
  317. }
  318. }
  319. ## ToggleFocus -- toggle opened/closed state of focus item
  320. #
  321. proc ttk::treeview::ToggleFocus {w} {
  322. set item [$w focus]
  323. if {$item ne ""} {
  324. Toggle $w $item
  325. }
  326. }
  327. ## BrowseTo -- navigate to specified item; set focus and selection
  328. #
  329. proc ttk::treeview::BrowseTo {w item} {
  330. $w see $item
  331. $w focus $item
  332. $w selection set [list $item]
  333. }
  334. #*EOF*