Control.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. # -*- mode: TCL; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*-
  2. #
  3. # $Id: Control.tcl,v 1.9 2004/03/28 02:44:57 hobbs Exp $
  4. #
  5. # Control.tcl --
  6. #
  7. # Implements the TixControl Widget. It is called the "SpinBox"
  8. # in other toolkits.
  9. #
  10. # Copyright (c) 1993-1999 Ioi Kim Lam.
  11. # Copyright (c) 2000-2001 Tix Project Group.
  12. # Copyright (c) 2004 ActiveState
  13. #
  14. # See the file "license.terms" for information on usage and redistribution
  15. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  16. #
  17. tixWidgetClass tixControl {
  18. -classname TixControl
  19. -superclass tixLabelWidget
  20. -method {
  21. incr decr invoke update
  22. }
  23. -flag {
  24. -allowempty -autorepeat -command -decrcmd -disablecallback
  25. -disabledforeground -incrcmd -initwait -integer -llimit
  26. -repeatrate -max -min -selectmode -step -state -validatecmd
  27. -value -variable -ulimit
  28. }
  29. -forcecall {
  30. -variable -state
  31. }
  32. -configspec {
  33. {-allowempty allowEmpty AllowEmpty false}
  34. {-autorepeat autoRepeat AutoRepeat true}
  35. {-command command Command ""}
  36. {-decrcmd decrCmd DecrCmd ""}
  37. {-disablecallback disableCallback DisableCallback 0 tixVerifyBoolean}
  38. {-disabledforeground disabledForeground DisabledForeground #303030}
  39. {-incrcmd incrCmd IncrCmd ""}
  40. {-initwait initWait InitWait 500}
  41. {-integer integer Integer false}
  42. {-max max Max ""}
  43. {-min min Min ""}
  44. {-repeatrate repeatRate RepeatRate 50}
  45. {-step step Step 1}
  46. {-state state State normal}
  47. {-selectmode selectMode SelectMode normal}
  48. {-validatecmd validateCmd ValidateCmd ""}
  49. {-value value Value 0}
  50. {-variable variable Variable ""}
  51. }
  52. -alias {
  53. {-llimit -min}
  54. {-ulimit -max}
  55. }
  56. -default {
  57. {.borderWidth 0}
  58. {*entry.relief sunken}
  59. {*entry.width 5}
  60. {*label.anchor e}
  61. {*label.borderWidth 0}
  62. {*Button.anchor c}
  63. {*Button.borderWidth 2}
  64. {*Button.highlightThickness 1}
  65. {*Button.takeFocus 0}
  66. }
  67. }
  68. proc tixControl:InitWidgetRec {w} {
  69. upvar #0 $w data
  70. tixChainMethod $w InitWidgetRec
  71. set data(varInited) 0
  72. set data(serial) 0
  73. }
  74. proc tixControl:ConstructFramedWidget {w frame} {
  75. upvar #0 $w data
  76. tixChainMethod $w ConstructFramedWidget $frame
  77. set data(w:entry) [entry $frame.entry]
  78. set data(w:incr) \
  79. [button $frame.incr -bitmap [tix getbitmap incr] -takefocus 0]
  80. set data(w:decr) \
  81. [button $frame.decr -bitmap [tix getbitmap decr] -takefocus 0]
  82. # tixForm $data(w:entry) -left 0 -top 0 -bottom -1 -right $data(w:decr)
  83. # tixForm $data(w:incr) -right -1 -top 0 -bottom %50
  84. # tixForm $data(w:decr) -right -1 -top $data(w:incr) -bottom -1
  85. pack $data(w:entry) -side left -expand yes -fill both
  86. pack $data(w:decr) -side bottom -fill both -expand yes
  87. pack $data(w:incr) -side top -fill both -expand yes
  88. $data(w:entry) delete 0 end
  89. $data(w:entry) insert 0 $data(-value)
  90. # This value is used to configure the disable/normal fg of the ebtry
  91. set data(entryfg) [$data(w:entry) cget -fg]
  92. set data(labelfg) [$data(w:label) cget -fg]
  93. }
  94. proc tixControl:SetBindings {w} {
  95. upvar #0 $w data
  96. tixChainMethod $w SetBindings
  97. bind $data(w:incr) <ButtonPress-1> \
  98. [list after idle tixControl:StartRepeat $w 1]
  99. bind $data(w:decr) <ButtonPress-1> \
  100. [list after idle tixControl:StartRepeat $w -1]
  101. # These bindings will stop the button autorepeat when the
  102. # mouse button is up
  103. foreach btn [list $data(w:incr) $data(w:decr)] {
  104. bind $btn <ButtonRelease-1> [list tixControl:StopRepeat $w]
  105. }
  106. tixSetMegaWidget $data(w:entry) $w
  107. # If user press <return>, verify the value and call the -command
  108. #
  109. tixAddBindTag $data(w:entry) TixControl:Entry
  110. }
  111. proc tixControlBind {} {
  112. tixBind TixControl:Entry <Return> {
  113. tixControl:Invoke [tixGetMegaWidget %W] 1
  114. }
  115. tixBind TixControl:Entry <Escape> {
  116. tixControl:Escape [tixGetMegaWidget %W]
  117. }
  118. tixBind TixControl:Entry <Up> {
  119. [tixGetMegaWidget %W] incr
  120. }
  121. tixBind TixControl:Entry <Down> {
  122. [tixGetMegaWidget %W] decr
  123. }
  124. tixBind TixControl:Entry <FocusOut> {
  125. if {"%d" eq "NotifyNonlinear" || "%d" eq "NotifyNonlinearVirtual"} {
  126. tixControl:Tab [tixGetMegaWidget %W] %d
  127. }
  128. }
  129. tixBind TixControl:Entry <Any-KeyPress> {
  130. tixControl:KeyPress [tixGetMegaWidget %W]
  131. }
  132. tixBind TixControl:Entry <Any-Tab> {
  133. # This has a higher priority than the <Any-KeyPress> binding
  134. # --> so that data(edited) is not set
  135. }
  136. }
  137. #----------------------------------------------------------------------
  138. # CONFIG OPTIONS
  139. #----------------------------------------------------------------------
  140. proc tixControl:config-state {w arg} {
  141. upvar #0 $w data
  142. if {$arg eq "normal"} {
  143. $data(w:incr) config -state $arg
  144. $data(w:decr) config -state $arg
  145. catch {
  146. $data(w:label) config -fg $data(labelfg)
  147. }
  148. $data(w:entry) config -state $arg -fg $data(entryfg)
  149. } else {
  150. $data(w:incr) config -state $arg
  151. $data(w:decr) config -state $arg
  152. catch {
  153. $data(w:label) config -fg $data(-disabledforeground)
  154. }
  155. $data(w:entry) config -state $arg -fg $data(-disabledforeground)
  156. }
  157. }
  158. proc tixControl:config-value {w value} {
  159. upvar #0 $w data
  160. tixControl:SetValue $w $value 0 1
  161. # This will tell the Intrinsics: "Please use this value"
  162. # because "value" might be altered by SetValues
  163. #
  164. return $data(-value)
  165. }
  166. proc tixControl:config-variable {w arg} {
  167. upvar #0 $w data
  168. if {[tixVariable:ConfigVariable $w $arg]} {
  169. # The value of data(-value) is changed if tixVariable:ConfigVariable
  170. # returns true
  171. tixControl:SetValue $w $data(-value) 1 1
  172. }
  173. catch {
  174. unset data(varInited)
  175. }
  176. set data(-variable) $arg
  177. }
  178. #----------------------------------------------------------------------
  179. # User Commands
  180. #----------------------------------------------------------------------
  181. proc tixControl:incr {w {by 1}} {
  182. upvar #0 $w data
  183. if {$data(-state) ne "disabled"} {
  184. if {![catch {$data(w:entry) index sel.first}]} {
  185. $data(w:entry) select from end
  186. $data(w:entry) select to end
  187. }
  188. # CYGNUS - why set value before changing it?
  189. #tixControl:SetValue $w [$data(w:entry) get] 0 1
  190. tixControl:AdjustValue $w $by
  191. }
  192. }
  193. proc tixControl:decr {w {by 1}} {
  194. upvar #0 $w data
  195. if {$data(-state) ne "disabled"} {
  196. if {![catch {$data(w:entry) index sel.first}]} {
  197. $data(w:entry) select from end
  198. $data(w:entry) select to end
  199. }
  200. # CYGNUS - why set value before changing it?
  201. #tixControl:SetValue $w [$data(w:entry) get] 0 1
  202. tixControl:AdjustValue $w [expr {0 - $by}]
  203. }
  204. }
  205. proc tixControl:invoke {w} {
  206. upvar #0 $w data
  207. tixControl:Invoke $w 0
  208. }
  209. proc tixControl:update {w} {
  210. upvar #0 $w data
  211. if {[info exists data(edited)]} {
  212. tixControl:invoke $w
  213. }
  214. }
  215. #----------------------------------------------------------------------
  216. # Internal Commands
  217. #----------------------------------------------------------------------
  218. # Change the value by a multiple of the data(-step)
  219. #
  220. proc tixControl:AdjustValue {w amount} {
  221. upvar #0 $w data
  222. if {$amount == 1 && [llength $data(-incrcmd)]} {
  223. set newValue [tixEvalCmdBinding $w $data(-incrcmd) "" $data(-value)]
  224. } elseif {$amount == -1 && [llength $data(-decrcmd)]} {
  225. set newValue [tixEvalCmdBinding $w $data(-decrcmd) "" $data(-value)]
  226. } else {
  227. set newValue [expr {$data(-value) + $amount * $data(-step)}]
  228. }
  229. if {$data(-state) ne "disabled"} {
  230. tixControl:SetValue $w $newValue 0 1
  231. }
  232. }
  233. proc tixControl:SetValue {w newvalue noUpdate forced} {
  234. upvar #0 $w data
  235. if {[$data(w:entry) selection present]} {
  236. set oldSelection [list [$data(w:entry) index sel.first] \
  237. [$data(w:entry) index sel.last]]
  238. }
  239. set oldvalue $data(-value)
  240. set oldCursor [$data(w:entry) index insert]
  241. set changed 0
  242. if {[llength $data(-validatecmd)]} {
  243. # Call the user supplied validation command
  244. #
  245. set data(-value) [tixEvalCmdBinding $w $data(-validatecmd) "" $newvalue]
  246. } else {
  247. # Here we only allow int or floating numbers
  248. #
  249. # If the new value is not a valid number, the old value will be
  250. # kept due to the "catch" statements
  251. #
  252. if {[catch {expr 0+$newvalue}]} {
  253. set newvalue 0
  254. set data(-value) 0
  255. set changed 1
  256. }
  257. if {$newvalue == ""} {
  258. if {![string is true -strict $data(-allowempty)]} {
  259. set newvalue 0
  260. set changed 1
  261. } else {
  262. set data(-value) ""
  263. }
  264. }
  265. if {$newvalue != ""} {
  266. # Change this to a valid decimal string (trim leading 0)
  267. #
  268. regsub -- {^[0]*} $newvalue "" newvalue
  269. if {[catch {expr 0+$newvalue}]} {
  270. set newvalue 0
  271. set data(-value) 0
  272. set changed 1
  273. }
  274. if {$newvalue == ""} {
  275. set newvalue 0
  276. }
  277. if {[string is true -strict $data(-integer)]} {
  278. set data(-value) [tixGetInt -nocomplain $newvalue]
  279. } else {
  280. if {[catch {set data(-value) [format "%d" $newvalue]}]} {
  281. if {[catch {set data(-value) [expr $newvalue+0.0]}]} {
  282. set data(-value) $oldvalue
  283. }
  284. }
  285. }
  286. # Now perform boundary checking
  287. #
  288. if {$data(-max) != "" && $data(-value) > $data(-max)} {
  289. set data(-value) $data(-max)
  290. }
  291. if {$data(-min) != "" && $data(-value) < $data(-min)} {
  292. set data(-value) $data(-min)
  293. }
  294. }
  295. }
  296. if {! $noUpdate} {
  297. tixVariable:UpdateVariable $w
  298. }
  299. if {$forced || ($newvalue ne $data(-value)) || $changed} {
  300. $data(w:entry) delete 0 end
  301. $data(w:entry) insert 0 $data(-value)
  302. $data(w:entry) icursor $oldCursor
  303. if {[info exists oldSelection]} {
  304. eval [list $data(w:entry) selection range] $oldSelection
  305. }
  306. }
  307. if {!$data(-disablecallback) && $data(-command) != ""} {
  308. if {![info exists data(varInited)]} {
  309. set bind(specs) ""
  310. tixEvalCmdBinding $w $data(-command) bind $data(-value)
  311. }
  312. }
  313. }
  314. proc tixControl:Invoke {w forced} {
  315. upvar #0 $w data
  316. catch {
  317. unset data(edited)
  318. }
  319. if {[catch {$data(w:entry) index sel.first}] == 0} {
  320. # THIS ENTRY OWNS SELECTION --> TURN IT OFF
  321. #
  322. $data(w:entry) select from end
  323. $data(w:entry) select to end
  324. }
  325. tixControl:SetValue $w [$data(w:entry) get] 0 $forced
  326. }
  327. #----------------------------------------------------------------------
  328. # The three functions StartRepeat, Repeat and StopRepeat make use of the
  329. # data(serial) variable to discard spurious repeats: If a button is clicked
  330. # repeatedly but is not hold down, the serial counter will increase
  331. # successively and all "after" time event handlers will be discarded
  332. #----------------------------------------------------------------------
  333. proc tixControl:StartRepeat {w amount} {
  334. if {![winfo exists $w]} {
  335. return
  336. }
  337. upvar #0 $w data
  338. incr data(serial)
  339. # CYGNUS bug fix
  340. # Need to set a local variable because otherwise the buttonrelease
  341. # callback could change the value of data(serial) between now and
  342. # the time the repeat is scheduled.
  343. set serial $data(serial)
  344. if {![catch {$data(w:entry) index sel.first}]} {
  345. $data(w:entry) select from end
  346. $data(w:entry) select to end
  347. }
  348. if {[info exists data(edited)]} {
  349. unset data(edited)
  350. tixControl:SetValue $w [$data(w:entry) get] 0 1
  351. }
  352. tixControl:AdjustValue $w $amount
  353. if {$data(-autorepeat)} {
  354. after $data(-initwait) tixControl:Repeat $w $amount $serial
  355. }
  356. focus $data(w:entry)
  357. }
  358. proc tixControl:Repeat {w amount serial} {
  359. if {![winfo exists $w]} {
  360. return
  361. }
  362. upvar #0 $w data
  363. if {$serial eq $data(serial)} {
  364. tixControl:AdjustValue $w $amount
  365. if {$data(-autorepeat)} {
  366. after $data(-repeatrate) tixControl:Repeat $w $amount $serial
  367. }
  368. }
  369. }
  370. proc tixControl:StopRepeat {w} {
  371. upvar #0 $w data
  372. incr data(serial)
  373. }
  374. proc tixControl:Destructor {w} {
  375. tixVariable:DeleteVariable $w
  376. # Chain this to the superclass
  377. #
  378. tixChainMethod $w Destructor
  379. }
  380. # ToDo: maybe should return -code break if the value is not good ...
  381. #
  382. proc tixControl:Tab {w detail} {
  383. upvar #0 $w data
  384. if {![info exists data(edited)]} {
  385. return
  386. } else {
  387. unset data(edited)
  388. }
  389. tixControl:invoke $w
  390. }
  391. proc tixControl:Escape {w} {
  392. upvar #0 $w data
  393. $data(w:entry) delete 0 end
  394. $data(w:entry) insert 0 $data(-value)
  395. }
  396. proc tixControl:KeyPress {w} {
  397. upvar #0 $w data
  398. if {$data(-selectmode) eq "normal"} {
  399. set data(edited) 0
  400. return
  401. } else {
  402. # == "immediate"
  403. after 1 tixControl:invoke $w
  404. }
  405. }