File: optimenu.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 (227 lines) | stat: -rw-r--r-- 10,188 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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# $Id: optimenu.tcl,v 2.9 2004/12/03 20:44:06 jfontain Exp $


class optionMenu {}

proc optionMenu::optionMenu {this parentPath args} composite {
    [new frame $parentPath -relief $widget::option(button,relief) -borderwidth $widget::option(button,borderwidth)] $args
} {
    set path $widget::($this,path)
    grid rowconfigure $path 0 -weight 1
    grid columnconfigure $path 0 -weight 1
    composite::manage $this [new label $path -padx 0 -pady 0] label
    grid $composite::($this,label,path) -column 0 -row 0 -sticky nsew
    # separate label from stub with border width value so that shell when popped does not hide part of the stub
    grid columnconfigure $path 1 -minsize $widget::option(button,borderwidth)
    # use a frame instead of a button which does not accept pixel sizes
    composite::manage $this [new frame $path\
        -background $widget::option(button,background) -relief $widget::option(button,relief)\
        -borderwidth $widget::option(button,borderwidth) -width 12 -height 8\
    ] stub
    set stubPath $composite::($this,stub,path)
    grid $stubPath -column 2 -row 0
    grid columnconfigure $path 3 -minsize 8

    # setup bindings for activation highlighting
    bind $path <Enter> "if {!\$tk_strictMotif} {$stubPath configure -background $widget::option(button,activebackground)}"
    bind $path <Leave> "if {!\$tk_strictMotif} {$stubPath configure -background $widget::option(button,background)}"

    composite::manage $this [new toplevel [winfo toplevel $parentPath] -cursor right_ptr] shell
    set shellPath $composite::($this,shell,path)
    if {$widget::option(menu,borderwidth)==0} {                ;# use a thin black border for popup window, such as in windows menus
        $shellPath configure -highlightbackground black -highlightthickness 1
    } else {                                                                                       ;# use a border, such as in motif
        $shellPath configure -relief raised -borderwidth $widget::option(menu,borderwidth)                   ;# for Windows and UNIX
    }
    wm overrideredirect $shellPath 1                              ;# no window manager decorations, choices are invisible by default
    wm withdraw $shellPath

    global embed_args
    if {[info exists embed_args]} {
        # running in the plug-in environment, menu emulation is not possible since grab command is not yet available
        set sequence <ButtonRelease-1>
    } else {       ;# if not running in the plug-in environment, act as a menu and allow button press in label to pop up the choices
        set sequence <ButtonPress-1>
        bind $composite::($this,label,path) $sequence "optionMenu::popChoices $this"
    }
    bind $path $sequence "optionMenu::popChoices $this"
    bind $composite::($this,stub,path) $sequence "optionMenu::popChoices $this"
    set ($this,selectedLabelIndex) 0
    composite::complete $this
}

proc optionMenu::~optionMenu {this} {}

proc optionMenu::options {this} {
    return [list\
        [list -choices {} {}]\
        [list -command {} {}]\
        [list -font $widget::option(menu,font) $widget::option(menu,font)]\
        [list -popupcommand {} {}]\
        [list -takefocus 1]\
        [list -text {} {}]\
    ]
}

proc optionMenu::set-command {this value} {}
proc optionMenu::set-popupcommand {this value} {}

proc optionMenu::set-font {this value} {
    $composite::($this,label,path) configure -font $value
    set-choices $this $composite::($this,-choices)                      ;# geometry management must be updated according to new font
}

proc optionMenu::set-text {this value} {
    $composite::($this,label,path) configure -text $value
}

proc optionMenu::set-choices {this value} {
    set path $composite::($this,shell,path)
    eval destroy [winfo children $path]                                                              ;# destroy current labels first
    set index 0
    set width 0
    foreach choice $composite::($this,-choices) {
        set label [label $path.$index -text $choice -relief flat -font $composite::($this,-font)]
        if {[winfo reqwidth $label]>$width} {
            set width [winfo reqwidth $label]
        }
        bind $label <Enter> "optionMenu::select $this $index"
        pack $label -fill x
        incr index
    }
    grid columnconfigure $widget::($this,path) 0 -minsize $width                 ;# find maximum width and apply it to visible label
    showTopLevel $path 0x0                                      ;# make sure sizes will be correct the first time choices are popped
    update idletasks
    wm withdraw $path
    wm geometry $path {}
}

proc optionMenu::set-takefocus {this value} {
    set path $widget::($this,path)
    switch $value {
        0 {
            bind $path <space> {}
            bind $path <Return> {}
            bind $path <KP_Enter> {}
            bind $path <Up> {}
            bind $path <Down> {}
            bind $path <Escape> {}
        }
        1 {
            bind $path <space> "optionMenu::processSpaceKey $this"
            bind $path <Return> "optionMenu::unpopChoices $this; optionMenu::checkSelection $this"
            bind $path <KP_Enter> [bind $path <Return>]
            bind $path <Up> "optionMenu::selectPrevious $this"
            bind $path <Down> "optionMenu::selectNext $this"
            bind $path <Escape> "optionMenu::unpopChoices $this"
        }
        default {
            error "bad takefocus value \"$value\": must be 0 or 1"
        }
    }
    $path configure -takefocus $value
}

proc optionMenu::popChoices {this} {
    if {\
        ([llength $composite::($this,-choices)] == 0) ||\
        (([string length $composite::($this,-popupcommand)] > 0) && ![uplevel #0 $composite::($this,-popupcommand)])\
    } return                                                                     ;# user code may cancel popping up the choices menu
    update idletasks                                                                                  ;# make sure sizes are correct
    set shellPath $composite::($this,shell,path)

    set selected [lindex [winfo children $composite::($this,shell,path)] $($this,selectedLabelIndex)]
    $selected configure -background $widget::option(menu,activebackground) -foreground $widget::option(menu,activeforeground)\
        -relief $widget::option(menu,relief)

    set labelPath $composite::($this,label,path)  ;# position selected label center at the same abscissa of the display label center
    set x [expr {[winfo rootx $labelPath]-$widget::option(menu,borderwidth)}]
    if {$x<0} {set x 0}
    set y [expr {[winfo rooty $labelPath]+(([winfo height $labelPath]-[winfo height $selected])/2)-[winfo y $selected]}]
    if {$y<0} {set y 0}
    # make sure choices width is identical to label width
    showTopLevel $shellPath\
        [expr {[winfo width $labelPath]+(2*$widget::option(menu,borderwidth))}]x[winfo reqheight $shellPath]+$x+$y
    update idletasks
    raise $shellPath
    set (previousGrab) [grab current $shellPath]
    global embed_args
    if {[info exists embed_args]} {                    ;# running in the plug-in environment, grab does not work yet and is emulated
        bind $shellPath <ButtonRelease-1> "optionMenu::unpopChoices $this; optionMenu::checkSelection $this"
        grab $shellPath
    } else {
        # add a small delay before allowing a button release to unpop the shell so that a rapid press / release sequence leaves the
        # shell popped to emulate the Motif behavior
        after 300 "bind $shellPath <ButtonRelease-1> {optionMenu::unpopChoices $this; optionMenu::checkSelection $this}"
        grab -global $shellPath
    }
}

proc optionMenu::unpopChoices {this} {
    set path $composite::($this,shell,path)
    if {![winfo ismapped $path]} {
        return                                                                                                   ;# already unpopped
    }
    wm withdraw $path
    if {[string length $(previousGrab)]>0} {
        grab $(previousGrab)
        unset (previousGrab)
    } else {
        grab release $path
    }
    bind $path <ButtonRelease-1> {}
}

proc optionMenu::checkSelection {this} {
    set selected [lindex [winfo children $composite::($this,shell,path)] $($this,selectedLabelIndex)]
    if {[string length $selected]==0} return
    set selection [$selected cget -text]
    composite::configure $this -text $selection                            ;# use composite layer so that cget returns current value
    invokeCommand $this $selection
}

proc optionMenu::invokeCommand {this choice} {
    if {[string length $composite::($this,-command)]>0} {
        uplevel #0 $composite::($this,-command) [list $choice]             ;# always invoke command at global level as tk buttons do
    }
}

proc optionMenu::configureChoices {this args} {
    foreach label [winfo children $composite::($this,shell,path)] {
        eval $label configure $option $args
    }
}

proc optionMenu::select {this index} {
    if {![winfo ismapped $composite::($this,shell,path)]} {
        return                                                           ;# no selection should be allowed if choices are not posted
    }
    set labels [winfo children $composite::($this,shell,path)]
    if {$index<0} {
        set index 0
    } elseif {$index>=[llength $labels]} {
        set index [expr {[llength $labels]-1}]
    }
    [lindex $labels $($this,selectedLabelIndex)] configure -background $widget::option(menu,background)\
        -foreground $widget::option(menu,foreground) -relief flat
    [lindex $labels $index] configure -background $widget::option(menu,activebackground)\
        -foreground $widget::option(menu,activeforeground) -relief $widget::option(menu,relief)
    set ($this,selectedLabelIndex) $index
}

proc optionMenu::selectPrevious {this} {
    select $this [expr {$($this,selectedLabelIndex)-1}]
}

proc optionMenu::selectNext {this} {
    select $this [expr {$($this,selectedLabelIndex)+1}]
}

proc optionMenu::processSpaceKey {this} {
    if {[winfo ismapped $composite::($this,shell,path)]} {
        unpopChoices $this
        checkSelection $this
    } else {
        popChoices $this
    }
}