|
|
1.1 root 1: # menu.tcl --
2: #
3: # This file contains Tcl procedures used to manage Tk menus and
4: # menubuttons. Most of the code here is dedicated to support for
5: # menu traversal via the keyboard.
6: #
7: # $Header: /user6/ouster/wish/scripts/RCS/menu.tcl,v 1.11 92/08/08 14:49:55 ouster Exp $ SPRITE (Berkeley)
8: #
9: # Copyright 1992 Regents of the University of California
10: # Permission to use, copy, modify, and distribute this
11: # software and its documentation for any purpose and without
12: # fee is hereby granted, provided that this copyright
13: # notice appears in all copies. The University of California
14: # makes no representations about the suitability of this
15: # software for any purpose. It is provided "as is" without
16: # express or implied warranty.
17: #
18:
19: # The procedure below is publically available. It is used to indicate
20: # the menus associated with a particular top-level window, for purposes
21: # of keyboard menu traversal. Its first argument is the path name of
22: # a top-level window, and any additional arguments are the path names of
23: # the menu buttons associated with that top-level window, in the order
24: # they should be traversed. If no menu buttons are named, the procedure
25: # returns the current list of menus for w. If a single empty string is
26: # supplied, then the menu list for w is cancelled. Otherwise, tk_menus
27: # sets the menu list for w to the menu buttons.
28:
29: proc tk_menus {w args} {
30: global tk_priv
31:
32: if {$args == ""} {
33: if [catch {set result [set tk_priv(menusFor$w)]}] {
34: return ""
35: }
36: return $result
37: }
38:
39: if {$args == "{}"} {
40: catch {unset tk_priv(menusFor$w)}
41: return ""
42: }
43:
44: set tk_priv(menusFor$w) $args
45: }
46:
47: # The procedure below is publically available. It takes any number of
48: # arguments taht are names of widgets or classes. It sets up bindings
49: # for the widgets or classes so that keyboard menu traversal is possible
50: # when the input focus is in those widgets or classes.
51:
52: proc tk_bindForTraversal args {
53: foreach w $args {
54: bind $w <Alt-KeyPress> {tk_traverseToMenu %W %A}
55: bind $w <F10> {tk_firstMenu %W}
56: }
57: }
58:
59: # The procedure below does all of the work of posting a menu (including
60: # unposting any other menu that might currently be posted). The "w"
61: # argument is the name of the menubutton for the menu to be posted.
62: # Note: if $w is disabled then the procedure does nothing.
63:
64: proc tk_mbPost {w} {
65: global tk_priv tk_strictMotif
66: if {[lindex [$w config -state] 4] == "disabled"} {
67: return
68: }
69: set cur $tk_priv(posted)
70: if {$cur == $w} {
71: return
72: }
73: if {$cur != ""} tk_mbUnpost
74: set tk_priv(relief) [lindex [$w config -relief] 4]
75: $w config -relief raised
76: set tk_priv(cursor) [lindex [$w config -cursor] 4]
77: $w config -cursor arrow
78: $w post
79: grab -global $w
80: set tk_priv(posted) $w
81: if {$tk_priv(focus) == ""} {
82: set tk_priv(focus) [focus]
83: }
84: set menu [lindex [$w config -menu] 4]
85: set tk_priv(activeBg) [lindex [$menu config -activebackground] 4]
86: set tk_priv(activeFg) [lindex [$menu config -activeforeground] 4]
87: if $tk_strictMotif {
88: $menu config -activebackground [lindex [$menu config -background] 4]
89: $menu config -activeforeground [lindex [$menu config -foreground] 4]
90: }
91: focus $menu
92: }
93:
94: # The procedure below does all the work of unposting the menubutton that's
95: # currently posted. It takes no arguments.
96:
97: proc tk_mbUnpost {} {
98: global tk_priv
99: if {$tk_priv(posted) != ""} {
100: $tk_priv(posted) config -relief $tk_priv(relief)
101: $tk_priv(posted) config -cursor $tk_priv(cursor)
102: $tk_priv(posted) config -activebackground $tk_priv(activeBg)
103: $tk_priv(posted) config -activeforeground $tk_priv(activeFg)
104: $tk_priv(posted) unpost
105: # deh multi display bug fix
106: # grab none
107: set menu [lindex [$tk_priv(posted) config -menu] 4]
108: grab -off $menu
109: focus $tk_priv(focus)
110: set tk_priv(focus) ""
111: $menu config -activebackground $tk_priv(activeBg)
112: $menu config -activeforeground $tk_priv(activeFg)
113: set tk_priv(posted) {}
114: }
115: }
116:
117: # The procedure below is invoked to implement keyboard traversal to
118: # a menu button. It takes two arguments: the name of a window where
119: # a keystroke originated, and the ascii character that was typed.
120: # This procedure finds a menu bar by looking upward for a top-level
121: # window, then looking for a window underneath that named "menu".
122: # Then it searches through all the subwindows of "menu" for a menubutton
123: # with an underlined character matching char. If one is found, it
124: # posts that menu.
125:
126: proc tk_traverseToMenu {w char} {
127: global tk_priv
128: if {$char == ""} {
129: return
130: }
131: set char [string tolower $char]
132:
133: foreach mb [tk_getMenuButtons $w] {
134: if {[winfo class $mb] == "Menubutton"} {
135: set char2 [string index [lindex [$mb config -text] 4] \
136: [lindex [$mb config -underline] 4]]
137: if {[string compare $char [string tolower $char2]] == 0} {
138: tk_mbPost $mb
139: [lindex [$mb config -menu] 4] activate 0
140: return
141: }
142: }
143: }
144: }
145:
146: # The procedure below is used to implement keyboard traversal within
147: # the posted menu. It takes two arguments: the name of the menu to
148: # be traversed within, and an ASCII character. It searches for an
149: # entry in the menu that has that character underlined. If such an
150: # entry is found, it is invoked and the menu is unposted.
151:
152: proc tk_traverseWithinMenu {w char} {
153: if {$char == ""} {
154: return
155: }
156: set char [string tolower $char]
157: set last [$w index last]
158: for {set i 0} {$i <= $last} {incr i} {
159: if [catch {set char2 [string index \
160: [lindex [$w entryconfig $i -label] 4] \
161: [lindex [$w entryconfig $i -underline] 4]]}] {
162: continue
163: }
164: if {[string compare $char [string tolower $char2]] == 0} {
165: tk_mbUnpost
166: $w invoke $i
167: return
168: }
169: }
170: }
171:
172: # The procedure below takes a single argument, which is the name of
173: # a window. It returns a list containing path names for all of the
174: # menu buttons associated with that window's top-level window, or an
175: # empty list if there are none.
176:
177: proc tk_getMenuButtons w {
178: global tk_priv
179: set top [winfo toplevel $w]
180: if [catch {set buttons [set tk_priv(menusFor$top)]}] {
181: return ""
182: }
183: return $buttons
184: }
185:
186: # The procedure below is used to traverse to the next or previous
187: # menu in a menu bar. It takes one argument, which is a count of
188: # how many menu buttons forward or backward (if negative) to move.
189: # If there is no posted menu then this procedure has no effect.
190:
191: proc tk_nextMenu count {
192: global tk_priv
193: if {$tk_priv(posted) == ""} {
194: return
195: }
196: set buttons [tk_getMenuButtons $tk_priv(posted)]
197: set length [llength $buttons]
198: for {set i 0} 1 {incr i} {
199: if {$i >= $length} {
200: return
201: }
202: if {[lindex $buttons $i] == $tk_priv(posted)} {
203: break
204: }
205: }
206: incr i $count
207: while 1 {
208: while {$i < 0} {
209: incr i $length
210: }
211: while {$i >= $length} {
212: incr i -$length
213: }
214: set mb [lindex $buttons $i]
215: if {[lindex [$mb configure -state] 4] != "disabled"} {
216: break
217: }
218: incr i $count
219: }
220: tk_mbUnpost
221: tk_mbPost $mb
222: [lindex [$mb config -menu] 4] activate 0
223: }
224:
225: # The procedure below is used to traverse to the next or previous entry
226: # in the posted menu. It takes one argument, which is 1 to go to the
227: # next entry or -1 to go to the previous entry. Disabled entries are
228: # skipped in this process.
229:
230: proc tk_nextMenuEntry count {
231: global tk_priv
232: if {$tk_priv(posted) == ""} {
233: return
234: }
235: set menu [lindex [$tk_priv(posted) config -menu] 4]
236: set length [expr [$menu index last]+1]
237: set i [$menu index active]
238: if {$i == "none"} {
239: set i 0
240: } else {
241: incr i $count
242: }
243: while 1 {
244: while {$i < 0} {
245: incr i $length
246: }
247: while {$i >= $length} {
248: incr i -$length
249: }
250: if {[catch {$menu entryconfigure $i -state} state] == 0} {
251: if {[lindex $state 4] != "disabled"} {
252: break
253: }
254: }
255: incr i $count
256: }
257: $menu activate $i
258: }
259:
260: # The procedure below invokes the active entry in the posted menu,
261: # if there is one. Otherwise it does nothing.
262:
263: proc tk_invokeMenu {menu} {
264: set i [$menu index active]
265: if {$i != "none"} {
266: tk_mbUnpost
267: update idletasks
268: $menu invoke $i
269: }
270: }
271:
272: # The procedure below is invoked to keyboard-traverse to the first
273: # menu for a given source window. The source window is passed as
274: # parameter.
275:
276: proc tk_firstMenu w {
277: set mb [lindex [tk_getMenuButtons $w] 0]
278: if {$mb != ""} {
279: tk_mbPost $mb
280: [lindex [$mb config -menu] 4] activate 0
281: }
282: }
283:
284: # The procedure below is invoked when a button-1-down event is
285: # received by a menu button. If the mouse is in the menu button
286: # then it posts the button's menu. If the mouse isn't in the
287: # button's menu, then it deactivates any active entry in the menu.
288: # Remember, event-sharing can cause this procedure to be invoked
289: # for two different menu buttons on the same event.
290:
291: proc tk_mbButtonDown w {
292: global tk_priv
293: if {[lindex [$w config -state] 4] == "disabled"} {
294: return
295: }
296: if {$tk_priv(inMenuButton) == $w} {
297: tk_mbPost $w
298: }
299: set menu [lindex [$tk_priv(posted) config -menu] 4]
300: if {$tk_priv(window) != $menu} {
301: $menu activate none
302: }
303: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.