combobox.tcl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. # ------------------------------------------------------------------------------
  2. # combobox.tcl
  3. # This file is part of Unifix BWidget Toolkit
  4. # $Id$
  5. # ------------------------------------------------------------------------------
  6. # Index of commands:
  7. # - ComboBox::create
  8. # - ComboBox::configure
  9. # - ComboBox::cget
  10. # - ComboBox::setvalue
  11. # - ComboBox::getvalue
  12. # - ComboBox::_create_popup
  13. # - ComboBox::_mapliste
  14. # - ComboBox::_unmapliste
  15. # - ComboBox::_select
  16. # - ComboBox::_modify_value
  17. # ------------------------------------------------------------------------------
  18. namespace eval ComboBox {
  19. ArrowButton::use
  20. Entry::use
  21. LabelFrame::use
  22. Widget::bwinclude ComboBox LabelFrame .labf \
  23. rename {-text -label} \
  24. remove {-focus} \
  25. prefix {label -justify -width -anchor -height -font} \
  26. initialize {-relief sunken -borderwidth 2}
  27. Widget::bwinclude ComboBox Entry .e \
  28. remove {-relief -bd -borderwidth -bg -fg} \
  29. rename {-foreground -entryfg -background -entrybg}
  30. Widget::declare ComboBox {
  31. {-height TkResource 0 0 listbox}
  32. {-values String "" 0}
  33. {-modifycmd String "" 0}
  34. {-postcommand String "" 0}
  35. }
  36. Widget::addmap ComboBox "" :cmd {-background {}}
  37. Widget::addmap ComboBox ArrowButton .a \
  38. {-foreground {} -background {} -disabledforeground {} -state {}}
  39. Widget::syncoptions ComboBox Entry .e {-text {}}
  40. Widget::syncoptions ComboBox LabelFrame .labf {-label -text -underline {}}
  41. ::bind BwComboBox <FocusIn> {focus %W.labf}
  42. ::bind BwComboBox <Destroy> {Widget::destroy %W; rename %W {}}
  43. proc ::ComboBox { path args } { return [eval ComboBox::create $path $args] }
  44. proc use {} {}
  45. }
  46. # ------------------------------------------------------------------------------
  47. # Command ComboBox::create
  48. # ------------------------------------------------------------------------------
  49. proc ComboBox::create { path args } {
  50. Widget::init ComboBox $path $args
  51. frame $path -background [Widget::getoption $path -background] \
  52. -highlightthickness 0 -bd 0 -relief flat -takefocus 0
  53. bindtags $path [list $path BwComboBox [winfo toplevel $path] all]
  54. set labf [eval LabelFrame::create $path.labf [Widget::subcget $path .labf] \
  55. -focus $path.e]
  56. set entry [eval Entry::create $path.e [Widget::subcget $path .e] \
  57. -relief flat -borderwidth 0]
  58. set width 11
  59. set height [winfo reqheight $entry]
  60. set arrow [eval ArrowButton::create $path.a [Widget::subcget $path .a] \
  61. -width $width -height $height \
  62. -highlightthickness 0 -borderwidth 1 -takefocus 0 \
  63. -dir bottom \
  64. -type button \
  65. -command [list "ComboBox::_mapliste $path"]]
  66. set frame [LabelFrame::getframe $labf]
  67. pack $arrow -in $frame -side right -fill y
  68. pack $entry -in $frame -side left -fill both -expand yes
  69. pack $labf -fill x -expand yes
  70. if { [Widget::getoption $path -editable] == 0 } {
  71. ::bind $entry <ButtonPress-1> "ArrowButton::invoke $path.a"
  72. } else {
  73. ::bind $entry <ButtonPress-1> "ComboBox::_unmapliste $path"
  74. }
  75. ::bind $path <ButtonPress-1> "ComboBox::_unmapliste $path"
  76. ::bind $entry <Key-Up> "ComboBox::_modify_value $path previous"
  77. ::bind $entry <Key-Down> "ComboBox::_modify_value $path next"
  78. ::bind $entry <Key-Prior> "ComboBox::_modify_value $path first"
  79. ::bind $entry <Key-Next> "ComboBox::_modify_value $path last"
  80. rename $path ::$path:cmd
  81. proc ::$path { cmd args } "return \[eval ComboBox::\$cmd $path \$args\]"
  82. return $path
  83. }
  84. # ------------------------------------------------------------------------------
  85. # Command ComboBox::configure
  86. # ------------------------------------------------------------------------------
  87. proc ComboBox::configure { path args } {
  88. set res [Widget::configure $path $args]
  89. if { [Widget::hasChanged $path -values values] |
  90. [Widget::hasChanged $path -height h] |
  91. [Widget::hasChanged $path -font f] } {
  92. destroy $path.shell.listb
  93. }
  94. if { [Widget::hasChanged $path -editable ed] } {
  95. if { $ed } {
  96. ::bind $path.e <ButtonPress-1> "ComboBox::_unmapliste $path"
  97. } else {
  98. ::bind $path.e <ButtonPress-1> "ArrowButton::invoke $path.a"
  99. }
  100. }
  101. return $res
  102. }
  103. # ------------------------------------------------------------------------------
  104. # Command ComboBox::cget
  105. # ------------------------------------------------------------------------------
  106. proc ComboBox::cget { path option } {
  107. Widget::setoption $path -text [Entry::cget $path.e -text]
  108. return [Widget::cget $path $option]
  109. }
  110. # ------------------------------------------------------------------------------
  111. # Command ComboBox::setvalue
  112. # ------------------------------------------------------------------------------
  113. proc ComboBox::setvalue { path index } {
  114. set values [Widget::getoption $path -values]
  115. set value [Entry::cget $path.e -text]
  116. switch -- $index {
  117. next {
  118. if { [set idx [lsearch $values $value]] != -1 } {
  119. incr idx
  120. } else {
  121. set idx [lsearch $values "$value*"]
  122. }
  123. }
  124. previous {
  125. if { [set idx [lsearch $values $value]] != -1 } {
  126. incr idx -1
  127. } else {
  128. set idx [lsearch $values "$value*"]
  129. }
  130. }
  131. first {
  132. set idx 0
  133. }
  134. last {
  135. set idx [expr {[llength $values]-1}]
  136. }
  137. default {
  138. if { [string index $index 0] == "@" } {
  139. set idx [string range $index 1 end]
  140. if { [catch {string compare [expr {int($idx)}] $idx} res] || $res != 0 } {
  141. return -code error "bad index \"$index\""
  142. }
  143. } else {
  144. return -code error "bad index \"$index\""
  145. }
  146. }
  147. }
  148. if { $idx >= 0 && $idx < [llength $values] } {
  149. set newval [lindex $values $idx]
  150. Widget::setoption $path -text $newval
  151. if { [set varname [Entry::cget $path.e -textvariable]] != "" } {
  152. GlobalVar::setvar $varname $newval
  153. } else {
  154. Entry::configure $path.e -text $newval
  155. }
  156. return 1
  157. }
  158. return 0
  159. }
  160. # ------------------------------------------------------------------------------
  161. # Command ComboBox::getvalue
  162. # ------------------------------------------------------------------------------
  163. proc ComboBox::getvalue { path } {
  164. set values [Widget::getoption $path -values]
  165. set value [Entry::cget $path.e -text]
  166. return [lsearch $values $value]
  167. }
  168. # ------------------------------------------------------------------------------
  169. # Command ComboBox::bind
  170. # ------------------------------------------------------------------------------
  171. proc ComboBox::bind { path args } {
  172. return [eval ::bind $path.e $args]
  173. }
  174. # ------------------------------------------------------------------------------
  175. # Command ComboBox::_create_popup
  176. # ------------------------------------------------------------------------------
  177. proc ComboBox::_create_popup { path } {
  178. set shell [menu $path.shell -tearoff 0 -relief flat -bd 0]
  179. wm overrideredirect $shell 1
  180. wm withdraw $shell
  181. wm transient $shell [winfo toplevel $path]
  182. wm group $shell [winfo toplevel $path]
  183. set lval [Widget::getoption $path -values]
  184. set h [Widget::getoption $path -height]
  185. set sb 0
  186. if { $h <= 0 } {
  187. set len [llength $lval]
  188. if { $len < 3 } {
  189. set h 3
  190. } elseif { $len > 10 } {
  191. set h 10
  192. set sb 1
  193. }
  194. }
  195. set frame [frame $shell.frame -relief sunken -bd 2]
  196. set listb [listbox $shell.listb -relief flat -bd 0 -highlightthickness 0 \
  197. -exportselection false \
  198. -font [Widget::getoption $path -font] \
  199. -height $h]
  200. if { $sb } {
  201. set scroll [scrollbar $shell.scroll \
  202. -orient vertical \
  203. -command "$shell.listb yview" \
  204. -highlightthickness 0 -takefocus 0 -width 9]
  205. $listb configure -yscrollcommand "$scroll set"
  206. }
  207. $listb delete 0 end
  208. foreach val $lval {
  209. $listb insert end $val
  210. }
  211. if { $sb } {
  212. pack $scroll -in $frame -side right -fill y
  213. }
  214. pack $listb -in $frame -side left -fill both -expand yes
  215. pack $frame -fill both -expand yes -padx 1 -padx 1
  216. ::bind $listb <ButtonRelease-1> "ComboBox::_select $path @%x,%y"
  217. ::bind $listb <Return> "ComboBox::_select $path active"
  218. ::bind $listb <Escape> "ComboBox::_unmapliste $path"
  219. }
  220. # ------------------------------------------------------------------------------
  221. # Command ComboBox::_mapliste
  222. # ------------------------------------------------------------------------------
  223. proc ComboBox::_mapliste { path } {
  224. set listb $path.shell.listb
  225. if { [winfo exists $path.shell] } {
  226. _unmapliste $path
  227. return
  228. }
  229. if { [Widget::getoption $path -state] == "disabled" } {
  230. return
  231. }
  232. if { [set cmd [Widget::getoption $path -postcommand]] != "" } {
  233. uplevel \#0 $cmd
  234. }
  235. if { ![llength [Widget::getoption $path -values]] } {
  236. return
  237. }
  238. _create_popup $path
  239. ArrowButton::configure $path.a -dir top
  240. $listb selection clear 0 end
  241. set values [$listb get 0 end]
  242. set curval [Entry::cget $path.e -text]
  243. if { [set idx [lsearch $values $curval]] != -1 ||
  244. [set idx [lsearch $values "$curval*"]] != -1 } {
  245. $listb selection set $idx
  246. $listb activate $idx
  247. $listb see $idx
  248. } else {
  249. $listb activate 0
  250. $listb see 0
  251. }
  252. set frame [LabelFrame::getframe $path.labf]
  253. BWidget::place $path.shell [winfo width $frame] 0 below $frame
  254. wm deiconify $path.shell
  255. raise $path.shell
  256. BWidget::grab global $path
  257. }
  258. # ------------------------------------------------------------------------------
  259. # Command ComboBox::_unmapliste
  260. # ------------------------------------------------------------------------------
  261. proc ComboBox::_unmapliste { path } {
  262. BWidget::grab release $path
  263. destroy $path.shell
  264. ArrowButton::configure $path.a -dir bottom
  265. }
  266. # ------------------------------------------------------------------------------
  267. # Command ComboBox::_select
  268. # ------------------------------------------------------------------------------
  269. proc ComboBox::_select { path index } {
  270. set index [$path.shell.listb index $index]
  271. _unmapliste $path
  272. if { $index != -1 } {
  273. if { [setvalue $path @$index] } {
  274. if { [set cmd [Widget::getoption $path -modifycmd]] != "" } {
  275. uplevel \#0 $cmd
  276. }
  277. }
  278. }
  279. return -code break
  280. }
  281. # ------------------------------------------------------------------------------
  282. # Command ComboBox::_modify_value
  283. # ------------------------------------------------------------------------------
  284. proc ComboBox::_modify_value { path direction } {
  285. if { [setvalue $path $direction] } {
  286. if { [set cmd [Widget::getoption $path -modifycmd]] != "" } {
  287. uplevel \#0 $cmd
  288. }
  289. }
  290. }