File: spinent.tcl

package info (click to toggle)
moomps 4.6-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 2,444 kB
  • ctags: 2,307
  • sloc: tcl: 34,882; sh: 167; makefile: 91
file content (201 lines) | stat: -rw-r--r-- 9,453 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# $Id: spinent.tcl,v 2.6 2003/08/25 09:28:55 jfontain Exp $


class spinEntry {}

proc spinEntry::spinEntry {this parentPath args} composite {
    [new frame $parentPath -highlightthickness $widget::option(button,highlightthickness)] $args
} {
    ::set path $widget::($this,path)
    # prevent the arrow buttons from ever getting the focus
    composite::manage $this [new entry $path -highlightthickness 0] entry\
        [new arrowButton $path\
            -takefocus 0 -command "spinEntry::decrease $this" -height 4 -highlightthickness 0\
            -repeatdelay $widget::option(scrollbar,repeatdelay)\
        ] decrease\
        [new arrowButton $path\
            -direction up -takefocus 0 -command "spinEntry::increase $this" -height 4 -highlightthickness 0\
            -repeatdelay $widget::option(scrollbar,repeatdelay)\
        ] increase

    # the following bindings get activated when either the main button or the entry get the focus
    bind $path <Return> "spinEntry::invoke $this"
    bind $path <KP_Enter> "spinEntry::invoke $this"
    bind $composite::($this,entry,path) <Return> "spinEntry::invoke $this"
    bind $composite::($this,entry,path) <KP_Enter> "spinEntry::invoke $this"

    # either entry or widget will get the focus so setup key bindings for both of them
    spinEntry::setupUpAndDownKeysBindings $this $path
    spinEntry::setupUpAndDownKeysBindings $this $composite::($this,entry,path)

    composite::complete $this
}

proc spinEntry::~spinEntry {this} {}

proc spinEntry::options {this} {                                            ;# force initialization on font, side and  state options
    return [list\
        [list -command {} {}]\
        [list -editable 1 1]\
        [list -font $widget::option(button,font)]\
        [list -justify $widget::option(entry,justify) $widget::option(entry,justify)]\
        [list -list {} {}]\
        [list -range {} {}]\
        [list -repeatdelay $widget::option(scrollbar,repeatdelay) $widget::option(scrollbar,repeatdelay)]\
        [list -side left]\
        [list -state normal]\
        [list -width $widget::option(entry,width) $widget::option(entry,width)]\
        [list -wrap 0 0]\
    ]
}

proc spinEntry::set-command {this value} {}                                  ;# do nothing, command is stored at the composite level

proc spinEntry::set-editable {this value} {
    setStatesAndBindings $this
}

proc spinEntry::set-list {this value} {
    if {$composite::($this,complete)} {
        error {option -orient cannot be set dynamically}
    }
    if {[string length [$composite::($this,entry,path) get]] == 0} {         ;# if not done yet, initialize value with first element
        set $this [lindex $value 0]
    }
}

proc spinEntry::set-range {this value} {
    if {$composite::($this,complete)} {
        error {option -range cannot be set dynamically}
    }
    if {[llength $value] != 3} {
        error {option -range argument format must be {minimum maximum increment}}
    }
    ::set ($this,minimum) [lindex $composite::($this,-range) 0]
    ::set ($this,maximum) [lindex $composite::($this,-range) 1]
    ::set ($this,increment) [lindex $composite::($this,-range) 2]
    if {[catch {expr {$($this,maximum) - $($this,minimum) + $($this,increment)}}]} {
        error {option -range arguments must be numeric values}
    }
    if {[string length [$composite::($this,entry,path) get]] == 0} {               ;# if not done yet, initialize value with minimum
        set $this $($this,minimum)
    }
}

proc spinEntry::set-repeatdelay {this value} {
    widget::configure $composite::($this,decrease) -repeatdelay $value
    widget::configure $composite::($this,increase) -repeatdelay $value
}

proc spinEntry::set-state {this value} {
    if {![regexp {^(disabled|normal)$} $value]} {
        error "bad state value \"$value\": must be normal or disabled"
    }
    setStatesAndBindings $this
}

foreach option {-font -justify -width} {
    proc spinEntry::set$option {this value} "\$composite::(\$this,entry,path) configure $option \$value"
}

proc spinEntry::set-side {this value} {                          ;# specifies on which side of the arrow buttons the entry should be
    if {![regexp {^(left|right)$} $value]} {
        error "bad side value \"$value\": must be left or right"
    }
    pack forget $composite::($this,entry,path) $composite::($this,increase,path) $composite::($this,decrease,path)
    pack $composite::($this,entry,path) -side $value -fill both -expand 1
    pack $composite::($this,increase,path) $composite::($this,decrease,path) -fill y -expand 1
}

proc spinEntry::set-wrap {this value} {}                                     ;# do nothing, command is stored at the composite level

# only now that the widget is in a final state (since the user was able to interact with it) can we do some validity checking
proc spinEntry::decrease {this} {
    set $this [spinEntry::next $this -1]
    invoke $this down
}
proc spinEntry::increase {this} {
    set $this [spinEntry::next $this 1]
    invoke $this up
}

proc spinEntry::next {this direction} {
    ::set value [$composite::($this,entry,path) get]
    ::set wrap $composite::($this,-wrap)
    if {[catch {::set increment $($this,increment)}]} {                                                                 ;# list mode
        ::set index [lsearch -exact $composite::($this,-list) $value]                         ;# try to find the current value index
        incr index $direction                                         ;# note: if not found, we restart at one extremity of the list
        if {$index < 0} {
            if {$wrap} {::set index end} else {::set index 0}
        } elseif {$index >= [llength $composite::($this,-list)]} {
            if {$wrap} {::set index 0} else {::set index end}
        }
        return [lindex $composite::($this,-list) $index]
    } else {                                                                                                           ;# range mode
        ::set minimum $($this,minimum)
        ::set maximum $($this,maximum)
        if {[catch {expr {$value + 0}}]} {                               ;# if entry is editable, contents may not be a valid number
            return [expr {$direction < 0? $minimum: $maximum}]
        } else {
            ::set value [expr {$value + ($direction * $increment)}]
            if {$value <= $minimum} {
                if {$wrap} {return $maximum} else {return $minimum}
            } elseif {$value >= $maximum} {
                if {$wrap} {return $minimum} else {return $maximum}
            } else {
                return $value
            }
        }
    }
}

proc spinEntry::setStatesAndBindings {this} {
    if {[string equal $composite::($this,-state) normal]} {
        widget::configure $composite::($this,decrease) -state normal
        widget::configure $composite::($this,increase) -state normal
        if {$composite::($this,-editable)} {
            $widget::($this,path) configure -takefocus 0                                                  ;# let entry get the focus
            $composite::($this,entry,path) configure -state normal
        } else {
            $widget::($this,path) configure -takefocus 1                                               ;# main widget gets the focus
            $composite::($this,entry,path) configure -state disabled
        }
        $composite::($this,entry,path) configure -foreground $widget::option(entry,foreground)
    } else {
        $widget::($this,path) configure -takefocus 0
        widget::configure $composite::($this,decrease) -state disabled
        widget::configure $composite::($this,increase) -state disabled
        widget::configure $composite::($this,entry) -state disabled
        $composite::($this,entry,path) configure -foreground $widget::option(label,disabledforeground)
    }
}

proc spinEntry::setupUpAndDownKeysBindings {this path} {
    # handle arrow keys events and make arrow buttons match key movements
    bind $path <KeyPress-Down> "arrowButton::sink $composite::($this,decrease); spinEntry::decrease $this"
    bind $path <KeyRelease-Down> "arrowButton::raise $composite::($this,decrease)"
    bind $path <KeyPress-Up> "arrowButton::sink $composite::($this,increase); spinEntry::increase $this"
    bind $path <KeyRelease-Up> "arrowButton::raise $composite::($this,increase)"
}

proc spinEntry::invoke {this {direction none}} {
    ::set command $composite::($this,-command)
    if {[string length $command] > 0} {                                    ;# always invoke command at global level as tk buttons do
        regsub -all %d $command $direction command
        uplevel #0 $command [list [$composite::($this,entry,path) get]]
    }
}

proc spinEntry::set {this text} {                                                     ;# public procedure for setting entry contents
    ::set path $composite::($this,entry,path)
    $path configure -state normal                                                           ;# entry may not be in an editable state
    $path delete 0 end
    $path insert 0 $text
    if {!$composite::($this,-editable)} {
        $path configure -state disabled                                                            ;# eventually restore entry state
    }
}

proc spinEntry::get {this} {                                                       ;# public procedure for retrieving entry contents
    return [$composite::($this,entry,path) get]
}