spinbox.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # ------------------------------------------------------------------------------
  2. # spinbox.tcl
  3. # This file is part of Unifix BWidget Toolkit
  4. # ------------------------------------------------------------------------------
  5. # Index of commands:
  6. # - SpinBox::create
  7. # - SpinBox::configure
  8. # - SpinBox::cget
  9. # - SpinBox::setvalue
  10. # - SpinBox::_destroy
  11. # - SpinBox::_modify_value
  12. # - SpinBox::_test_options
  13. # ------------------------------------------------------------------------------
  14. namespace eval SpinBox {
  15. ArrowButton::use
  16. Entry::use
  17. LabelFrame::use
  18. Widget::bwinclude SpinBox LabelFrame .labf \
  19. rename {-text -label} \
  20. prefix {label -justify -width -anchor -height -font} \
  21. remove {-focus} \
  22. initialize {-relief sunken -borderwidth 2}
  23. Widget::bwinclude SpinBox Entry .e \
  24. remove {-relief -bd -borderwidth -fg -bg} \
  25. rename {-foreground -entryfg -background -entrybg}
  26. Widget::declare SpinBox {
  27. {-range String "" 0}
  28. {-values String "" 0}
  29. {-modifycmd String "" 0}
  30. {-repeatdelay Int 400 0 {=0}}
  31. {-repeatinterval Int 100 0 {=0}}
  32. }
  33. Widget::addmap SpinBox "" :cmd {-background {}}
  34. Widget::addmap SpinBox ArrowButton .arrup {
  35. -foreground {} -background {} -disabledforeground {} -state {}
  36. -repeatdelay {} -repeatinterval {}
  37. }
  38. Widget::addmap SpinBox ArrowButton .arrdn {
  39. -foreground {} -background {} -disabledforeground {} -state {}
  40. -repeatdelay {} -repeatinterval {}
  41. }
  42. Widget::syncoptions SpinBox Entry .e {-text {}}
  43. Widget::syncoptions SpinBox LabelFrame .labf {-label -text -underline {}}
  44. ::bind BwSpinBox <FocusIn> {focus %W.labf}
  45. ::bind BwSpinBox <Destroy> {SpinBox::_destroy %W}
  46. proc ::SpinBox { path args } { return [eval SpinBox::create $path $args] }
  47. proc use {} {}
  48. variable _widget
  49. }
  50. # ------------------------------------------------------------------------------
  51. # Command SpinBox::create
  52. # ------------------------------------------------------------------------------
  53. proc SpinBox::create { path args } {
  54. variable _widget
  55. Widget::init SpinBox $path $args
  56. _test_options $path
  57. eval frame $path [Widget::subcget $path :cmd] \
  58. -highlightthickness 0 -bd 0 -relief flat -takefocus 0
  59. set labf [eval LabelFrame::create $path.labf [Widget::subcget $path .labf] \
  60. -borderwidth 2 -relief sunken -focus $path.e]
  61. set entry [eval Entry::create $path.e [Widget::subcget $path .e] \
  62. -relief flat -borderwidth 0]
  63. bindtags $path [list $path BwSpinBox [winfo toplevel $path] all]
  64. set farr [frame $path.farr -relief flat -bd 0 -highlightthickness 0]
  65. set height [expr {[winfo reqheight $path.e]/2-2}]
  66. set width 11
  67. set arrup [eval ArrowButton::create $path.arrup -dir top \
  68. [Widget::subcget $path .arrup] \
  69. -highlightthickness 0 -borderwidth 1 -takefocus 0 \
  70. -type button \
  71. -width $width -height $height \
  72. -armcommand [list "SpinBox::_modify_value $path next arm"] \
  73. -disarmcommand [list "SpinBox::_modify_value $path next disarm"]]
  74. set arrdn [eval ArrowButton::create $path.arrdn -dir bottom \
  75. [Widget::subcget $path .arrdn] \
  76. -highlightthickness 0 -borderwidth 1 -takefocus 0 \
  77. -type button \
  78. -width $width -height $height \
  79. -armcommand [list "SpinBox::_modify_value $path previous arm"] \
  80. -disarmcommand [list "SpinBox::_modify_value $path previous disarm"]]
  81. set frame [LabelFrame::getframe $path.labf]
  82. # --- update -value ---
  83. if { [set val [Entry::cget $path.e -text]] != "" } {
  84. set _widget($path,curval) $val
  85. } else {
  86. if { [set var [Widget::getoption $path -textvariable]] != "" } {
  87. GlobalVar::setvar $var $_widget($path,curval)
  88. } else {
  89. Entry::configure $path.e -text $_widget($path,curval)
  90. }
  91. }
  92. Widget::setoption $path -text $_widget($path,curval)
  93. grid $arrup -in $farr -column 0 -row 0 -sticky nsew
  94. grid $arrdn -in $farr -column 0 -row 2 -sticky nsew
  95. grid rowconfigure $farr 0 -weight 1
  96. grid rowconfigure $farr 2 -weight 1
  97. pack $farr -in $frame -side right -fill y
  98. pack $entry -in $frame -side left -fill both -expand yes
  99. pack $labf -fill both -expand yes
  100. ::bind $entry <Key-Up> "SpinBox::_modify_value $path next activate"
  101. ::bind $entry <Key-Down> "SpinBox::_modify_value $path previous activate"
  102. ::bind $entry <Key-Prior> "SpinBox::_modify_value $path last activate"
  103. ::bind $entry <Key-Next> "SpinBox::_modify_value $path first activate"
  104. ::bind $farr <Configure> {grid rowconfigure %W 1 -minsize [expr {%h%%2}]}
  105. rename $path ::$path:cmd
  106. proc ::$path { cmd args } "return \[eval SpinBox::\$cmd $path \$args\]"
  107. return $path
  108. }
  109. # ------------------------------------------------------------------------------
  110. # Command SpinBox::configure
  111. # ------------------------------------------------------------------------------
  112. proc SpinBox::configure { path args } {
  113. set res [Widget::configure $path $args]
  114. if { [Widget::hasChanged $path -values val] ||
  115. [Widget::hasChanged $path -range val] } {
  116. _test_options $path
  117. }
  118. return $res
  119. }
  120. # ------------------------------------------------------------------------------
  121. # Command SpinBox::cget
  122. # ------------------------------------------------------------------------------
  123. proc SpinBox::cget { path option } {
  124. return [Widget::cget $path $option]
  125. }
  126. # ------------------------------------------------------------------------------
  127. # Command SpinBox::setvalue
  128. # ------------------------------------------------------------------------------
  129. proc SpinBox::setvalue { path index } {
  130. variable _widget
  131. set values [Widget::getoption $path -values]
  132. set value [Entry::cget $path.e -text]
  133. if { [llength $values] } {
  134. # --- -values SpinBox ---
  135. switch -- $index {
  136. next {
  137. if { [set idx [lsearch $values $value]] != -1 } {
  138. incr idx
  139. } elseif { [set idx [lsearch $values "$value*"]] == -1 } {
  140. set idx [lsearch $values $_widget($path,curval)]
  141. }
  142. }
  143. previous {
  144. if { [set idx [lsearch $values $value]] != -1 } {
  145. incr idx -1
  146. } elseif { [set idx [lsearch $values "$value*"]] == -1 } {
  147. set idx [lsearch $values $_widget($path,curval)]
  148. }
  149. }
  150. first {
  151. set idx 0
  152. }
  153. last {
  154. set idx [expr {[llength $values]-1}]
  155. }
  156. default {
  157. if { [string index $index 0] == "@" } {
  158. set idx [string range $index 1 end]
  159. if { [catch {string compare [expr {int($idx)}] $idx} res] || $res != 0 } {
  160. return -code error "bad index \"$index\""
  161. }
  162. } else {
  163. return -code error "bad index \"$index\""
  164. }
  165. }
  166. }
  167. if { $idx >= 0 && $idx < [llength $values] } {
  168. set newval [lindex $values $idx]
  169. } else {
  170. return 0
  171. }
  172. } else {
  173. # --- -range SpinBox ---
  174. set range [Widget::getoption $path -range]
  175. set vmin [lindex $range 0]
  176. set vmax [lindex $range 1]
  177. set incr [lindex $range 2]
  178. switch -- $index {
  179. next {
  180. if { [catch {expr {double($value-$vmin)/$incr}} idx] } {
  181. set newval $_widget($path,curval)
  182. } else {
  183. set newval [expr {$vmin+(round($idx)+1)*$incr}]
  184. if { $newval < $vmin } {
  185. set newval $vmin
  186. } elseif { $newval > $vmax } {
  187. set newval $vmax
  188. }
  189. }
  190. }
  191. previous {
  192. if { [catch {expr {double($value-$vmin)/$incr}} idx] } {
  193. set newval $_widget($path,curval)
  194. } else {
  195. set newval [expr {$vmin+(round($idx)-1)*$incr}]
  196. if { $newval < $vmin } {
  197. set newval $vmin
  198. } elseif { $newval > $vmax } {
  199. set newval $vmax
  200. }
  201. }
  202. }
  203. first {
  204. set newval $vmin
  205. }
  206. last {
  207. set newval $vmax
  208. }
  209. default {
  210. if { [string index $index 0] == "@" } {
  211. set idx [string range $index 1 end]
  212. if { [catch {string compare [expr {int($idx)}] $idx} res] || $res != 0 } {
  213. return -code error "bad index \"$index\""
  214. }
  215. set newval [expr {$vmin+int($idx)*$incr}]
  216. if { $newval < $vmin || $newval > $vmax } {
  217. return 0
  218. }
  219. } else {
  220. return -code error "bad index \"$index\""
  221. }
  222. }
  223. }
  224. }
  225. set _widget($path,curval) $newval
  226. Widget::setoption $path -text $newval
  227. if { [set varname [Entry::cget $path.e -textvariable]] != "" } {
  228. GlobalVar::setvar $varname $newval
  229. } else {
  230. Entry::configure $path.e -text $newval
  231. }
  232. return 1
  233. }
  234. # ------------------------------------------------------------------------------
  235. # Command SpinBox::getvalue
  236. # ------------------------------------------------------------------------------
  237. proc SpinBox::getvalue { path } {
  238. variable _widget
  239. set values [Widget::getoption $path -values]
  240. set value [Entry::cget $path.e -text]
  241. if { [llength $values] } {
  242. # --- -values SpinBox ---
  243. return [lsearch $values $value]
  244. } else {
  245. set range [Widget::getoption $path -range]
  246. set vmin [lindex $range 0]
  247. set vmax [lindex $range 1]
  248. set incr [lindex $range 2]
  249. if { ![catch {expr {double($value-$vmin)/$incr}} idx] &&
  250. $idx == int($idx) } {
  251. return [expr {int($idx)}]
  252. }
  253. return -1
  254. }
  255. }
  256. # ------------------------------------------------------------------------------
  257. # Command SpinBox::bind
  258. # ------------------------------------------------------------------------------
  259. proc SpinBox::bind { path args } {
  260. return [eval ::bind $path.e $args]
  261. }
  262. # ------------------------------------------------------------------------------
  263. # Command SpinBox::_destroy
  264. # ------------------------------------------------------------------------------
  265. proc SpinBox::_destroy { path } {
  266. variable _widget
  267. unset _widget($path,curval)
  268. Widget::destroy $path
  269. rename $path {}
  270. }
  271. # ------------------------------------------------------------------------------
  272. # Command SpinBox::_modify_value
  273. # ------------------------------------------------------------------------------
  274. proc SpinBox::_modify_value { path direction reason } {
  275. if { $reason == "arm" || $reason == "activate" } {
  276. SpinBox::setvalue $path $direction
  277. }
  278. if { ($reason == "disarm" || $reason == "activate") &&
  279. [set cmd [Widget::getoption $path -modifycmd]] != "" } {
  280. uplevel \#0 $cmd
  281. }
  282. }
  283. # ------------------------------------------------------------------------------
  284. # Command SpinBox::_test_options
  285. # ------------------------------------------------------------------------------
  286. proc SpinBox::_test_options { path } {
  287. variable _widget
  288. set values [Widget::getoption $path -values]
  289. if { [llength $values] } {
  290. set _widget($path,curval) [lindex $values 0]
  291. } else {
  292. set range [Widget::getoption $path -range]
  293. set vmin [lindex $range 0]
  294. set vmax [lindex $range 1]
  295. set incr [lindex $range 2]
  296. if { [catch {expr {int($vmin)}}] } {
  297. set vmin 0
  298. }
  299. if { [catch {expr {$vmax<$vmin}} res] || $res } {
  300. set vmax $vmin
  301. }
  302. if { [catch {expr {$incr<0}} res] || $res } {
  303. set incr 1
  304. }
  305. Widget::setoption $path -range [list $vmin $vmax $incr]
  306. set _widget($path,curval) $vmin
  307. }
  308. }