PageRenderTime 53ms CodeModel.GetById 5ms RepoModel.GetById 0ms app.codeStats 0ms

/tcl/BWidget-1.9.0/tree.tcl

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