PageRenderTime 63ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/FetchDICOM.vfs/lib/BWidget/tree.tcl

http://github.com/dblezek/FetchDICOM
TCL | 2114 lines | 1480 code | 245 blank | 389 comment | 339 complexity | ef94ae048ef98dbd50c1ea8c597c018c MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. # ----------------------------------------------------------------------------
  2. # tree.tcl
  3. # This file is part of Unifix BWidget Toolkit
  4. # $Id: tree.tcl,v 1.1 2003/11/11 18:16:57 blezek Exp $
  5. # ----------------------------------------------------------------------------
  6. # Index of commands:
  7. # - Tree::create
  8. # - Tree::configure
  9. # - Tree::cget
  10. # - Tree::insert
  11. # - Tree::itemconfigure
  12. # - Tree::itemcget
  13. # - Tree::bindText
  14. # - Tree::bindImage
  15. # - Tree::delete
  16. # - Tree::move
  17. # - Tree::reorder
  18. # - Tree::selection
  19. # - Tree::exists
  20. # - Tree::parent
  21. # - Tree::index
  22. # - Tree::nodes
  23. # - Tree::see
  24. # - Tree::opentree
  25. # - Tree::closetree
  26. # - Tree::edit
  27. # - Tree::xview
  28. # - Tree::yview
  29. # - Tree::_update_edit_size
  30. # - Tree::_destroy
  31. # - Tree::_see
  32. # - Tree::_recexpand
  33. # - Tree::_subdelete
  34. # - Tree::_update_scrollregion
  35. # - Tree::_cross_event
  36. # - Tree::_draw_node
  37. # - Tree::_draw_subnodes
  38. # - Tree::_update_nodes
  39. # - Tree::_draw_tree
  40. # - Tree::_redraw_tree
  41. # - Tree::_redraw_selection
  42. # - Tree::_redraw_idle
  43. # - Tree::_drag_cmd
  44. # - Tree::_drop_cmd
  45. # - Tree::_over_cmd
  46. # - Tree::_auto_scroll
  47. # - Tree::_scroll
  48. # ----------------------------------------------------------------------------
  49. namespace eval Tree {
  50. namespace eval Node {
  51. Widget::declare Tree::Node {
  52. {-text String "" 0}
  53. {-font TkResource "" 0 listbox}
  54. {-image TkResource "" 0 label}
  55. {-window String "" 0}
  56. {-fill TkResource black 0 {listbox -foreground}}
  57. {-data String "" 0}
  58. {-open Boolean 0 0}
  59. {-selectable Boolean 1 0}
  60. {-drawcross Enum auto 0 {auto allways never}}
  61. {-padx Int -1 0 "%d >= -1"}
  62. {-deltax Int -1 0 "%d >= -1"}
  63. }
  64. }
  65. DynamicHelp::include Tree::Node balloon
  66. Widget::tkinclude Tree canvas .c \
  67. remove {
  68. -insertwidth -insertbackground -insertborderwidth -insertofftime
  69. -insertontime -selectborderwidth -closeenough -confine -scrollregion
  70. -xscrollincrement -yscrollincrement -width -height
  71. } \
  72. initialize {
  73. -relief sunken -borderwidth 2 -takefocus 1
  74. -highlightthickness 1 -width 200
  75. }
  76. Widget::declare Tree {
  77. {-deltax Int 10 0 "%d >= 0"}
  78. {-deltay Int 15 0 "%d >= 0"}
  79. {-padx Int 20 0 "%d >= 0"}
  80. {-background TkResource "" 0 listbox}
  81. {-selectbackground TkResource "" 0 listbox}
  82. {-selectforeground TkResource "" 0 listbox}
  83. {-selectcommand String "" 0}
  84. {-width TkResource "" 0 listbox}
  85. {-height TkResource "" 0 listbox}
  86. {-selectfill Boolean 0 0}
  87. {-showlines Boolean 1 0}
  88. {-linesfill TkResource black 0 {listbox -foreground}}
  89. {-linestipple TkResource "" 0 {label -bitmap}}
  90. {-crossfill TkResource black 0 {listbox -foreground}}
  91. {-redraw Boolean 1 0}
  92. {-opencmd String "" 0}
  93. {-closecmd String "" 0}
  94. {-dropovermode Flag "wpn" 0 "wpn"}
  95. {-bg Synonym -background}
  96. }
  97. DragSite::include Tree "TREE_NODE" 1
  98. DropSite::include Tree {
  99. TREE_NODE {copy {} move {}}
  100. }
  101. Widget::addmap Tree "" .c {-deltay -yscrollincrement}
  102. # Trees on windows have a white (system window) background
  103. if { $::tcl_platform(platform) == "windows" } {
  104. option add *Tree.c.background SystemWindow widgetDefault
  105. option add *TreeNode.fill SystemWindowText widgetDefault
  106. }
  107. bind TreeSentinalStart <Button-1> {
  108. if { $::Tree::sentinal(%W) } {
  109. set ::Tree::sentinal(%W) 0
  110. break
  111. }
  112. }
  113. bind TreeSentinalEnd <Button-1> {
  114. set ::Tree::sentinal(%W) 0
  115. }
  116. bind TreeFocus <Button-1> [list focus %W]
  117. proc ::Tree { path args } { return [eval [list Tree::create $path] $args] }
  118. proc use {} {}
  119. variable _edit
  120. }
  121. # ----------------------------------------------------------------------------
  122. # Command Tree::create
  123. # ----------------------------------------------------------------------------
  124. proc Tree::create { path args } {
  125. variable $path
  126. upvar 0 $path data
  127. Widget::init Tree $path $args
  128. set ::Tree::sentinal($path.c) 0
  129. Widget::getVariable $path autoIndex
  130. set autoIndex 0
  131. set data(root) {{}}
  132. set data(selnodes) {}
  133. set data(upd,level) 0
  134. set data(upd,nodes) {}
  135. set data(upd,afterid) ""
  136. set data(dnd,scroll) ""
  137. set data(dnd,afterid) ""
  138. set data(dnd,selnodes) {}
  139. set data(dnd,node) ""
  140. frame $path -class Tree -bd 0 -highlightthickness 0 -relief flat \
  141. -takefocus 0
  142. # For 8.4+ we don't want to inherit the padding
  143. catch {$path configure -padx 0 -pady 0}
  144. eval [list canvas $path.c] [Widget::subcget $path .c] -xscrollincrement 8
  145. bindtags $path.c [list TreeSentinalStart TreeFocus $path.c Canvas \
  146. [winfo toplevel $path] all TreeSentinalEnd]
  147. pack $path.c -expand yes -fill both
  148. $path.c bind cross <ButtonPress-1> [list Tree::_cross_event $path]
  149. # Added by ericm@scriptics.com
  150. # These allow keyboard traversal of the tree
  151. bind $path.c <KeyPress-Up> [list Tree::_keynav up $path]
  152. bind $path.c <KeyPress-Down> [list Tree::_keynav down $path]
  153. bind $path.c <KeyPress-Right> [list Tree::_keynav right $path]
  154. bind $path.c <KeyPress-Left> [list Tree::_keynav left $path]
  155. bind $path.c <KeyPress-space> [list +Tree::_keynav space $path]
  156. # These allow keyboard control of the scrolling
  157. bind $path.c <Control-KeyPress-Up> [list $path.c yview scroll -1 units]
  158. bind $path.c <Control-KeyPress-Down> [list $path.c yview scroll 1 units]
  159. bind $path.c <Control-KeyPress-Left> [list $path.c xview scroll -1 units]
  160. bind $path.c <Control-KeyPress-Right> [list $path.c xview scroll 1 units]
  161. # ericm@scriptics.com
  162. bind $path <Configure> [list Tree::_update_scrollregion $path]
  163. bind $path <Destroy> [list Tree::_destroy $path]
  164. bind $path <FocusIn> [list after idle {BWidget::refocus %W %W.c}]
  165. DragSite::setdrag $path $path.c Tree::_init_drag_cmd \
  166. [Widget::cget $path -dragendcmd] 1
  167. DropSite::setdrop $path $path.c Tree::_over_cmd Tree::_drop_cmd 1
  168. rename $path ::$path:cmd
  169. proc ::$path { cmd args } "return \[eval Tree::\$cmd [list $path] \$args\]"
  170. set w [Widget::cget $path -width]
  171. set h [Widget::cget $path -height]
  172. set dy [Widget::cget $path -deltay]
  173. $path.c configure -width [expr {$w*8}] -height [expr {$h*$dy}]
  174. # ericm
  175. # Bind <Button-1> to select the clicked node -- no reason not to, right?
  176. Tree::bindText $path <Button-1> [list $path selection set]
  177. Tree::bindImage $path <Button-1> [list $path selection set]
  178. Tree::bindText $path <Control-Button-1> [list $path selection toggle]
  179. Tree::bindImage $path <Control-Button-1> [list $path selection toggle]
  180. # Add sentinal bindings for double-clicking on items, to handle the
  181. # gnarly Tk bug wherein:
  182. # ButtonClick
  183. # ButtonClick
  184. # On a canvas item translates into button click on the item, button click
  185. # on the canvas, double-button on the item, single button click on the
  186. # canvas (which can happen if the double-button on the item causes some
  187. # other event to be handled in between when the button clicks are examined
  188. # for the canvas)
  189. $path.c bind TreeItemSentinal <Double-Button-1> \
  190. [list set ::Tree::sentinal($path.c) 1]
  191. # ericm
  192. return $path
  193. }
  194. # ----------------------------------------------------------------------------
  195. # Command Tree::configure
  196. # ----------------------------------------------------------------------------
  197. proc Tree::configure { path args } {
  198. variable $path
  199. upvar 0 $path data
  200. set res [Widget::configure $path $args]
  201. set ch1 [expr {[Widget::hasChanged $path -deltax val] |
  202. [Widget::hasChanged $path -deltay dy] |
  203. [Widget::hasChanged $path -padx val] |
  204. [Widget::hasChanged $path -showlines val]}]
  205. set ch2 [expr {[Widget::hasChanged $path -selectbackground val] |
  206. [Widget::hasChanged $path -selectforeground val]}]
  207. if { [Widget::hasChanged $path -linesfill fill] |
  208. [Widget::hasChanged $path -linestipple stipple] } {
  209. $path.c itemconfigure line -fill $fill -stipple $stipple
  210. }
  211. if { [Widget::hasChanged $path -crossfill fill] } {
  212. $path.c itemconfigure cross -foreground $fill
  213. }
  214. if {[Widget::hasChanged $path -selectfill fill]} {
  215. # Make sure that the full-width boxes have either all or none
  216. # of the standard node bindings
  217. if {$fill} {
  218. foreach event [$path.c bind "node"] {
  219. $path.c bind "box" $event [$path.c bind "node" $event]
  220. }
  221. } else {
  222. foreach event [$path.c bind "node"] {
  223. $path.c bind "box" $event {}
  224. }
  225. }
  226. }
  227. if { $ch1 } {
  228. _redraw_idle $path 3
  229. } elseif { $ch2 } {
  230. _redraw_idle $path 1
  231. }
  232. if { [Widget::hasChanged $path -height h] } {
  233. $path.c configure -height [expr {$h*$dy}]
  234. }
  235. if { [Widget::hasChanged $path -width w] } {
  236. $path.c configure -width [expr {$w*8}]
  237. }
  238. if { [Widget::hasChanged $path -redraw bool] && $bool } {
  239. set upd $data(upd,level)
  240. set data(upd,level) 0
  241. _redraw_idle $path $upd
  242. }
  243. set force [Widget::hasChanged $path -dragendcmd dragend]
  244. DragSite::setdrag $path $path.c Tree::_init_drag_cmd $dragend $force
  245. DropSite::setdrop $path $path.c Tree::_over_cmd Tree::_drop_cmd
  246. return $res
  247. }
  248. # ----------------------------------------------------------------------------
  249. # Command Tree::cget
  250. # ----------------------------------------------------------------------------
  251. proc Tree::cget { path option } {
  252. return [Widget::cget $path $option]
  253. }
  254. # ----------------------------------------------------------------------------
  255. # Command Tree::insert
  256. # ----------------------------------------------------------------------------
  257. proc Tree::insert { path index parent node args } {
  258. variable $path
  259. upvar 0 $path data
  260. Widget::getVariable $path autoIndex
  261. set node [string map [list #auto [incr autoIndex]] $node]
  262. if { [info exists data($node)] } {
  263. return -code error "node \"$node\" already exists"
  264. }
  265. if { ![info exists data($parent)] } {
  266. return -code error "node \"$parent\" does not exist"
  267. }
  268. Widget::init Tree::Node $path.$node $args
  269. if {[string equal $index "end"]} {
  270. lappend data($parent) $node
  271. } else {
  272. incr index
  273. set data($parent) [linsert $data($parent) $index $node]
  274. }
  275. set data($node) [list $parent]
  276. if { [string equal $parent "root"] } {
  277. _redraw_idle $path 3
  278. } elseif { [visible $path $parent] } {
  279. # parent is visible...
  280. if { [Widget::getMegawidgetOption $path.$parent -open] } {
  281. # ...and opened -> redraw whole
  282. _redraw_idle $path 3
  283. } else {
  284. # ...and closed -> redraw cross
  285. lappend data(upd,nodes) $parent 8
  286. _redraw_idle $path 2
  287. }
  288. }
  289. return $node
  290. }
  291. # ----------------------------------------------------------------------------
  292. # Command Tree::itemconfigure
  293. # ----------------------------------------------------------------------------
  294. proc Tree::itemconfigure { path node args } {
  295. variable $path
  296. upvar 0 $path data
  297. if { [string equal $node "root"] || ![info exists data($node)] } {
  298. return -code error "node \"$node\" does not exist"
  299. }
  300. set result [Widget::configure $path.$node $args]
  301. _set_help $path $node
  302. if { [visible $path $node] } {
  303. set lopt {}
  304. set flag 0
  305. foreach opt {-window -image -drawcross -font -text -fill} {
  306. set flag [expr {$flag << 1}]
  307. if { [Widget::hasChanged $path.$node $opt val] } {
  308. set flag [expr {$flag | 1}]
  309. }
  310. }
  311. if { [Widget::hasChanged $path.$node -open val] } {
  312. if {[llength $data($node)] > 1} {
  313. # node have subnodes - full redraw
  314. _redraw_idle $path 3
  315. } else {
  316. # force a redraw of the plus/minus sign
  317. set flag [expr {$flag | 8}]
  318. }
  319. }
  320. if {$data(upd,level) < 3 && [Widget::hasChanged $path.$node -padx x]} {
  321. _redraw_idle $path 3
  322. }
  323. if { $data(upd,level) < 3 && $flag } {
  324. if { [set idx [lsearch -exact $data(upd,nodes) $node]] == -1 } {
  325. lappend data(upd,nodes) $node $flag
  326. } else {
  327. incr idx
  328. set flag [expr {[lindex $data(upd,nodes) $idx] | $flag}]
  329. set data(upd,nodes) [lreplace $data(upd,nodes) $idx $idx $flag]
  330. }
  331. _redraw_idle $path 2
  332. }
  333. }
  334. return $result
  335. }
  336. # ----------------------------------------------------------------------------
  337. # Command Tree::itemcget
  338. # ----------------------------------------------------------------------------
  339. proc Tree::itemcget { path node option } {
  340. # Instead of upvar'ing $path as data for this test, just directly refer to
  341. # it, as that is faster.
  342. if { [string equal $node "root"] || \
  343. ![info exists ::Tree::${path}($node)] } {
  344. return -code error "node \"$node\" does not exist"
  345. }
  346. return [Widget::cget $path.$node $option]
  347. }
  348. # ----------------------------------------------------------------------------
  349. # Command Tree::bindText
  350. # ----------------------------------------------------------------------------
  351. proc Tree::bindText { path event script } {
  352. if {[string length $script]} {
  353. append script " \[Tree::_get_node_name [list $path] current 2\]"
  354. }
  355. $path.c bind "node" $event $script
  356. if {[Widget::getoption $path -selectfill]} {
  357. $path.c bind "box" $event $script
  358. } else {
  359. $path.c bind "box" $event {}
  360. }
  361. }
  362. # ----------------------------------------------------------------------------
  363. # Command Tree::bindImage
  364. # ----------------------------------------------------------------------------
  365. proc Tree::bindImage { path event script } {
  366. if {[string length $script]} {
  367. append script " \[Tree::_get_node_name [list $path] current 2\]"
  368. }
  369. $path.c bind "img" $event $script
  370. if {[Widget::getoption $path -selectfill]} {
  371. $path.c bind "box" $event $script
  372. } else {
  373. $path.c bind "box" $event {}
  374. }
  375. }
  376. # ----------------------------------------------------------------------------
  377. # Command Tree::delete
  378. # ----------------------------------------------------------------------------
  379. proc Tree::delete { path args } {
  380. variable $path
  381. upvar 0 $path data
  382. foreach lnodes $args {
  383. foreach node $lnodes {
  384. if { ![string equal $node "root"] && [info exists data($node)] } {
  385. set parent [lindex $data($node) 0]
  386. set idx [lsearch -exact $data($parent) $node]
  387. set data($parent) [lreplace $data($parent) $idx $idx]
  388. _subdelete $path [list $node]
  389. }
  390. }
  391. }
  392. _redraw_idle $path 3
  393. }
  394. # ----------------------------------------------------------------------------
  395. # Command Tree::move
  396. # ----------------------------------------------------------------------------
  397. proc Tree::move { path parent node index } {
  398. variable $path
  399. upvar 0 $path data
  400. if { [string equal $node "root"] || ![info exists data($node)] } {
  401. return -code error "node \"$node\" does not exist"
  402. }
  403. if { ![info exists data($parent)] } {
  404. return -code error "node \"$parent\" does not exist"
  405. }
  406. set p $parent
  407. while { ![string equal $p "root"] } {
  408. if { [string equal $p $node] } {
  409. return -code error "node \"$parent\" is a descendant of \"$node\""
  410. }
  411. set p [parent $path $p]
  412. }
  413. set oldp [lindex $data($node) 0]
  414. set idx [lsearch -exact $data($oldp) $node]
  415. set data($oldp) [lreplace $data($oldp) $idx $idx]
  416. set data($node) [concat [list $parent] [lrange $data($node) 1 end]]
  417. if { [string equal $index "end"] } {
  418. lappend data($parent) $node
  419. } else {
  420. incr index
  421. set data($parent) [linsert $data($parent) $index $node]
  422. }
  423. if { ([string equal $oldp "root"] ||
  424. ([visible $path $oldp] && [Widget::getoption $path.$oldp -open])) ||
  425. ([string equal $parent "root"] ||
  426. ([visible $path $parent] && [Widget::getoption $path.$parent -open])) } {
  427. _redraw_idle $path 3
  428. }
  429. }
  430. # ----------------------------------------------------------------------------
  431. # Command Tree::reorder
  432. # ----------------------------------------------------------------------------
  433. proc Tree::reorder { path node neworder } {
  434. variable $path
  435. upvar 0 $path data
  436. if { ![info exists data($node)] } {
  437. return -code error "node \"$node\" does not exist"
  438. }
  439. set children [lrange $data($node) 1 end]
  440. if { [llength $children] } {
  441. set children [BWidget::lreorder $children $neworder]
  442. set data($node) [linsert $children 0 [lindex $data($node) 0]]
  443. if { [visible $path $node] && [Widget::getoption $path.$node -open] } {
  444. _redraw_idle $path 3
  445. }
  446. }
  447. }
  448. # ----------------------------------------------------------------------------
  449. # Command Tree::selection
  450. # ----------------------------------------------------------------------------
  451. proc Tree::selection { path cmd args } {
  452. variable $path
  453. upvar 0 $path data
  454. switch -- $cmd {
  455. toggle {
  456. foreach node $args {
  457. if {![info exists data($node)]} {
  458. return -code error \
  459. "$path selection toggle: Cannot toggle unknown node \"$node\"."
  460. }
  461. }
  462. foreach node $args {
  463. if {[$path selection includes $node]} {
  464. $path selection remove $node
  465. } else {
  466. $path selection add $node
  467. }
  468. }
  469. }
  470. set {
  471. foreach node $args {
  472. if {![info exists data($node)]} {
  473. return -code error \
  474. "$path selection set: Cannot select unknown node \"$node\"."
  475. }
  476. }
  477. set data(selnodes) {}
  478. foreach node $args {
  479. if { [Widget::getoption $path.$node -selectable] } {
  480. if { [lsearch -exact $data(selnodes) $node] == -1 } {
  481. lappend data(selnodes) $node
  482. }
  483. }
  484. }
  485. __call_selectcmd $path
  486. }
  487. add {
  488. foreach node $args {
  489. if {![info exists data($node)]} {
  490. return -code error \
  491. "$path selection add: Cannot select unknown node \"$node\"."
  492. }
  493. }
  494. foreach node $args {
  495. if { [Widget::getoption $path.$node -selectable] } {
  496. if { [lsearch -exact $data(selnodes) $node] == -1 } {
  497. lappend data(selnodes) $node
  498. }
  499. }
  500. }
  501. __call_selectcmd $path
  502. }
  503. range {
  504. # Here's our algorithm:
  505. # make a list of all nodes, then take the range from node1
  506. # to node2 and select those nodes
  507. #
  508. # This works because of how this widget handles redraws:
  509. # The tree is always completely redrawn, and always from
  510. # top to bottom. So the list of visible nodes *is* the
  511. # list of nodes, and we can use that to decide which nodes
  512. # to select.
  513. if {[llength $args] != 2} {
  514. return -code error \
  515. "wrong#args: Expected $path selection range node1 node2"
  516. }
  517. foreach {node1 node2} $args break
  518. if {![info exists data($node1)]} {
  519. return -code error \
  520. "$path selection range: Cannot start range at unknown node \"$node1\"."
  521. }
  522. if {![info exists data($node2)]} {
  523. return -code error \
  524. "$path selection range: Cannot end range at unknown node \"$node2\"."
  525. }
  526. set nodes {}
  527. foreach nodeItem [$path.c find withtag node] {
  528. set node [Tree::_get_node_name $path $nodeItem 2]
  529. if { [Widget::getoption $path.$node -selectable] } {
  530. lappend nodes $node
  531. }
  532. }
  533. # surles: Set the root string to the first element on the list.
  534. if {$node1 == "root"} {
  535. set node1 [lindex $nodes 0]
  536. }
  537. if {$node2 == "root"} {
  538. set node2 [lindex $nodes 0]
  539. }
  540. # Find the first visible ancestor of node1, starting with node1
  541. while {[set index1 [lsearch -exact $nodes $node1]] == -1} {
  542. set node1 [lindex $data($node1) 0]
  543. }
  544. # Find the first visible ancestor of node2, starting with node2
  545. while {[set index2 [lsearch -exact $nodes $node2]] == -1} {
  546. set node2 [lindex $data($node2) 0]
  547. }
  548. # If the nodes were given in backwards order, flip the
  549. # indices now
  550. if { $index2 < $index1 } {
  551. incr index1 $index2
  552. set index2 [expr {$index1 - $index2}]
  553. set index1 [expr {$index1 - $index2}]
  554. }
  555. set data(selnodes) [lrange $nodes $index1 $index2]
  556. __call_selectcmd $path
  557. }
  558. remove {
  559. foreach node $args {
  560. if { [set idx [lsearch -exact $data(selnodes) $node]] != -1 } {
  561. set data(selnodes) [lreplace $data(selnodes) $idx $idx]
  562. }
  563. }
  564. __call_selectcmd $path
  565. }
  566. clear {
  567. if {[llength $args] != 0} {
  568. return -code error \
  569. "wrong#args: Expected $path selection clear"
  570. }
  571. set data(selnodes) {}
  572. __call_selectcmd $path
  573. }
  574. get {
  575. if {[llength $args] != 0} {
  576. return -code error \
  577. "wrong#args: Expected $path selection get"
  578. }
  579. return $data(selnodes)
  580. }
  581. includes {
  582. if {[llength $args] != 1} {
  583. return -code error \
  584. "wrong#args: Expected $path selection includes node"
  585. }
  586. set node [lindex $args 0]
  587. return [expr {[lsearch -exact $data(selnodes) $node] != -1}]
  588. }
  589. default {
  590. return
  591. }
  592. }
  593. _redraw_idle $path 1
  594. }
  595. proc Tree::__call_selectcmd { path } {
  596. variable $path
  597. upvar 0 $path data
  598. set selectcmd [Widget::getoption $path -selectcommand]
  599. if {[llength $selectcmd]} {
  600. lappend selectcmd $path
  601. lappend selectcmd $data(selnodes)
  602. uplevel \#0 $selectcmd
  603. }
  604. return
  605. }
  606. # ----------------------------------------------------------------------------
  607. # Command Tree::exists
  608. # ----------------------------------------------------------------------------
  609. proc Tree::exists { path node } {
  610. variable $path
  611. upvar 0 $path data
  612. return [info exists data($node)]
  613. }
  614. # ----------------------------------------------------------------------------
  615. # Command Tree::visible
  616. # ----------------------------------------------------------------------------
  617. proc Tree::visible { path node } {
  618. set idn [$path.c find withtag n:$node]
  619. return [llength $idn]
  620. }
  621. # ----------------------------------------------------------------------------
  622. # Command Tree::parent
  623. # ----------------------------------------------------------------------------
  624. proc Tree::parent { path node } {
  625. variable $path
  626. upvar 0 $path data
  627. if { ![info exists data($node)] } {
  628. return -code error "node \"$node\" does not exist"
  629. }
  630. return [lindex $data($node) 0]
  631. }
  632. # ----------------------------------------------------------------------------
  633. # Command Tree::index
  634. # ----------------------------------------------------------------------------
  635. proc Tree::index { path node } {
  636. variable $path
  637. upvar 0 $path data
  638. if { [string equal $node "root"] || ![info exists data($node)] } {
  639. return -code error "node \"$node\" does not exist"
  640. }
  641. set parent [lindex $data($node) 0]
  642. return [expr {[lsearch -exact $data($parent) $node] - 1}]
  643. }
  644. # ----------------------------------------------------------------------------
  645. # Tree::find
  646. # Returns the node given a position.
  647. # findInfo @x,y ?confine?
  648. # lineNumber
  649. # ----------------------------------------------------------------------------
  650. proc Tree::find {path findInfo {confine ""}} {
  651. if {[regexp -- {^@([0-9]+),([0-9]+)$} $findInfo match x y]} {
  652. set x [$path.c canvasx $x]
  653. set y [$path.c canvasy $y]
  654. } elseif {[regexp -- {^[0-9]+$} $findInfo lineNumber]} {
  655. set dy [Widget::getoption $path -deltay]
  656. set y [expr {$dy*($lineNumber+0.5)}]
  657. set confine ""
  658. } else {
  659. return -code error "invalid find spec \"$findInfo\""
  660. }
  661. set found 0
  662. set region [$path.c bbox all]
  663. if {[llength $region]} {
  664. set xi [lindex $region 0]
  665. set xs [lindex $region 2]
  666. foreach id [$path.c find overlapping $xi $y $xs $y] {
  667. set ltags [$path.c gettags $id]
  668. set item [lindex $ltags 1]
  669. if { [string equal $item "node"] ||
  670. [string equal $item "img"] ||
  671. [string equal $item "win"] } {
  672. # item is the label or image/window of the node
  673. set node [Tree::_get_node_name $path $id 2]
  674. set found 1
  675. break
  676. }
  677. }
  678. }
  679. if {$found} {
  680. if {[string equal $confine "confine"]} {
  681. # test if x stand inside node bbox
  682. set padx [_get_node_padx $path $node]
  683. set xi [expr {[lindex [$path.c coords n:$node] 0] - $padx}]
  684. set xs [lindex [$path.c bbox n:$node] 2]
  685. if {$x >= $xi && $x <= $xs} {
  686. return $node
  687. }
  688. } else {
  689. return $node
  690. }
  691. }
  692. return ""
  693. }
  694. # ----------------------------------------------------------------------------
  695. # Command Tree::line
  696. # Returns the line where is drawn a node.
  697. # ----------------------------------------------------------------------------
  698. proc Tree::line {path node} {
  699. set item [$path.c find withtag n:$node]
  700. if {[string length $item]} {
  701. set dy [Widget::getoption $path -deltay]
  702. set y [lindex [$path.c coords $item] 1]
  703. set line [expr {int($y/$dy)}]
  704. } else {
  705. set line -1
  706. }
  707. return $line
  708. }
  709. # ----------------------------------------------------------------------------
  710. # Command Tree::nodes
  711. # ----------------------------------------------------------------------------
  712. proc Tree::nodes { path node {first ""} {last ""} } {
  713. variable $path
  714. upvar 0 $path data
  715. if { ![info exists data($node)] } {
  716. return -code error "node \"$node\" does not exist"
  717. }
  718. if { ![string length $first] } {
  719. return [lrange $data($node) 1 end]
  720. }
  721. if { ![string length $last] } {
  722. return [lindex [lrange $data($node) 1 end] $first]
  723. } else {
  724. return [lrange [lrange $data($node) 1 end] $first $last]
  725. }
  726. }
  727. # Tree::visiblenodes --
  728. #
  729. # Retrieve a list of all the nodes in a tree.
  730. #
  731. # Arguments:
  732. # path tree to retrieve nodes for.
  733. #
  734. # Results:
  735. # nodes list of nodes in the tree.
  736. proc Tree::visiblenodes { path } {
  737. variable $path
  738. upvar 0 $path data
  739. # Root is always open (?), so all of its children automatically get added
  740. # to the result, and to the stack.
  741. set st [lrange $data(root) 1 end]
  742. set result $st
  743. while {[llength $st]} {
  744. set node [lindex $st end]
  745. set st [lreplace $st end end]
  746. # Danger, danger! Using getMegawidgetOption is fragile, but much
  747. # much faster than going through cget.
  748. if { [Widget::getMegawidgetOption $path.$node -open] } {
  749. set nodes [lrange $data($node) 1 end]
  750. set result [concat $result $nodes]
  751. set st [concat $st $nodes]
  752. }
  753. }
  754. return $result
  755. }
  756. # ----------------------------------------------------------------------------
  757. # Command Tree::see
  758. # ----------------------------------------------------------------------------
  759. proc Tree::see { path node } {
  760. variable $path
  761. upvar 0 $path data
  762. if { [Widget::getoption $path -redraw] && $data(upd,afterid) != "" } {
  763. after cancel $data(upd,afterid)
  764. _redraw_tree $path
  765. }
  766. set idn [$path.c find withtag n:$node]
  767. if { $idn != "" } {
  768. Tree::_see $path $idn
  769. }
  770. }
  771. # ----------------------------------------------------------------------------
  772. # Command Tree::opentree
  773. # ----------------------------------------------------------------------------
  774. # JDC: added option recursive
  775. proc Tree::opentree { path node {recursive 1} } {
  776. variable $path
  777. upvar 0 $path data
  778. if { [string equal $node "root"] || ![info exists data($node)] } {
  779. return -code error "node \"$node\" does not exist"
  780. }
  781. _recexpand $path $node 1 $recursive [Widget::getoption $path -opencmd]
  782. _redraw_idle $path 3
  783. }
  784. # ----------------------------------------------------------------------------
  785. # Command Tree::closetree
  786. # ----------------------------------------------------------------------------
  787. proc Tree::closetree { path node {recursive 1} } {
  788. variable $path
  789. upvar 0 $path data
  790. if { [string equal $node "root"] || ![info exists data($node)] } {
  791. return -code error "node \"$node\" does not exist"
  792. }
  793. _recexpand $path $node 0 $recursive [Widget::getoption $path -closecmd]
  794. _redraw_idle $path 3
  795. }
  796. # ----------------------------------------------------------------------------
  797. # Command Tree::edit
  798. # ----------------------------------------------------------------------------
  799. proc Tree::edit { path node text {verifycmd ""} {clickres 0} {select 1}} {
  800. variable _edit
  801. variable $path
  802. upvar 0 $path data
  803. if { [Widget::getoption $path -redraw] && $data(upd,afterid) != "" } {
  804. after cancel $data(upd,afterid)
  805. _redraw_tree $path
  806. }
  807. set idn [$path.c find withtag n:$node]
  808. if { $idn != "" } {
  809. Tree::_see $path $idn
  810. set oldfg [$path.c itemcget $idn -fill]
  811. set sbg [Widget::getoption $path -selectbackground]
  812. set coords [$path.c coords $idn]
  813. set x [lindex $coords 0]
  814. set y [lindex $coords 1]
  815. set bd [expr {[$path.c cget -borderwidth]+[$path.c cget -highlightthickness]}]
  816. set w [expr {[winfo width $path] - 2*$bd}]
  817. set wmax [expr {[$path.c canvasx $w]-$x}]
  818. set _edit(text) $text
  819. set _edit(wait) 0
  820. $path.c itemconfigure $idn -fill [Widget::getoption $path -background]
  821. $path.c itemconfigure s:$node -fill {} -outline {}
  822. set frame [frame $path.edit \
  823. -relief flat -borderwidth 0 -highlightthickness 0 \
  824. -background [Widget::getoption $path -background]]
  825. set ent [entry $frame.edit \
  826. -width 0 \
  827. -relief solid \
  828. -borderwidth 1 \
  829. -highlightthickness 0 \
  830. -foreground [Widget::getoption $path.$node -fill] \
  831. -background [Widget::getoption $path -background] \
  832. -selectforeground [Widget::getoption $path -selectforeground] \
  833. -selectbackground $sbg \
  834. -font [Widget::getoption $path.$node -font] \
  835. -textvariable Tree::_edit(text)]
  836. pack $ent -ipadx 8 -anchor w
  837. set idw [$path.c create window $x $y -window $frame -anchor w]
  838. trace variable Tree::_edit(text) w \
  839. [list Tree::_update_edit_size $path $ent $idw $wmax]
  840. tkwait visibility $ent
  841. grab $frame
  842. BWidget::focus set $ent
  843. _update_edit_size $path $ent $idw $wmax
  844. update
  845. if { $select } {
  846. $ent selection range 0 end
  847. $ent icursor end
  848. $ent xview end
  849. }
  850. bindtags $ent [list $ent Entry]
  851. bind $ent <Escape> {set Tree::_edit(wait) 0}
  852. bind $ent <Return> {set Tree::_edit(wait) 1}
  853. if { $clickres == 0 || $clickres == 1 } {
  854. bind $frame <Button> [list set Tree::_edit(wait) $clickres]
  855. }
  856. set ok 0
  857. while { !$ok } {
  858. tkwait variable Tree::_edit(wait)
  859. if { !$_edit(wait) || $verifycmd == "" ||
  860. [uplevel \#0 $verifycmd [list $_edit(text)]] } {
  861. set ok 1
  862. }
  863. }
  864. trace vdelete Tree::_edit(text) w \
  865. [list Tree::_update_edit_size $path $ent $idw $wmax]
  866. grab release $frame
  867. BWidget::focus release $ent
  868. destroy $frame
  869. $path.c delete $idw
  870. $path.c itemconfigure $idn -fill $oldfg
  871. $path.c itemconfigure s:$node -fill $sbg -outline $sbg
  872. if { $_edit(wait) } {
  873. return $_edit(text)
  874. }
  875. }
  876. return ""
  877. }
  878. # ----------------------------------------------------------------------------
  879. # Command Tree::xview
  880. # ----------------------------------------------------------------------------
  881. proc Tree::xview { path args } {
  882. return [eval [list $path.c xview] $args]
  883. }
  884. # ----------------------------------------------------------------------------
  885. # Command Tree::yview
  886. # ----------------------------------------------------------------------------
  887. proc Tree::yview { path args } {
  888. return [eval [list $path.c yview] $args]
  889. }
  890. # ----------------------------------------------------------------------------
  891. # Command Tree::_update_edit_size
  892. # ----------------------------------------------------------------------------
  893. proc Tree::_update_edit_size { path entry idw wmax args } {
  894. set entw [winfo reqwidth $entry]
  895. if { $entw+8 >= $wmax } {
  896. $path.c itemconfigure $idw -width $wmax
  897. } else {
  898. $path.c itemconfigure $idw -width 0
  899. }
  900. }
  901. # ----------------------------------------------------------------------------
  902. # Command Tree::_destroy
  903. # ----------------------------------------------------------------------------
  904. proc Tree::_destroy { path } {
  905. variable $path
  906. upvar 0 $path data
  907. if { $data(upd,afterid) != "" } {
  908. after cancel $data(upd,afterid)
  909. }
  910. if { $data(dnd,afterid) != "" } {
  911. after cancel $data(dnd,afterid)
  912. }
  913. _subdelete $path [lrange $data(root) 1 end]
  914. Widget::destroy $path
  915. unset data
  916. rename $path {}
  917. }
  918. # ----------------------------------------------------------------------------
  919. # Command Tree::_see
  920. # ----------------------------------------------------------------------------
  921. proc Tree::_see { path idn } {
  922. set bbox [$path.c bbox $idn]
  923. set scrl [$path.c cget -scrollregion]
  924. set ymax [lindex $scrl 3]
  925. set dy [$path.c cget -yscrollincrement]
  926. set yv [$path yview]
  927. set yv0 [expr {round([lindex $yv 0]*$ymax/$dy)}]
  928. set yv1 [expr {round([lindex $yv 1]*$ymax/$dy)}]
  929. set y [expr {int([lindex [$path.c coords $idn] 1]/$dy)}]
  930. if { $y < $yv0 } {
  931. $path.c yview scroll [expr {$y-$yv0}] units
  932. } elseif { $y >= $yv1 } {
  933. $path.c yview scroll [expr {$y-$yv1+1}] units
  934. }
  935. set xmax [lindex $scrl 2]
  936. set dx [$path.c cget -xscrollincrement]
  937. set xv [$path xview]
  938. set x0 [expr {int([lindex $bbox 0]/$dx)}]
  939. set xv0 [expr {round([lindex $xv 0]*$xmax/$dx)}]
  940. set xv1 [expr {round([lindex $xv 1]*$xmax/$dx)}]
  941. if { $x0 >= $xv1 || $x0 < $xv0 } {
  942. $path.c xview scroll [expr {$x0-$xv0}] units
  943. }
  944. }
  945. # ----------------------------------------------------------------------------
  946. # Command Tree::_recexpand
  947. # ----------------------------------------------------------------------------
  948. # JDC : added option recursive
  949. proc Tree::_recexpand { path node expand recursive cmd } {
  950. variable $path
  951. upvar 0 $path data
  952. if { [Widget::getoption $path.$node -open] != $expand } {
  953. Widget::setoption $path.$node -open $expand
  954. if { $cmd != "" } {
  955. uplevel \#0 $cmd [list $node]
  956. }
  957. }
  958. if { $recursive } {
  959. foreach subnode [lrange $data($node) 1 end] {
  960. _recexpand $path $subnode $expand $recursive $cmd
  961. }
  962. }
  963. }
  964. # ----------------------------------------------------------------------------
  965. # Command Tree::_subdelete
  966. # ----------------------------------------------------------------------------
  967. proc Tree::_subdelete { path lnodes } {
  968. variable $path
  969. upvar 0 $path data
  970. set sel $data(selnodes)
  971. while { [llength $lnodes] } {
  972. set lsubnodes [list]
  973. foreach node $lnodes {
  974. foreach subnode [lrange $data($node) 1 end] {
  975. lappend lsubnodes $subnode
  976. }
  977. unset data($node)
  978. set idx [lsearch -exact $sel $node]
  979. if { $idx >= 0 } {
  980. set sel [lreplace $sel $idx $idx]
  981. }
  982. if { [set win [Widget::getoption $path.$node -window]] != "" } {
  983. destroy $win
  984. }
  985. Widget::destroy $path.$node
  986. }
  987. set lnodes $lsubnodes
  988. }
  989. set data(selnodes) $sel
  990. }
  991. # ----------------------------------------------------------------------------
  992. # Command Tree::_update_scrollregion
  993. # ----------------------------------------------------------------------------
  994. proc Tree::_update_scrollregion { path } {
  995. set bd [expr {2*([$path.c cget -borderwidth]+[$path.c cget -highlightthickness])}]
  996. set w [expr {[winfo width $path] - $bd}]
  997. set h [expr {[winfo height $path] - $bd}]
  998. set xinc [$path.c cget -xscrollincrement]
  999. set yinc [$path.c cget -yscrollincrement]
  1000. set bbox [$path.c bbox node]
  1001. if { [llength $bbox] } {
  1002. set xs [lindex $bbox 2]
  1003. set ys [lindex $bbox 3]
  1004. if { $w < $xs } {
  1005. set w [expr {int($xs)}]
  1006. if { [set r [expr {$w % $xinc}]] } {
  1007. set w [expr {$w+$xinc-$r}]
  1008. }
  1009. }
  1010. if { $h < $ys } {
  1011. set h [expr {int($ys)}]
  1012. if { [set r [expr {$h % $yinc}]] } {
  1013. set h [expr {$h+$yinc-$r}]
  1014. }
  1015. }
  1016. }
  1017. $path.c configure -scrollregion [list 0 0 $w $h]
  1018. if {[Widget::getoption $path -selectfill]} {
  1019. _redraw_selection $path
  1020. }
  1021. }
  1022. # ----------------------------------------------------------------------------
  1023. # Command Tree::_cross_event
  1024. # ----------------------------------------------------------------------------
  1025. proc Tree::_cross_event { path } {
  1026. variable $path
  1027. upvar 0 $path data
  1028. set node [Tree::_get_node_name $path current 1]
  1029. if { [Widget::getoption $path.$node -open] } {
  1030. Tree::itemconfigure $path $node -open 0
  1031. if { [set cmd [Widget::getoption $path -closecmd]] != "" } {
  1032. uplevel \#0 $cmd [list $node]
  1033. }
  1034. } else {
  1035. Tree::itemconfigure $path $node -open 1
  1036. if { [set cmd [Widget::getoption $path -opencmd]] != "" } {
  1037. uplevel \#0 $cmd [list $node]
  1038. }
  1039. }
  1040. }
  1041. # ----------------------------------------------------------------------------
  1042. # Command Tree::_draw_node
  1043. # ----------------------------------------------------------------------------
  1044. proc Tree::_draw_node { path node x0 y0 deltax deltay padx showlines } {
  1045. global env
  1046. variable $path
  1047. upvar 0 $path data
  1048. set x1 [expr {$x0+$deltax+5}]
  1049. set y1 $y0
  1050. if { $showlines } {
  1051. $path.c create line $x0 $y0 $x1 $y0 \
  1052. -fill [Widget::getoption $path -linesfill] \
  1053. -stipple [Widget::getoption $path -linestipple] \
  1054. -tags line
  1055. }
  1056. $path.c create text [expr {$x1+$padx}] $y0 \
  1057. -text [Widget::getoption $path.$node -text] \
  1058. -fill [Widget::getoption $path.$node -fill] \
  1059. -font [Widget::getoption $path.$node -font] \
  1060. -anchor w \
  1061. -tags [Tree::_get_node_tags $path $node [list node n:$node]]
  1062. set len [expr {[llength $data($node)] > 1}]
  1063. set dc [Widget::getoption $path.$node -drawcross]
  1064. set exp [Widget::getoption $path.$node -open]
  1065. if { $len && $exp } {
  1066. set y1 [_draw_subnodes $path [lrange $data($node) 1 end] \
  1067. [expr {$x0+$deltax}] $y0 $deltax $deltay $padx $showlines]
  1068. }
  1069. if {![string equal $dc "never"] && ($len || [string equal $dc "allways"])} {
  1070. if { $exp } {
  1071. set bmp [file join $::BWIDGET::LIBRARY "images" "minus.xbm"]
  1072. } else {
  1073. set bmp [file join $::BWIDGET::LIBRARY "images" "plus.xbm"]
  1074. }
  1075. $path.c create bitmap $x0 $y0 \
  1076. -bitmap @$bmp \
  1077. -background [$path.c cget -background] \
  1078. -foreground [Widget::getoption $path -crossfill] \
  1079. -tags [list cross c:$node] -anchor c
  1080. }
  1081. if { [set win [Widget::getoption $path.$node -window]] != "" } {
  1082. $path.c create window $x1 $y0 -window $win -anchor w \
  1083. -tags [Tree::_get_node_tags $path $node [list win i:$node]]
  1084. } elseif { [set img [Widget::getoption $path.$node -image]] != "" } {
  1085. $path.c create image $x1 $y0 -image $img -anchor w \
  1086. -tags [Tree::_get_node_tags $path $node [list img i:$node]]
  1087. }
  1088. set box [$path.c bbox n:$node i:$node]
  1089. set id [$path.c create rect 0 [lindex $box 1] \
  1090. [winfo screenwidth $path] [lindex $box 3] \
  1091. -tags [Tree::_get_node_tags $path $node [list box b:$node]] \
  1092. -fill {} -outline {}]
  1093. $path.c lower $id
  1094. _set_help $path $node
  1095. return $y1
  1096. }
  1097. # ----------------------------------------------------------------------------
  1098. # Command Tree::_draw_subnodes
  1099. # ----------------------------------------------------------------------------
  1100. proc Tree::_draw_subnodes { path nodes x0 y0 deltax deltay padx showlines } {
  1101. set y1 $y0
  1102. foreach node $nodes {
  1103. set padx [_get_node_padx $path $node]
  1104. set deltax [_get_node_deltax $path $node]
  1105. set yp $y1
  1106. set y1 [_draw_node $path $node $x0 [expr {$y1+$deltay}] $deltax $deltay $padx $showlines]
  1107. }
  1108. if { $showlines && [llength $nodes] } {
  1109. set id [$path.c create line $x0 $y0 $x0 [expr {$yp+$deltay}] \
  1110. -fill [Widget::getoption $path -linesfill] \
  1111. -stipple [Widget::getoption $path -linestipple] \
  1112. -tags line]
  1113. $path.c lower $id
  1114. }
  1115. return $y1
  1116. }
  1117. # ----------------------------------------------------------------------------
  1118. # Command Tree::_update_nodes
  1119. # ----------------------------------------------------------------------------
  1120. proc Tree::_update_nodes { path } {
  1121. global env
  1122. variable $path
  1123. upvar 0 $path data
  1124. set deltax [Widget::getoption $path -deltax]
  1125. set padx [Widget::getoption $path -padx]
  1126. foreach {node flag} $data(upd,nodes) {
  1127. set idn [$path.c find withtag "n:$node"]
  1128. if { $idn == "" } {
  1129. continue
  1130. }
  1131. set padx [_get_node_padx $path $node]
  1132. set deltax [_get_node_deltax $path $node]
  1133. set c [$path.c coords $idn]
  1134. set x0 [expr {[lindex $c 0]-$padx}]
  1135. set y0 [lindex $c 1]
  1136. if { $flag & 48 } {
  1137. # -window or -image modified
  1138. set win [Widget::getoption $path.$node -window]
  1139. set img [Widget::getoption $path.$node -image]
  1140. set idi [$path.c find withtag i:$node]
  1141. set type [lindex [$path.c gettags $idi] 1]
  1142. if { [string length $win] } {
  1143. if { [string equal $type "win"] } {
  1144. $path.c itemconfigure $idi -window $win
  1145. } else {
  1146. $path.c delete $idi
  1147. $path.c create window $x0 $y0 -window $win -anchor w \
  1148. -tags [Tree::_get_node_tags $path $node \
  1149. [list win i:$node]]
  1150. }
  1151. } elseif { [string length $img] } {
  1152. if { [string equal $type "img"] } {
  1153. $path.c itemconfigure $idi -image $img
  1154. } else {
  1155. $path.c delete $idi
  1156. $path.c create image $x0 $y0 -image $img -anchor w \
  1157. -tags [Tree::_get_node_tags $path $node \
  1158. [list img i:$node]]
  1159. }
  1160. } else {
  1161. $path.c delete $idi
  1162. }
  1163. }
  1164. if { $flag & 8 } {
  1165. # -drawcross modified
  1166. set len [expr {[llength $data($node)] > 1}]
  1167. set dc [Widget::getoption $path.$node -drawcross]
  1168. set exp [Widget::getoption $path.$node -open]
  1169. set idc [$path.c find withtag c:$node]
  1170. if {![string equal $dc "never"] && ($len || [string equal $dc "allways"])} {
  1171. if { $exp } {
  1172. set bmp [file join $::BWIDGET::LIBRARY "images" "minus.xbm"]
  1173. } else {
  1174. set bmp [file join $::BWIDGET::LIBRARY "images" "plus.xbm"]
  1175. }
  1176. if { $idc == "" } {
  1177. $path.c create bitmap [expr {$x0-$deltax-5}] $y0 \
  1178. -bitmap @$bmp \
  1179. -background [$path.c cget -background] \
  1180. -foreground [Widget::getoption $path -crossfill] \
  1181. -tags [list cross c:$node] -anchor c
  1182. } else {
  1183. $path.c itemconfigure $idc -bitmap @$bmp
  1184. }
  1185. } else {
  1186. $path.c delete $idc
  1187. }
  1188. }
  1189. if { $flag & 7 } {
  1190. # -font, -text or -fill modified
  1191. $path.c itemconfigure $idn \
  1192. -text [Widget::getoption $path.$node -text] \
  1193. -fill [Widget::getoption $path.$node -fill] \
  1194. -font [Widget::getoption $path.$node -font]
  1195. }
  1196. }
  1197. }
  1198. # ----------------------------------------------------------------------------
  1199. # Command Tree::_draw_tree
  1200. # ----------------------------------------------------------------------------
  1201. proc Tree::_draw_tree { path } {
  1202. variable $path
  1203. upvar 0 $path data
  1204. $path.c delete all
  1205. set cursor [$path.c cget -cursor]
  1206. $path.c configure -cursor watch
  1207. _draw_subnodes $path [lrange $data(root) 1 end] 8 \
  1208. [expr {-[Widget::getoption $path -deltay]/2}] \
  1209. [Widget::getoption $path -deltax] \
  1210. [Widget::getoption $path -deltay] \
  1211. [Widget::getoption $path -padx] \
  1212. [Widget::getoption $path -showlines]
  1213. $path.c configure -cursor $cursor
  1214. }
  1215. # ----------------------------------------------------------------------------
  1216. # Command Tree::_redraw_tree
  1217. # ----------------------------------------------------------------------------
  1218. proc Tree::_redraw_tree { path } {
  1219. variable $path
  1220. upvar 0 $path data
  1221. if { [Widget::getoption $path -redraw] } {
  1222. if { $data(upd,level) == 2 } {
  1223. _update_nodes $path
  1224. } elseif { $data(upd,level) == 3 } {
  1225. _draw_tree $path
  1226. }
  1227. _redraw_selection $path
  1228. _update_scrollregion $path
  1229. set data(upd,nodes) {}
  1230. set data(upd,level) 0
  1231. set data(upd,afterid) ""
  1232. }
  1233. }
  1234. # ----------------------------------------------------------------------------
  1235. # Command Tree::_redraw_selection
  1236. # ----------------------------------------------------------------------------
  1237. proc Tree::_redraw_selection { path } {
  1238. variable $path
  1239. upvar 0 $path data
  1240. set selbg [Widget::getoption $path -selectbackground]
  1241. set selfg [Widget::getoption $path -selectforeground]
  1242. set fill [Widget::getoption $path -selectfill]
  1243. if {$fill} {
  1244. set scroll [$path.c cget -scrollregion]
  1245. if {[llength $scroll]} {
  1246. set xmax [expr {[lindex $scroll 2]-1}]
  1247. } else {
  1248. set xmax [winfo width $path]
  1249. }
  1250. }
  1251. foreach id [$path.c find withtag sel] {
  1252. set node [Tree::_get_node_name $path $id 1]
  1253. $path.c itemconfigure "n:$node" -fill [Widget::getoption $path.$node -fill]
  1254. }
  1255. $path.c delete sel
  1256. foreach node $data(selnodes) {
  1257. set bbox [$path.c bbox "n:$node"]
  1258. if { [llength $bbox] } {
  1259. if {$fill} {
  1260. # get the image to (if any), as it may have different height
  1261. set bbox [$path.c bbox "n:$node" "i:$node"]
  1262. set bbox [list 0 [lindex $bbox 1] $xmax [lindex $bbox 3]]
  1263. }
  1264. set id [$path.c create rectangle $bbox -tags [list sel s:$node] \
  1265. -fill $selbg -outline $selbg]
  1266. $path.c itemconfigure "n:$node" -fill $selfg
  1267. $path.c lower $id
  1268. }
  1269. }
  1270. }
  1271. # ----------------------------------------------------------------------------
  1272. # Command Tree::_redraw_idle
  1273. # ----------------------------------------------------------------------------
  1274. proc Tree::_redraw_idle { path level } {
  1275. variable $path
  1276. upvar 0 $path data
  1277. if { [Widget::getoption $path -redraw] && $data(upd,afterid) == "" } {
  1278. set data(upd,afterid) [after idle Tree::_redraw_tree $path]
  1279. }
  1280. if { $level > $data(upd,level) } {
  1281. set data(upd,level) $level
  1282. }
  1283. return ""
  1284. }
  1285. # ----------------------------------------------------------------------------
  1286. # Command Tree::_init_drag_cmd
  1287. # ----------------------------------------------------------------------------
  1288. proc Tree::_init_drag_cmd { path X Y top } {
  1289. set path [winfo parent $path]
  1290. set ltags [$path.c gettags current]
  1291. set item [lindex $ltags 1]
  1292. if { [string equal $item "node"] ||
  1293. [string equal $item "img"] ||
  1294. [string equal $item "win"] } {
  1295. set node [Tree::_get_node_name $path current 2]
  1296. if { [set cmd [Widget::getoption $path -draginitcmd]] != "" } {
  1297. return [uplevel \#0 $cmd [list $path $node $top]]
  1298. }
  1299. if { [set type [Widget::getoption $path -dragtype]] == "" } {
  1300. set type "TREE_NODE"
  1301. }
  1302. if { [set img [Widget::getoption $path.$node -image]] != "" } {
  1303. pack [label $top.l -image $img -padx 0 -pady 0]
  1304. }
  1305. return [list $type {copy move link} $node]
  1306. }
  1307. return {}
  1308. }
  1309. # ----------------------------------------------------------------------------
  1310. # Command Tree::_drop_cmd
  1311. # ----------------------------------------------------------------------------
  1312. proc Tree::_drop_cmd { path source X Y op type dnddata } {
  1313. set path [winfo parent $path]
  1314. variable $path
  1315. upvar 0 $path data
  1316. $path.c delete drop
  1317. if { [string length $data(dnd,afterid)] } {
  1318. after cancel $data(dnd,afterid)
  1319. set data(dnd,afterid) ""
  1320. }
  1321. set data(dnd,scroll) ""
  1322. if { [llength $data(dnd,node)] } {
  1323. if { [set cmd [Widget::getoption $path -dropcmd]] != "" } {
  1324. return [uplevel \#0 $cmd [list $path $source $data(dnd,node) $op $type $dnddata]]
  1325. }
  1326. }
  1327. return 0
  1328. }
  1329. # ----------------------------------------------------------------------------
  1330. # Command Tree::_over_cmd
  1331. # ----------------------------------------------------------------------------
  1332. proc Tree::_over_cmd { path source event X Y op type dnddata } {
  1333. set path [winfo parent $path]
  1334. variable $path
  1335. upvar 0 $path data
  1336. if { [string equal $event "leave"] } {
  1337. # we leave the window tree
  1338. $path.c delete drop
  1339. if { [string length $data(dnd,afterid)] } {
  1340. after cancel $data(dnd,afterid)
  1341. set data(dnd,afterid) ""
  1342. }
  1343. set data(dnd,scroll) ""
  1344. return 0
  1345. }
  1346. if { [string equal $event "enter"] } {
  1347. # we enter the window tree - dnd data initialization
  1348. set mode [Widget::getoption $path -dropovermode]
  1349. set data(dnd,mode) 0
  1350. foreach c {w p n} {
  1351. set data(dnd,mode) [expr {($data(dnd,mode) << 1) | ([string first $c $mode] != -1)}]
  1352. }
  1353. set bbox [$path.c bbox all]
  1354. if { [llength $bbox] } {
  1355. set data(dnd,xs) [lindex $bbox 2]
  1356. set data(dnd,empty) 0
  1357. } else {
  1358. set data(dnd,xs) 0
  1359. set data(dnd,empty) 1
  1360. }
  1361. set data(dnd,node) {}
  1362. }
  1363. set x [expr {$X-[winfo rootx $path]}]
  1364. set y [expr {$Y-[winfo rooty $path]}]
  1365. $path.c delete drop
  1366. set data(dnd,node) {}
  1367. # test for auto-scroll unless mode is widget only
  1368. if { $data(dnd,mode) != 4 && [_auto_scroll $path $x $y] != "" } {
  1369. return 2
  1370. }
  1371. if { $data(dnd,mode) & 4 } {
  1372. # dropovermode includes widget
  1373. set target [list widget]
  1374. set vmode 4
  1375. } else {
  1376. set target [list ""]
  1377. set vmode 0
  1378. }
  1379. if { ($data(dnd,mode) & 2) && $data(dnd,empty) } {
  1380. # dropovermode includes position and tree is empty
  1381. lappend target [list root 0]
  1382. set vmode [expr {$vmode | 2}]
  1383. }
  1384. set xc [$path.c canvasx $x]
  1385. set xs $data(dnd,xs)
  1386. if { $xc <= $xs } {
  1387. set yc [$path.c canvasy $y]
  1388. set dy [$path.c cget -yscrollincrement]
  1389. set line [expr {int($yc/$dy)}]
  1390. set xi 0
  1391. set yi [expr {$line*$dy}]
  1392. set ys [expr {$yi+$dy}]
  1393. set found 0
  1394. foreach id [$path.c find overlapping $xi $yi $xs $ys] {
  1395. set ltags [$path.c gettags $id]
  1396. set item [lindex $ltags 1]
  1397. if { [string equal $item "node"] ||
  1398. [string equal $item "img"] ||
  1399. [string equal $item "win"] } {
  1400. # item is the label or image/window of the node
  1401. set node [Tree::_get_node_name $path $id 2]
  1402. set found 1
  1403. break
  1404. }
  1405. }
  1406. if {$found} {
  1407. set padx [_get_node_padx $path $node]
  1408. set deltax [_get_node_deltax $path $node]
  1409. set xi [expr {[lindex [$path.c coords n:$node] 0] - $padx - 1}]
  1410. if { $data(dnd,mode) & 1 } {
  1411. # dropovermode includes node
  1412. lappend target $node
  1413. set vmode [expr {$vmode | 1}]
  1414. } else {
  1415. lappend target ""
  1416. }
  1417. if { $data(dnd,mode) & 2 } {
  1418. # dropovermode includes position
  1419. if { $yc >= $yi+$dy/2 } {
  1420. # position is after $node
  1421. if { [Widget::getoption $path.$node -open] &&
  1422. [llength $data($node)] > 1 } {
  1423. # $node is open and have subnodes
  1424. # drop position is 0 in children of $node
  1425. set parent $node
  1426. set index 0
  1427. set xli [expr {$xi-5}]
  1428. } else {
  1429. # $node is not open and doesn't have subnodes
  1430. # drop position is after $node in children of parent of $node
  1431. set parent [lindex $data($node) 0]
  1432. set index [lsearch -exact $data($parent) $node]
  1433. set xli [expr {$xi - $deltax - 5}]
  1434. }
  1435. set yl $ys
  1436. } else {
  1437. # position is before $node
  1438. # drop position is before $node in children of parent of $node
  1439. set parent [lindex $data($node) 0]
  1440. set index [expr {[lsearch -exact $data($parent) $node] - 1}]
  1441. set xli [expr {$xi - $deltax - 5}]
  1442. set yl $yi
  1443. }
  1444. lappend target [list $parent $index]
  1445. set vmode [expr {$vmode | 2}]
  1446. } else {
  1447. lappend target {}
  1448. }
  1449. if { ($vmode & 3) == 3 } {
  1450. # result have both node and position
  1451. # we compute what is the preferred method
  1452. if { $yc-$yi <= 3 || $ys-$yc <= 3 } {
  1453. lappend target "position"
  1454. } else {
  1455. lappend target "node"
  1456. }
  1457. }
  1458. }
  1459. }
  1460. if { $vmode && [set cmd [Widget::getoption $path -dropovercmd]] != "" } {
  1461. # user-defined dropover command
  1462. set res [uplevel \#0 $cmd [list $path $source $target $op $type $dnddata]]
  1463. set code [lindex $res 0]
  1464. set newmode 0
  1465. if { $code & 1 } {
  1466. # update vmode
  1467. set mode [lindex $res 1]
  1468. if { ($vmode & 1) && [string equal $mode "node"] } {
  1469. set newmode 1
  1470. } elseif { ($vmode & 2) && [string equal $mode "position"] } {
  1471. set newmode 2
  1472. } elseif { ($vmode & 4) && [string equal $mode "widget"] } {
  1473. set newmode 4
  1474. }
  1475. }
  1476. set vmode $newmode
  1477. } else {
  1478. if { ($vmode & 3) == 3 } {
  1479. # result have both item and position
  1480. # we choose the preferred method
  1481. if { [string equal [lindex $target 3] "position"] } {
  1482. set vmode [expr {$vmode & ~1}]
  1483. } else {
  1484. set vmode [expr {$vmode & ~2}]
  1485. }
  1486. }
  1487. if { $data(dnd,mode) == 4 || $data(dnd,mode) == 0 } {
  1488. # dropovermode is widget or empty - recall is not necessary
  1489. set code 1
  1490. } else {
  1491. set code 3
  1492. }
  1493. }
  1494. if {!$data(dnd,empty)} {
  1495. # draw dnd visual following vmode
  1496. if { $vmode & 1 } {
  1497. set data(dnd,node) [list "node" [lindex $target 1]]
  1498. $path.c create rectangle $xi $yi $xs $ys -tags drop
  1499. } elseif { $vmode & 2 } {
  1500. set data(dnd,node) [concat "position" [lindex $target 2]]
  1501. $path.c create line $xli [expr {$yl-$dy/2}] $xli $yl $xs $yl -tags drop
  1502. } elseif { $vmode & 4 } {
  1503. set data(dnd,node) [list "widget"]
  1504. } else {
  1505. set code [expr {$code & 2}]
  1506. }
  1507. }
  1508. if { $code & 1 } {
  1509. DropSite::setcursor based_arrow_down
  1510. } else {
  1511. DropSite::setcursor dot
  1512. }
  1513. return $code
  1514. }
  1515. # ----------------------------------------------------------------------------
  1516. # Command Tree::_auto_scroll
  1517. # ----------------------------------------------------------------------------
  1518. proc Tree::_auto_scroll { path x y } {
  1519. variable $path
  1520. upvar 0 $path data
  1521. set xmax [winfo width $path]
  1522. set ymax [winfo height $path]
  1523. set scroll {}
  1524. if { $y <= 6 } {
  1525. if { [lindex [$path.c yview] 0] > 0 } {
  1526. set scroll [list yview -1]
  1527. DropSite::setcursor sb_up_arrow
  1528. }
  1529. } elseif { $y >= $ymax-6 } {
  1530. if { [lindex [$path.c yview] 1] < 1 } {
  1531. set scroll [list yview 1]
  1532. DropSite::setcursor sb_down_arrow
  1533. }
  1534. } elseif { $x <= 6 } {
  1535. if { [lindex [$path.c xview] 0] > 0 } {
  1536. set scroll [list xview -1]
  1537. DropSite::setcursor sb_left_arrow
  1538. }
  1539. } elseif { $x >= $xmax-6 } {
  1540. if { [lindex [$path.c xview] 1] < 1 } {
  1541. set scroll [list xview 1]
  1542. DropSite::setcursor sb_right_arrow
  1543. }
  1544. }
  1545. if { [string length $data(dnd,afterid)] && ![string equal $data(dnd,scroll) $scroll] } {
  1546. after cancel $data(dnd,afterid)
  1547. set data(dnd,afterid) ""
  1548. }
  1549. set data(dnd,scroll) $scroll
  1550. if { [string length $scroll] && ![string length $data(dnd,afterid)] } {
  1551. set data(dnd,afterid) [after 200 Tree::_scroll $path $scroll]
  1552. }
  1553. return $data(dnd,afterid)
  1554. }
  1555. # ----------------------------------------------------------------------------
  1556. # Command Tree::_scroll
  1557. # ----------------------------------------------------------------------------
  1558. proc Tree::_scroll { path cmd dir } {
  1559. variable $path
  1560. upvar 0 $path data
  1561. if { ($dir == -1 && [lindex [$path.c $cmd] 0] > 0) ||
  1562. ($dir == 1 && [lindex [$path.c $cmd] 1] < 1) } {
  1563. $path.c $cmd scroll $dir units
  1564. set data(dnd,afterid) [after 100 Tree::_scroll $path $cmd $dir]
  1565. } else {
  1566. set data(dnd,afterid) ""
  1567. DropSite::setcursor dot
  1568. }
  1569. }
  1570. # Tree::_keynav --
  1571. #
  1572. # Handle navigational keypresses on the tree.
  1573. #
  1574. # Arguments:
  1575. # which tag indicating the direction of motion:
  1576. # up move to the node graphically above current
  1577. # down move to the node graphically below current
  1578. # left close current if open, else move to parent
  1579. # right open current if closed, else move to child
  1580. # open open current if closed, close current if open
  1581. # win name of the tree widget
  1582. #
  1583. # Results:
  1584. # None.
  1585. proc Tree::_keynav {which win} {
  1586. # Keyboard navigation is riddled with special cases. In order to avoid
  1587. # the complex logic, we will instead make a list of all the visible,
  1588. # selectable nodes, then do a simple next or previous operation.
  1589. # One easy way to get all of the visible nodes is to query the canvas
  1590. # object for all the items with the "node" tag; since the tree is always
  1591. # completely redrawn, this list will be in vertical order.
  1592. set nodes {}
  1593. foreach nodeItem [$win.c find withtag node] {
  1594. set node [Tree::_get_node_name $win $nodeItem 2]
  1595. if { [Widget::cget $win.$node -selectable] } {
  1596. lappend nodes $node
  1597. }
  1598. }
  1599. # Keyboard navigation is all relative to the current node
  1600. # surles: Get the current node for single or multiple selection schemas.
  1601. set node [_get_current_node $win]
  1602. switch -exact -- $which {
  1603. "up" {
  1604. # Up goes to the node that is vertically above the current node
  1605. # (NOT necessarily the current node's parent)
  1606. if { [string equal $node ""] } {
  1607. return
  1608. }
  1609. set index [lsearch -exact $nodes $node]
  1610. incr index -1
  1611. if { $index >= 0 } {
  1612. $win selection set [lindex $nodes $index]
  1613. _set_current_node $win [lindex $nodes $index]
  1614. $win see [lindex $nodes $index]
  1615. return
  1616. }
  1617. }
  1618. "down" {
  1619. # Down goes to the node that is vertically below the current node
  1620. if { [string equal $node ""] } {
  1621. $win selection set [lindex $nodes 0]
  1622. _set_current_node $win [lindex $nodes 0]
  1623. $win see [lindex $nodes 0]
  1624. return
  1625. }
  1626. set index [lsearch -exact $nodes $node]
  1627. incr index
  1628. if { $index < [llength $nodes] } {
  1629. $win selection set [lindex $nodes $index]
  1630. _set_current_node $win [lindex $nodes $index]
  1631. $win see [lindex $nodes $index]
  1632. return
  1633. }
  1634. }
  1635. "right" {
  1636. # On a right arrow, if the current node is closed, open it.
  1637. # If the current node is open, go to its first child
  1638. if { [string equal $node ""] } {
  1639. return
  1640. }
  1641. set open [$win itemcget $node -open]
  1642. if { $open } {
  1643. if { [llength [$win nodes $node]] } {
  1644. set index [lsearch -exact $nodes $node]
  1645. incr index
  1646. if { $index < [llength $nodes] } {
  1647. $win selection set [lindex $nodes $index]
  1648. _set_current_node $win [lindex $nodes $index]
  1649. $win see [lindex $nodes $index]
  1650. return
  1651. }
  1652. }
  1653. } else {
  1654. $win itemconfigure $node -open 1
  1655. if { [set cmd [Widget::getoption $win -opencmd]] != "" } {
  1656. uplevel \#0 $cmd [list $node]
  1657. }
  1658. return
  1659. }
  1660. }
  1661. "left" {
  1662. # On a left arrow, if the current node is open, close it.
  1663. # If the current node is closed, go to its parent.
  1664. if { [string equal $node ""] } {
  1665. return
  1666. }
  1667. set open [$win itemcget $node -open]
  1668. if { $open } {
  1669. $win itemconfigure $node -open 0
  1670. if { [set cmd [Widget::getoption $win -closecmd]] != "" } {
  1671. uplevel \#0 $cmd [list $node]
  1672. }
  1673. return
  1674. } else {
  1675. set parent [$win parent $node]
  1676. if { [string equal $parent "root"] } {
  1677. set parent $node
  1678. } else {
  1679. while { ![$win itemcget $parent -selectable] } {
  1680. set parent [$win parent $parent]
  1681. if { [string equal $parent "root"] } {
  1682. set parent $node
  1683. break
  1684. }
  1685. }
  1686. }
  1687. $win selection set $parent
  1688. _set_current_node $win $parent
  1689. $win see $parent
  1690. return
  1691. }
  1692. }
  1693. "space" {
  1694. if { [string equal $node ""] } {
  1695. return
  1696. }
  1697. set open [$win itemcget $node -open]
  1698. if { [llength [$win nodes $node]] } {
  1699. # Toggle the open status of the chosen node.
  1700. $win itemconfigure $node -open [expr {$open?0:1}]
  1701. if {$open} {
  1702. # Node was open, is now closed. Call the close-cmd
  1703. if { [set cmd [Widget::getoption $win -closecmd]] != "" } {
  1704. uplevel \#0 $cmd [list $node]
  1705. }
  1706. } else {
  1707. # Node was closed, is now open. Call the open-cmd
  1708. if { [set cmd [Widget::getoption $win -opencmd]] != "" } {
  1709. uplevel \#0 $cmd [list $node]
  1710. }
  1711. }
  1712. }
  1713. }
  1714. }
  1715. return
  1716. }
  1717. # Tree::_get_current_node --
  1718. #
  1719. # Get the current node for either single or multiple
  1720. # node selection trees. If the tree allows for
  1721. # multiple selection, return the cursor node. Otherwise,
  1722. # if there is a selection, return the first node in the
  1723. # list. If there is no selection, return the root node.
  1724. #
  1725. # arguments:
  1726. # win name of the tree widget
  1727. #
  1728. # Results:
  1729. # The current node.
  1730. proc Tree::_get_current_node {win} {
  1731. if {[info exists selectTree::selectCursor($win)]} {
  1732. set result $selectTree::selectCursor($win)
  1733. } elseif {[set selList [$win selection get]] != {}} {
  1734. set result [lindex $selList 0]
  1735. } else {
  1736. set result ""
  1737. }
  1738. return $result
  1739. }
  1740. # Tree::_set_current_node --
  1741. #
  1742. # Set the current node for either single or multiple
  1743. # node selection trees.
  1744. #
  1745. # arguments:
  1746. # win Name of the tree widget
  1747. # node The current node.
  1748. #
  1749. # Results:
  1750. # None.
  1751. proc Tree::_set_current_node {win node} {
  1752. if {[info exists selectTree::selectCursor($win)]} {
  1753. set selectTree::selectCursor($win) $node
  1754. }
  1755. return
  1756. }
  1757. # Tree::_get_node_name --
  1758. #
  1759. # Given a canvas item, get the name of the tree node represented by that
  1760. # item.
  1761. #
  1762. # Arguments:
  1763. # path tree to query
  1764. # item Optional canvas item to examine; if omitted,
  1765. # defaults to "current"
  1766. # tagindex Optional tag index, since the n:nodename tag is not
  1767. # in the same spot for all canvas items. If omitted,
  1768. # defaults to "end-1", so it works with "current" item.
  1769. #
  1770. # Results:
  1771. # node name of the tree node.
  1772. proc Tree::_get_node_name {path {item current} {tagindex end-1}} {
  1773. return [string range [lindex [$path.c gettags $item] $tagindex] 2 end]
  1774. }
  1775. # Tree::_get_node_padx --
  1776. #
  1777. # Given a node in the tree, return it's padx value. If the value is
  1778. # less than 0, default to the padx of the entire tree.
  1779. #
  1780. # Arguments:
  1781. # path Tree to query
  1782. # node Node in the tree
  1783. #
  1784. # Results:
  1785. # padx The numeric padx value
  1786. proc Tree::_get_node_padx {path node} {
  1787. set padx [Widget::getoption $path.$node -padx]
  1788. if {$padx < 0} { set padx [Widget::getoption $path -padx] }
  1789. return $padx
  1790. }
  1791. # Tree::_get_node_deltax --
  1792. #
  1793. # Given a node in the tree, return it's deltax value. If the value is
  1794. # less than 0, default to the deltax of the entire tree.
  1795. #
  1796. # Arguments:
  1797. # path Tree to query
  1798. # node Node in the tree
  1799. #
  1800. # Results:
  1801. # deltax The numeric deltax value
  1802. proc Tree::_get_node_deltax {path node} {
  1803. set deltax [Widget::getoption $path.$node -deltax]
  1804. if {$deltax < 0} { set deltax [Widget::getoption $path -deltax] }
  1805. return $deltax
  1806. }
  1807. # Tree::_get_node_tags --
  1808. #
  1809. # Given a node in the tree, return a list of tags to apply to its
  1810. # canvas item.
  1811. #
  1812. # Arguments:
  1813. # path Tree to query
  1814. # node Node in the tree
  1815. # tags A list of tags to add to the final list
  1816. #
  1817. # Results:
  1818. # list The list of tags to apply to the canvas item
  1819. proc Tree::_get_node_tags {path node {tags ""}} {
  1820. eval [list lappend list TreeItemSentinal] $tags
  1821. if {[Widget::getoption $path.$node -helptext] == ""} { return $list }
  1822. switch -- [Widget::getoption $path.$node -helptype] {
  1823. balloon {
  1824. lappend list BwHelpBalloon
  1825. }
  1826. variable {
  1827. lappend list BwHelpVariable
  1828. }
  1829. }
  1830. return $list
  1831. }
  1832. # Tree::_set_help --
  1833. #
  1834. # Register dynamic help for a node in the tree.
  1835. #
  1836. # Arguments:
  1837. # path Tree to query
  1838. # node Node in the tree
  1839. # force Optional argument to force a reset of the help
  1840. #
  1841. # Results:
  1842. # none
  1843. proc Tree::_set_help { path node } {
  1844. Widget::getVariable $path help
  1845. set item $path.$node
  1846. set opts [list -helptype -helptext -helpvar]
  1847. foreach {cty ctx cv} [eval [list Widget::hasChangedX $item] $opts] break
  1848. set text [Widget::getoption $item -helptext]
  1849. ## If we've never set help for this item before, and text is not blank,
  1850. ## we need to setup help. We also need to reset help if any of the
  1851. ## options have changed.
  1852. if { (![info exists help($node)] && $text != "") || $cty || $ctx || $cv } {
  1853. set help($node) 1
  1854. set type [Widget::getoption $item -helptype]
  1855. switch $type {
  1856. balloon {
  1857. DynamicHelp::register $path.c balloon n:$node $text
  1858. DynamicHelp::register $path.c balloon i:$node $text
  1859. DynamicHelp::register $path.c balloon b:$node $text
  1860. }
  1861. variable {
  1862. set var [Widget::getoption $item -helpvar]
  1863. DynamicHelp::register $path.c variable n:$node $var $text
  1864. DynamicHelp::register $path.c variable i:$node $var $text
  1865. DynamicHelp::register $path.c variable b:$node $var $text
  1866. }
  1867. }
  1868. }
  1869. }