select.tcl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. ##########################################################################
  2. #
  3. # select.tcl
  4. #
  5. # tree/listbox control for interactive selection of GRASS GIS elements
  6. #
  7. # Author: Unknown. Possibly Jacques Bouchard, author of tcltkgrass for
  8. # GRASS 5. Subsequent modifications by members of the GRASS Development
  9. # team.
  10. #
  11. # Last update: September 2007
  12. #
  13. # COPYRIGHT: (C) 1999 - 2007 by the GRASS Development Team
  14. #
  15. # This program is free software under the GNU General Public
  16. # License (>=v2). Read the file COPYING that comes with GRASS
  17. # for details.
  18. #
  19. ##########################################################################
  20. # Frame scrolling that works:
  21. # Scroll if the window exists AND
  22. # the window is mapped AND
  23. # This window's parent's descendant has the focus (keyboard or mouse pointer in)
  24. # We use the parent because the scrollbars are in the parent, and two scrollable
  25. # Things shouldn't have the same parent.
  26. set bind_scroll_list {}
  27. proc handle_scroll {ammount} {
  28. global bind_scroll_list
  29. foreach {x y} {-1 -1} {}
  30. set window_gone 0
  31. foreach window $bind_scroll_list {
  32. if {![winfo exists $window]} {
  33. set window_gone 1
  34. continue
  35. }
  36. if {![winfo ismapped $window]} continue
  37. set parent [winfo parent $window]
  38. set keyboard_focus [focus -displayof $window]
  39. foreach {x y} [winfo pointerxy $window] {break}
  40. set mouse_focus [winfo containing -displayof $window $x $y]
  41. set l [string length $parent]
  42. if {[string equal -length $l $parent $keyboard_focus] || \
  43. [string equal -length $l $parent $mouse_focus]} {
  44. $window yview scroll [expr {-$ammount/120}] units
  45. }
  46. }
  47. # We should thin out windows that don't exist anymore if we find them
  48. if {$window_gone} {
  49. set new_bind_scroll_list {}
  50. foreach window $bind_scroll_list {
  51. if {[winfo exists $window]} {
  52. lappend new_bind_scroll_list $window
  53. }
  54. }
  55. set bind_scroll_list $new_bind_scroll_list
  56. }
  57. }
  58. proc bind_scroll {frame} {
  59. global bind_scroll_list
  60. lappend bind_scroll_list $frame
  61. }
  62. bind all <MouseWheel> "handle_scroll %D"
  63. bind all <Button-4> "handle_scroll 120"
  64. bind all <Button-5> "handle_scroll -120"
  65. ##############################################################
  66. proc GSelect { element args } {
  67. # startup procedure
  68. set sel [eval [linsert $args 0 GSelect_::create $element]]
  69. return $sel
  70. }
  71. namespace eval GSelect_ {
  72. variable count 1
  73. variable dblclick
  74. variable array selwin
  75. }
  76. proc GSelect_::create { element args } {
  77. # main procedure for creating and managing selection window, which a tree
  78. # within a scrolling window.
  79. global env id
  80. variable selwin
  81. variable count
  82. incr count
  83. set id $count
  84. set selwin($id,self) selwin
  85. set title [G_msg "Select item"]
  86. set selwin($id,selected) {}
  87. if {[lsearch -exact $args "title"] > -1} {
  88. append title " - [lindex $args [expr [lsearch -exact $args title]+1]]"
  89. }
  90. # Leave selection on top of caller window till it's closed
  91. set parentwin "."
  92. if {[lsearch -exact $args "parent"] > -1} {
  93. set parentwin [lindex $args [expr [lsearch -exact $args "parent"]+1]]
  94. if { [string length $parentwin] > 1 } {
  95. set selwin($id,self) [regsub -all {[[:space:]]|[[:punct:]]} ".selwin[string range $parentwin 1 [string length $parentwin]]" ""]
  96. } elseif {[lsearch -exact $args "title"] > -1} { set selwin($id,self) [regsub -all {[[:space:]]|[[:punct:]]} ".selwin$title" ""] }
  97. }
  98. set selwin($id,self) ".$selwin($id,self)"
  99. set selftop "$selwin($id,self)top"
  100. # Do not create another select window, if one already exists.
  101. if {[winfo exists $selwin($id,self)]} {
  102. raise $selwin($id,self)
  103. focus $selwin($id,self)
  104. return
  105. }
  106. toplevel $selwin($id,self) -width 300 -height 400
  107. set sw [ScrolledWindow $selwin($id,self).sw -relief sunken -borderwidth 2 ]
  108. wm title $selwin($id,self) $title
  109. wm transient $selwin($id,self) $parentwin
  110. set tree [Tree $sw.tree \
  111. -relief flat -borderwidth 0 -width 15 -highlightthickness 0\
  112. -redraw 1 -dropenabled 1 -dragenabled 1 \
  113. -opencmd "GSelect_::moddir 1 $sw.tree" \
  114. -closecmd "GSelect_::moddir 0 $sw.tree"]
  115. $sw setwidget $tree
  116. bind_scroll $tree
  117. regexp -- {(.+)x(.+)([+-].+)([+-].+)} [wm geometry .] g w h x y
  118. #set w [expr int(2*$w/3)]
  119. set w 300
  120. set h 400
  121. wm geometry $selwin($id,self) ${w}x$h$x$y
  122. pack $sw -side top -expand yes -fill both
  123. pack $tree -side top -expand yes -fill both
  124. $tree bindText <ButtonPress-1> "GSelect_::select $id $tree"
  125. $tree bindImage <ButtonPress-1> "GSelect_::select $id $tree"
  126. $tree bindText <Double-ButtonPress-1> "GSelect_::selectclose $id $tree"
  127. $tree bindImage <Double-ButtonPress-1> "GSelect_::selectclose $id $tree"
  128. if {[lsearch $args "multiple"] >= 0} {
  129. $tree bindText <Control-ButtonPress-1> "GSelect_::select_toggle $id $tree"
  130. } else {
  131. $tree bindText <Control-ButtonPress-1> "GSelect_::select $id $tree"
  132. }
  133. set location_path "$env(GISDBASE)/$env(LOCATION_NAME)/"
  134. set current_mapset "$env(MAPSET)"
  135. set sympath "$env(GISBASE)/etc/symbol/"
  136. # main selection subroutine
  137. if {$element != "symbol"} {
  138. foreach dir [exec g.mapsets -p] {
  139. set windfile "$location_path/$dir/WIND"
  140. if { ! [ file exists $windfile ] } { continue }
  141. if { $dir == $current_mapset } {
  142. $tree insert end root ms_$dir -text $dir -data $dir -open 1 \
  143. -image [Bitmap::get openfold] -drawcross auto
  144. } else {
  145. $tree insert end root ms_$dir -text $dir -data $dir -open 0 \
  146. -image [Bitmap::get folder] -drawcross auto
  147. }
  148. set path "$location_path/$dir/$element/"
  149. foreach fp [ lsort [glob -nocomplain $path/*] ] {
  150. set file [file tail $fp]
  151. $tree insert end ms_$dir $file@$dir -text $file -data $file \
  152. -image [Bitmap::get file] -drawcross never
  153. }
  154. }
  155. }
  156. # vector symbol selection subroutine
  157. if {$element == "symbol"} {
  158. $tree insert end root ms_$sympath -text SYMBOLS -data $sympath -open 1 \
  159. -image [Bitmap::get openfold] -drawcross auto
  160. foreach ic_dir [ lsort [glob -nocomplain $sympath/*] ] {
  161. set dir_tail [file tail $ic_dir]
  162. $tree insert end ms_$sympath ms_$dir_tail -text $dir_tail -data $dir_tail \
  163. -image [Bitmap::get folder] -drawcross auto
  164. foreach ic_file [ lsort [glob -nocomplain $sympath/$dir_tail/*] ] {
  165. set file [file tail $ic_file]
  166. $tree insert end ms_$dir_tail $dir_tail/$file -text $file -data $file \
  167. -image [Bitmap::get file] -drawcross never
  168. }
  169. }
  170. }
  171. $tree configure -redraw 1
  172. # buttons
  173. button $selwin($id,self).ok -text [G_msg "Ok"] -command "destroy $selwin($id,self)"
  174. button $selwin($id,self).cancel -text [G_msg "Cancel"] -command "GSelect_::terminate $id"
  175. pack $selwin($id,self).ok $selwin($id,self).cancel -side left -expand yes
  176. # ScrollView
  177. toplevel $selftop -relief raised -borderwidth 2
  178. wm protocol $selftop WM_DELETE_WINDOW {
  179. # don't kill me
  180. }
  181. wm overrideredirect $selftop 1
  182. wm withdraw $selftop
  183. wm transient $selftop $selwin($id,self)
  184. ScrollView $selftop.sv -window $tree -fill black
  185. pack $selftop.sv -fill both -expand yes
  186. wm protocol $selwin($id,self) WM_DELETE_WINDOW "GSelect_::terminate $id"
  187. tkwait window $selwin($id,self)
  188. destroy $selftop
  189. # return selected elements -- separated by commas if there are > 1 elements
  190. if { $selwin($id,selected) != "" } {
  191. set ret ""
  192. set len [llength $selwin($id,selected)]
  193. foreach elem $selwin($id,selected) {
  194. append ret $elem
  195. if {[lsearch $selwin($id,selected) $elem] != -1 && \
  196. [lsearch $selwin($id,selected) $elem] < [expr $len-1]} {
  197. append ret ","
  198. }
  199. }
  200. return $ret
  201. }
  202. return ""
  203. }
  204. proc GSelect_::select { id tree node } {
  205. # Single selection (default). Clicking an item will select it and
  206. # deselect any other item selected
  207. variable selwin
  208. set parent [$tree parent $node]
  209. if { $parent == "root" } { return }
  210. $tree selection set $node
  211. update
  212. set selwin($id,selected) $node
  213. }
  214. proc GSelect_::select_toggle { id tree node} {
  215. # Multiple selections. Ctrl-1 will toggle an item as selected or not selected
  216. # and add it to a list of selected items
  217. variable selwin
  218. set parent [$tree parent $node]
  219. if { $parent == "root" } { return }
  220. if {[lsearch -exact [$tree selection get] $node] >= 0} {
  221. $tree selection remove $node
  222. update
  223. set nodeindex [lsearch $selwin($id,selected) $node]
  224. if {$nodeindex != -1} {
  225. set selwin($id,selected) [lreplace $selwin($id,selected) $nodeindex $nodeindex]
  226. }
  227. } else {
  228. $tree selection add $node
  229. update
  230. lappend selwin($id,selected) $node
  231. }
  232. #$tree selection add $node
  233. # set selwin($id,selected) [string trim $selwin($id,selected) ,]
  234. }
  235. proc GSelect_::selectclose { id tree node } {
  236. # return selection and close window (OK button)
  237. variable selwin
  238. GSelect_::select $id $tree $node
  239. destroy $selwin($id,self)
  240. }
  241. proc GSelect_::terminate { id } {
  242. # close window without returning selection (cancel)
  243. variable selwin
  244. set selwin($id,selected) {}
  245. destroy $selwin($id,self)
  246. }
  247. proc GSelect_::moddir { idx tree node } {
  248. if { $idx && [$tree itemcget $node -drawcross] == "always" } {
  249. getdir $tree $node [$tree itemcget $node -data]
  250. if { [llength [$tree nodes $node]] } {
  251. $tree itemconfigure $node -image [Bitmap::get openfold]
  252. } else {
  253. $tree itemconfigure $node -image [Bitmap::get folder]
  254. }
  255. } else {
  256. $tree itemconfigure $node -image [Bitmap::get [lindex {folder openfold} $idx]]
  257. }
  258. }