1 # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4
4 # onselect(newvalue) - New value selected
7 inherit tlc
::Border tlc
::Signalsource tlc
::Textvariable tlc
::Handlers
13 variable state
"normal"
16 variable value
"" seekvalue
17 variable pagelength
5 rebindpage
18 variable zoom_memory
2000 ;# in ms
21 method moveto
{sel_idx
}
22 method next
{{delta
1}}
23 method prev
{{delta
1}}
24 method last
{{offset
0}}
25 method first
{{offset
0}}
28 method set_choice
{newidx args
}
37 method textvariable_changed
{newvalue
}
45 variable choices_init
1
51 common downimg_refcount
54 method validate_input
{newval
}
55 method scrollwheel
{delta
}
59 method toggle_popup
{}
60 method statechanged
{newstate
}
61 method choices_set_changed
{newstate
}
66 configbody tlc
::Spinner::value { #<<<1
67 if {$value == "" && [info exists oldvalue
]} {
75 configbody tlc
::Spinner::choices { #<<<1
79 puts stderr
"choices: ([join $choices |]) value: ($value)"
80 $signals(choices_set
) set_state
1
86 configbody tlc
::Spinner::state { #<<<1
87 log debug
"state: ($state)"
88 [stategate_ref
] configure
-default [expr {$state == "normal"}]
92 body tlc
::Spinner::constructor {args
} { #<<<1
95 tlc
::Signal #auto signals(valid) -name "$w valid"
96 tlc
::Signal #auto signals(choices_set) -name "$w choices_set"
98 $signals(choices_set
) attach_output
[code
$this choices_set_changed
]
100 # Initialize bitmaps <<<
101 if {![info exists upimg
]} {
102 set upimg
[image create
bitmap \
103 -file [file join $::tlc::library scripts images spinup.xbm
] \
104 -foreground $tlc::config(enabledforeground
)]
110 if {![info exists downimg
]} {
111 set downimg
[image create
bitmap \
112 -file [file join $::tlc::library scripts images spindown.xbm
] \
113 -foreground $tlc::config(enabledforeground
)]
114 set downimg_refcount
1
116 incr downimg_refcount
118 # Initialize bitmaps >>>
121 $itk_component(border
) configure
\
122 -highlightthickness 1 \
124 -highlightcolor black
125 tlc
::StateToggle #auto toggles(border) $itk_component(border) \
127 $toggles(border
) attach_signal
[stategate_ref
]
128 bind $itk_component(border
).inner
<FocusIn
> \
129 [code
focus $itk_component(border
)]
133 itk_component add
entry {
134 entry $w.
entry -borderwidth 0 -highlightthickness 0 -state disabled
\
135 -validate all
-validatecommand [code
$this validate_input
%P
]
138 rename -background -textbackground textBackground TextBackground
140 set enabled_fg
[$tlc::theme setting foreground
]
141 set enabled_bg
[$tlc::theme setting textbackground
]
142 set disabled_fg
[$tlc::theme setting disabledforeground
]
143 set disabled_bg
[$tlc::theme setting disabledbackground
]
144 tlc
::StateToggle #auto toggles(entry) $itk_component(entry) \
145 -foreground [list $disabled_fg $enabled_fg] \
146 -background [list $disabled_bg $enabled_bg] \
147 -disabledforeground [list $disabled_fg $enabled_fg] \
148 -disabledbackground [list $disabled_bg $enabled_bg]
149 $toggles(entry) attach_signal
[stategate_ref
]
153 option add
*$wdb.Button.takeFocus
0
154 option add
*$wdb.Button.borderWidth
1
155 option add
*$wdb.Button.padX
2
156 option add
*$wdb.Button.width
10
157 option add
*$wdb.Button.padY
0
158 option add
*$wdb.Button.highlightThickness
0
160 button $w.up
-image $upimg -command [code
$this prev
]
161 tlc
::StateToggle #auto toggles(up) $w.up \
162 -state {disabled normal
}
163 $toggles(up
) attach_input
[stategate_ref
]
165 button $w.down
-image $downimg -command [code
$this next
]
166 tlc
::StateToggle #auto toggles(down) $w.down \
167 -state {disabled normal
}
168 $toggles(down
) attach_input
[stategate_ref
]
172 itk_component add popup
{
173 tlc
::Comboselect $w.
#auto -parent $this
175 keep
-maxheight -textbackground
176 rename -listwidth -width width Width
178 set popup_obj
$itk_component(popup
)
182 $w.
entry 1,1 -rspan 2 -fill both
\
185 blt
::table configure
$w c2
-resize none
187 eval itk_initialize
$args
189 set baselog_instancename
$w
191 set listend
[expr {[llength $choices] - 1}]
192 if {$listend < 0} {set listend
0}
194 bind $itk_component(entry) <Button-4
> [code
$this binding move
-1]
195 bind $itk_component(entry) <Button-5
> [code
$this binding move
1]
196 bind $itk_component(entry) <MouseWheel
> [code
$this binding scrollwheel
%D
]
197 #bind $itk_component(entry) <Button-1> [code focus $w]
198 bind $itk_component(entry) <Button-1
> [code
$this toggle_popup
]
199 bind $itk_component(entry) <Button-3
> [code
$this toggle_popup
]
200 bind $itk_component(border
) <Key-Up
> [code
$this binding move
-1]
201 bind $itk_component(border
) <Key-Down
> [code
$this binding move
1]
202 bind $itk_component(border
) <Key-Home
> [code
$this binding moveto
0]
203 bind $itk_component(border
) <Key-End
> [code
$this binding moveto
$listend]
204 bind $itk_component(border
) <Key
> [code
$this zoom
%A
]
206 [stategate_ref
] attach_output
[code
$this statechanged
]
210 body tlc
::Spinner::destructor {} { #<<<1
211 incr upimg_refcount
-1
212 incr downimg_refcount
-1
213 if {$upimg_refcount == 0} {
218 if {$downimg_refcount == 0} {
219 image delete
$downimg
221 unset downimg_refcount
226 body tlc
::Spinner::move {delta
} { #<<<1
227 set sel_idx
[lsearch $choices [$w.
entry get
]]
229 set choices_len
[llength $choices]
231 if {$sel_idx == -1} {
233 set sel_idx
[expr {$choices_len + $delta}]
234 if {$sel_idx == [llength $choices]} {
235 set sel_idx
[expr {$choices_len - 1}]
238 set sel_idx
[expr {$delta - 1}]
244 set sel_idx
[expr {$sel_idx + $delta}]
248 if {$choices_len == 0} {
251 set sel_idx
[expr {$sel_idx % $choices_len}]
254 if {$sel_idx > [last_idx
]} {
255 set sel_idx
[last_idx
]
256 } elseif
{$sel_idx < 0} {
265 body tlc
::Spinner::moveto {sel_idx
} { #<<<1
266 if {![[stategate_ref
] state
]} return
267 if {$sel_idx < 0 ||
$sel_idx > [last_idx
]} {
268 error "Spinner choice out of bounds: $sel_idx (max is [last_idx])"
276 body tlc
::Spinner::next {{delta
1}} { #<<<1
281 body tlc
::Spinner::prev {{delta
1}} { #<<<1
282 move
[expr {0 - $delta}]
286 body tlc
::Spinner::last {{offset
0}} { #<<<1
287 moveto
[expr {[last_idx
] + $offset}]
291 body tlc
::Spinner::first {{offset
0}} { #<<<1
296 body tlc
::Spinner::last_idx {} { #<<<1
297 return [expr {[llength $choices] - 1}]
301 body tlc
::Spinner::update_view {} { #<<<1
302 set new
[lindex $choices $idx]
304 set hold
[$itk_component(entry) cget
-state]
305 $itk_component(entry) configure
-state normal
306 $itk_component(entry) delete
0 end
307 $itk_component(entry) insert
0 $new
308 $itk_component(entry) configure
-state $hold
310 set_textvariable
$new
311 invoke_handlers onselect
$new
315 body tlc
::Spinner::seekvalue {} { #<<<1
316 if {![$signals(choices_set
) state
]} return
317 log debug
"looking for ($value) in ([join $choices |])"
318 set sel_idx
[lsearch $choices $value]
319 if {$sel_idx != -1} {
326 body tlc
::Spinner::rebindpage {} { #<<<1
327 bind $itk_component(border
) <Key-Prior
> [code
$this binding move
-$pagelength]
328 bind $itk_component(border
) <Key-Next
> [code
$this binding move
$pagelength]
332 body tlc
::Spinner::reset {} { #<<<1
333 log debug
"choices are: ([join $choices |]) value is: ($value)"
334 set tidx
[lsearch $choices [$itk_component(entry) get
]]
336 $itk_component(entry) delete
0 end
339 set listend
[expr {[llength $choices] - 1}]
340 if {$listend < 0} {set listend
0}
342 bind $itk_component(entry) <Key-End
> [code
$this binding moveto
$listend]
344 $popup_obj set_choices
[lrange $choices 0 end
]
350 body tlc
::Spinner::validate_input {newval
} { #<<<1
351 set exists
[expr {[lsearch $choices $newval] != -1}]
352 $signals(valid
) set_state
$exists
354 return [expr {$exists ||
$newval == ""}]
358 body tlc
::Spinner::textvariable_changed {newvalue
} { #<<<1
359 log debug
"choices set? [$signals(choices_set) state] choices: ([join $choices |])"
361 if {$newvalue == ""} {
362 set hold
[$itk_component(entry) cget
-state]
363 $itk_component(entry) configure
-state normal
364 $itk_component(entry) delete
0 end
365 $itk_component(entry) insert
0 ""
366 $itk_component(entry) configure
-state $hold
368 if {[$signals(choices_set
) state
]} {
369 if {[lsearch $choices $newvalue] == -1} {
370 error "Invalid value" "" [list reject_change
]
374 configure
-value $newvalue
379 body tlc
::Spinner::scrollwheel {delta
} { #<<<1
388 body tlc
::Spinner::zoom {char
} { #<<<1
389 set now
[clock clicks
-milliseconds]
390 if {![info exists last_zoom_ms
] ||
$now - $last_zoom_ms > $zoom_memory} {
393 append zoom_spec
$char
395 set last_zoom_ms
$now
397 set zoom_spec
[string tolower
$zoom_spec]
399 set zoom_last
[string length
$zoom_spec]
403 foreach choice
$choices {
406 [string tolower
[string range
$choice 0 $zoom_last]] == $zoom_spec
413 if {[string length
$zoom_spec] > 1} {
414 # Try instead with this char only
421 body tlc
::Spinner::binding {args
} { #<<<1
422 log debug
"Binding: $args"
424 eval [code
$this] $args
428 body tlc
::Spinner::popup {} { #<<<1
429 if {![[stategate_ref
] state
]} return
431 set topx
[winfo rootx
$w.up
]
432 set topy
[winfo rooty
$w.up
]
434 set screenheight
[winfo screenheight
$w]
435 set screenwidth
[winfo screenwidth
$w]
439 foreach {id x y wd ht
} [table extents
$w c2
] break
441 set botx
[expr $topx + $wd]
442 set boty
[expr $topy + $ht]
444 $popup_obj set_choices
[lrange $choices 0 end
]
446 set listwidth
[winfo reqwidth
$popup_obj]
447 set listheight
[winfo reqheight
$popup_obj]
448 wm overrideredirect
$popup_obj 1
449 wm positionfrom
$popup_obj user
451 set planx
[expr {$botx - $listwidth}]
452 set plany
[expr {$boty + 1}]
454 # Avoid going off the screen <<<
455 if {$planx < 0} {set planx
0}
456 if {$plany < 0} {set plany
0}
457 if {$planx + $listwidth > $screenwidth} {
458 set planx
[expr {$screenwidth - $listwidth}]
460 if {$plany + $listheight > $screenheight} {
461 set plany
[expr {$screenheight - $listheight}]
463 # Avoid going off the screen >>>
465 $popup_obj moveto
$planx $plany
468 focus [$popup_obj component
list]
470 bind $w <Button
> [code
$this popdown
]
471 bind $w <Key
> [code
$this popdown
]
475 body tlc
::Spinner::popdown {} { #<<<1
476 if {[winfo ismapped
$popup_obj]} {
480 focus $itk_component(border
)
485 body tlc
::Spinner::toggle_popup {} { #<<<1
486 if {[winfo ismapped
$popup_obj]} {
494 body tlc
::Spinner::set_choice {newidx args
} { #<<<1
501 body tlc
::Spinner::statechanged {newstate
} { #<<<1
509 body tlc
::Spinner::choices_set_changed {newstate
} { #<<<1