OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tk/] [library/] [menu.tcl] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
# menu.tcl --
2
#
3
# This file defines the default bindings for Tk menus and menubuttons.
4
# It also implements keyboard traversal of menus and implements a few
5
# other utility procedures related to menus.
6
#
7
# SCCS: @(#) menu.tcl 1.103 97/10/31 15:26:08
8
#
9
# Copyright (c) 1992-1994 The Regents of the University of California.
10
# Copyright (c) 1994-1997 Sun Microsystems, Inc.
11
#
12
# See the file "license.terms" for information on usage and redistribution
13
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14
#
15
 
16
#-------------------------------------------------------------------------
17
# Elements of tkPriv that are used in this file:
18
#
19
# cursor -              Saves the -cursor option for the posted menubutton.
20
# focus -               Saves the focus during a menu selection operation.
21
#                       Focus gets restored here when the menu is unposted.
22
# grabGlobal -          Used in conjunction with tkPriv(oldGrab):  if
23
#                       tkPriv(oldGrab) is non-empty, then tkPriv(grabGlobal)
24
#                       contains either an empty string or "-global" to
25
#                       indicate whether the old grab was a local one or
26
#                       a global one.
27
# inMenubutton -        The name of the menubutton widget containing
28
#                       the mouse, or an empty string if the mouse is
29
#                       not over any menubutton.
30
# menuBar -             The name of the menubar that is the root
31
#                       of the cascade hierarchy which is currently
32
#                       posted. This is null when there is no menu currently
33
#                       being pulled down from a menu bar.
34
# oldGrab -             Window that had the grab before a menu was posted.
35
#                       Used to restore the grab state after the menu
36
#                       is unposted.  Empty string means there was no
37
#                       grab previously set.
38
# popup -               If a menu has been popped up via tk_popup, this
39
#                       gives the name of the menu.  Otherwise this
40
#                       value is empty.
41
# postedMb -            Name of the menubutton whose menu is currently
42
#                       posted, or an empty string if nothing is posted
43
#                       A grab is set on this widget.
44
# relief -              Used to save the original relief of the current
45
#                       menubutton.
46
# window -              When the mouse is over a menu, this holds the
47
#                       name of the menu;  it's cleared when the mouse
48
#                       leaves the menu.
49
# tearoff -             Whether the last menu posted was a tearoff or not.
50
#                       This is true always for unix, for tearoffs for Mac
51
#                       and Windows.
52
# activeMenu -          This is the last active menu for use
53
#                       with the <<MenuSelect>> virtual event.
54
# activeItem -          This is the last active menu item for
55
#                       use with the <<MenuSelect>> virtual event.
56
#-------------------------------------------------------------------------
57
 
58
#-------------------------------------------------------------------------
59
# Overall note:
60
# This file is tricky because there are five different ways that menus
61
# can be used:
62
#
63
# 1. As a pulldown from a menubutton. In this style, the variable 
64
#    tkPriv(postedMb) identifies the posted menubutton.
65
# 2. As a torn-off menu copied from some other menu.  In this style
66
#    tkPriv(postedMb) is empty, and menu's type is "tearoff".
67
# 3. As an option menu, triggered from an option menubutton.  In this
68
#    style tkPriv(postedMb) identifies the posted menubutton.
69
# 4. As a popup menu.  In this style tkPriv(postedMb) is empty and
70
#    the top-level menu's type is "normal".
71
# 5. As a pulldown from a menubar. The variable tkPriv(menubar) has
72
#    the owning menubar, and the menu itself is of type "normal".
73
#
74
# The various binding procedures use the  state described above to
75
# distinguish the various cases and take different actions in each
76
# case.
77
#-------------------------------------------------------------------------
78
 
79
#-------------------------------------------------------------------------
80
# The code below creates the default class bindings for menus
81
# and menubuttons.
82
#-------------------------------------------------------------------------
83
 
84
bind Menubutton <FocusIn> {}
85
bind Menubutton <Enter> {
86
    tkMbEnter %W
87
}
88
bind Menubutton <Leave> {
89
    tkMbLeave %W
90
}
91
bind Menubutton <1> {
92
    if {$tkPriv(inMenubutton) != ""} {
93
        tkMbPost $tkPriv(inMenubutton) %X %Y
94
    }
95
}
96
bind Menubutton <Motion> {
97
    tkMbMotion %W up %X %Y
98
}
99
bind Menubutton <B1-Motion> {
100
    tkMbMotion %W down %X %Y
101
}
102
bind Menubutton <ButtonRelease-1> {
103
    tkMbButtonUp %W
104
}
105
bind Menubutton <space> {
106
    tkMbPost %W
107
    tkMenuFirstEntry [%W cget -menu]
108
}
109
 
110
# Must set focus when mouse enters a menu, in order to allow
111
# mixed-mode processing using both the mouse and the keyboard.
112
# Don't set the focus if the event comes from a grab release,
113
# though:  such an event can happen after as part of unposting
114
# a cascaded chain of menus, after the focus has already been
115
# restored to wherever it was before menu selection started.
116
 
117
bind Menu <FocusIn> {}
118
 
119
bind Menu <Enter> {
120
    set tkPriv(window) %W
121
    if {[%W cget -type] == "tearoff"} {
122
        if {"%m" != "NotifyUngrab"} {
123
            if {$tcl_platform(platform) == "unix"} {
124
                tk_menuSetFocus %W
125
            }
126
        }
127
    }
128
    tkMenuMotion %W %x %y %s
129
}
130
 
131
bind Menu <Leave> {
132
    tkMenuLeave %W %X %Y %s
133
}
134
bind Menu <Motion> {
135
    tkMenuMotion %W %x %y %s
136
}
137
bind Menu <ButtonPress> {
138
    tkMenuButtonDown %W
139
}
140
bind Menu <ButtonRelease> {
141
   tkMenuInvoke %W 1
142
}
143
bind Menu <space> {
144
    tkMenuInvoke %W 0
145
}
146
bind Menu <Return> {
147
    tkMenuInvoke %W 0
148
}
149
bind Menu <Escape> {
150
    tkMenuEscape %W
151
}
152
bind Menu <Left> {
153
    tkMenuLeftArrow %W
154
}
155
bind Menu <Right> {
156
    tkMenuRightArrow %W
157
}
158
bind Menu <Up> {
159
    tkMenuUpArrow %W
160
}
161
bind Menu <Down> {
162
    tkMenuDownArrow %W
163
}
164
bind Menu <KeyPress> {
165
    tkTraverseWithinMenu %W %A
166
}
167
 
168
# The following bindings apply to all windows, and are used to
169
# implement keyboard menu traversal.
170
 
171
if {$tcl_platform(platform) == "unix"} {
172
    bind all <Alt-KeyPress> {
173
        tkTraverseToMenu %W %A
174
    }
175
 
176
    bind all <F10> {
177
        tkFirstMenu %W
178
    }
179
} else {
180
    bind Menubutton <Alt-KeyPress> {
181
        tkTraverseToMenu %W %A
182
    }
183
 
184
    bind Menubutton <F10> {
185
        tkFirstMenu %W
186
    }
187
}
188
 
189
# tkMbEnter --
190
# This procedure is invoked when the mouse enters a menubutton
191
# widget.  It activates the widget unless it is disabled.  Note:
192
# this procedure is only invoked when mouse button 1 is *not* down.
193
# The procedure tkMbB1Enter is invoked if the button is down.
194
#
195
# Arguments:
196
# w -                   The  name of the widget.
197
 
198
proc tkMbEnter w {
199
    global tkPriv
200
 
201
    if {$tkPriv(inMenubutton) != ""} {
202
        tkMbLeave $tkPriv(inMenubutton)
203
    }
204
    set tkPriv(inMenubutton) $w
205
    if {[$w cget -state] != "disabled"} {
206
        $w configure -state active
207
    }
208
}
209
 
210
# tkMbLeave --
211
# This procedure is invoked when the mouse leaves a menubutton widget.
212
# It de-activates the widget, if the widget still exists.
213
#
214
# Arguments:
215
# w -                   The  name of the widget.
216
 
217
proc tkMbLeave w {
218
    global tkPriv
219
 
220
    set tkPriv(inMenubutton) {}
221
    if {![winfo exists $w]} {
222
        return
223
    }
224
    if {[$w cget -state] == "active"} {
225
        $w configure -state normal
226
    }
227
}
228
 
229
# tkMbPost --
230
# Given a menubutton, this procedure does all the work of posting
231
# its associated menu and unposting any other menu that is currently
232
# posted.
233
#
234
# Arguments:
235
# w -                   The name of the menubutton widget whose menu
236
#                       is to be posted.
237
# x, y -                Root coordinates of cursor, used for positioning
238
#                       option menus.  If not specified, then the center
239
#                       of the menubutton is used for an option menu.
240
 
241
proc tkMbPost {w {x {}} {y {}}} {
242
    global tkPriv errorInfo
243
    global tcl_platform
244
 
245
    if {([$w cget -state] == "disabled") || ($w == $tkPriv(postedMb))} {
246
        return
247
    }
248
    set menu [$w cget -menu]
249
    if {$menu == ""} {
250
        return
251
    }
252
    set tearoff [expr {($tcl_platform(platform) == "unix") \
253
                     || ([$menu cget -type] == "tearoff")}]
254
    if {[string first $w $menu] != 0} {
255
        error "can't post $menu:  it isn't a descendant of $w (this is a new requirement in Tk versions 3.0 and later)"
256
    }
257
    set cur $tkPriv(postedMb)
258
    if {$cur != ""} {
259
        tkMenuUnpost {}
260
    }
261
    set tkPriv(cursor) [$w cget -cursor]
262
    set tkPriv(relief) [$w cget -relief]
263
    $w configure -cursor arrow
264
    $w configure -relief raised
265
 
266
    set tkPriv(postedMb) $w
267
    set tkPriv(focus) [focus]
268
    $menu activate none
269
    tkGenerateMenuSelect $menu
270
 
271
    # If this looks like an option menubutton then post the menu so
272
    # that the current entry is on top of the mouse.  Otherwise post
273
    # the menu just below the menubutton, as for a pull-down.
274
 
275
    update idletasks
276
    if {[catch {
277
         switch [$w cget -direction] {
278
            above {
279
                set x [winfo rootx $w]
280
                set y [expr {[winfo rooty $w] - [winfo reqheight $menu]}]
281
                $menu post $x $y
282
            }
283
            below {
284
                set x [winfo rootx $w]
285
                set y [expr {[winfo rooty $w] + [winfo height $w]}]
286
                $menu post $x $y
287
            }
288
            left {
289
                set x [expr {[winfo rootx $w] - [winfo reqwidth $menu]}]
290
                set y [expr {(2 * [winfo rooty $w] + [winfo height $w]) / 2}]
291
                set entry [tkMenuFindName $menu [$w cget -text]]
292
                if {[$w cget -indicatoron]} {
293
                    if {$entry == [$menu index last]} {
294
                        incr y [expr {-([$menu yposition $entry] \
295
                                + [winfo reqheight $menu])/2}]
296
                    } else {
297
                        incr y [expr {-([$menu yposition $entry] \
298
                                + [$menu yposition [expr {$entry+1}]])/2}]
299
                    }
300
                }
301
                $menu post $x $y
302
                if {($entry != {}) && ([$menu entrycget $entry -state] != "disabled")} {
303
                    $menu activate $entry
304
                    tkGenerateMenuSelect $menu
305
                }
306
            }
307
            right {
308
                set x [expr {[winfo rootx $w] + [winfo width $w]}]
309
                set y [expr {(2 * [winfo rooty $w] + [winfo height $w]) / 2}]
310
                set entry [tkMenuFindName $menu [$w cget -text]]
311
                if {[$w cget -indicatoron]} {
312
                    if {$entry == [$menu index last]} {
313
                        incr y [expr {-([$menu yposition $entry] \
314
                                + [winfo reqheight $menu])/2}]
315
                    } else {
316
                        incr y [expr {-([$menu yposition $entry] \
317
                                + [$menu yposition [expr {$entry+1}]])/2}]
318
                    }
319
                }
320
                $menu post $x $y
321
                if {($entry != {}) && ([$menu entrycget $entry -state] != "disabled")} {
322
                    $menu activate $entry
323
                    tkGenerateMenuSelect $menu
324
                }
325
            }
326
            default {
327
                if {[$w cget -indicatoron]} {
328
                    if {$y == ""} {
329
                        set x [expr {[winfo rootx $w] + [winfo width $w]/2}]
330
                        set y [expr {[winfo rooty $w] + [winfo height $w]/2}]
331
                    }
332
                    tkPostOverPoint $menu $x $y [tkMenuFindName $menu [$w cget -text]]
333
                } else {
334
                    $menu post [winfo rootx $w] [expr {[winfo rooty $w]+[winfo height $w]}]
335
                }
336
            }
337
         }
338
     } msg]} {
339
        # Error posting menu (e.g. bogus -postcommand). Unpost it and
340
        # reflect the error.
341
 
342
        set savedInfo $errorInfo
343
        tkMenuUnpost {}
344
        error $msg $savedInfo
345
 
346
    }
347
 
348
    set tkPriv(tearoff) $tearoff
349
    if {$tearoff != 0} {
350
        focus $menu
351
        tkSaveGrabInfo $w
352
        grab -global $w
353
    }
354
}
355
 
356
# tkMenuUnpost --
357
# This procedure unposts a given menu, plus all of its ancestors up
358
# to (and including) a menubutton, if any.  It also restores various
359
# values to what they were before the menu was posted, and releases
360
# a grab if there's a menubutton involved.  Special notes:
361
# 1. It's important to unpost all menus before releasing the grab, so
362
#    that any Enter-Leave events (e.g. from menu back to main
363
#    application) have mode NotifyGrab.
364
# 2. Be sure to enclose various groups of commands in "catch" so that
365
#    the procedure will complete even if the menubutton or the menu
366
#    or the grab window has been deleted.
367
#
368
# Arguments:
369
# menu -                Name of a menu to unpost.  Ignored if there
370
#                       is a posted menubutton.
371
 
372
proc tkMenuUnpost menu {
373
    global tcl_platform
374
    global tkPriv
375
    set mb $tkPriv(postedMb)
376
 
377
    # Restore focus right away (otherwise X will take focus away when
378
    # the menu is unmapped and under some window managers (e.g. olvwm)
379
    # we'll lose the focus completely).
380
 
381
    catch {focus $tkPriv(focus)}
382
    set tkPriv(focus) ""
383
 
384
    # Unpost menu(s) and restore some stuff that's dependent on
385
    # what was posted.
386
 
387
    catch {
388
        if {$mb != ""} {
389
            set menu [$mb cget -menu]
390
            $menu unpost
391
            set tkPriv(postedMb) {}
392
            $mb configure -cursor $tkPriv(cursor)
393
            $mb configure -relief $tkPriv(relief)
394
        } elseif {$tkPriv(popup) != ""} {
395
            $tkPriv(popup) unpost
396
            set tkPriv(popup) {}
397
        } elseif {(!([$menu cget -type] == "menubar")
398
                && !([$menu cget -type] == "tearoff"))} {
399
            # We're in a cascaded sub-menu from a torn-off menu or popup.
400
            # Unpost all the menus up to the toplevel one (but not
401
            # including the top-level torn-off one) and deactivate the
402
            # top-level torn off menu if there is one.
403
 
404
            while 1 {
405
                set parent [winfo parent $menu]
406
                if {([winfo class $parent] != "Menu")
407
                        || ![winfo ismapped $parent]} {
408
                    break
409
                }
410
                $parent activate none
411
                $parent postcascade none
412
                tkGenerateMenuSelect $parent
413
                set type [$parent cget -type]
414
                if {($type == "menubar")|| ($type == "tearoff")} {
415
                    break
416
                }
417
                set menu $parent
418
            }
419
            if {[$menu cget -type] != "menubar"} {
420
                $menu unpost
421
            }
422
        }
423
    }
424
 
425
    if {($tkPriv(tearoff) != 0) || ($tkPriv(menuBar) != "")} {
426
        # Release grab, if any, and restore the previous grab, if there
427
        # was one.
428
 
429
        if {$menu != ""} {
430
            set grab [grab current $menu]
431
            if {$grab != ""} {
432
                grab release $grab
433
            }
434
        }
435
        tkRestoreOldGrab
436
        if {$tkPriv(menuBar) != ""} {
437
            $tkPriv(menuBar) configure -cursor $tkPriv(cursor)
438
            set tkPriv(menuBar) {}
439
        }
440
        if {$tcl_platform(platform) != "unix"} {
441
            set tkPriv(tearoff) 0
442
        }
443
    }
444
}
445
 
446
# tkMbMotion --
447
# This procedure handles mouse motion events inside menubuttons, and
448
# also outside menubuttons when a menubutton has a grab (e.g. when a
449
# menu selection operation is in progress).
450
#
451
# Arguments:
452
# w -                   The name of the menubutton widget.
453
# upDown -              "down" means button 1 is pressed, "up" means
454
#                       it isn't.
455
# rootx, rooty -        Coordinates of mouse, in (virtual?) root window.
456
 
457
proc tkMbMotion {w upDown rootx rooty} {
458
    global tkPriv
459
 
460
    if {$tkPriv(inMenubutton) == $w} {
461
        return
462
    }
463
    set new [winfo containing $rootx $rooty]
464
    if {($new != $tkPriv(inMenubutton)) && (($new == "")
465
            || ([winfo toplevel $new] == [winfo toplevel $w]))} {
466
        if {$tkPriv(inMenubutton) != ""} {
467
            tkMbLeave $tkPriv(inMenubutton)
468
        }
469
        if {($new != "") && ([winfo class $new] == "Menubutton")
470
                && ([$new cget -indicatoron] == 0)
471
                && ([$w cget -indicatoron] == 0)} {
472
            if {$upDown == "down"} {
473
                tkMbPost $new $rootx $rooty
474
            } else {
475
                tkMbEnter $new
476
            }
477
        }
478
    }
479
}
480
 
481
# tkMbButtonUp --
482
# This procedure is invoked to handle button 1 releases for menubuttons.
483
# If the release happens inside the menubutton then leave its menu
484
# posted with element 0 activated.  Otherwise, unpost the menu.
485
#
486
# Arguments:
487
# w -                   The name of the menubutton widget.
488
 
489
proc tkMbButtonUp w {
490
    global tkPriv
491
    global tcl_platform
492
 
493
    set tearoff [expr {($tcl_platform(platform) == "unix") \
494
                     || ([[$w cget -menu] cget -type] == "tearoff")}]
495
    if {($tearoff != 0) && ($tkPriv(postedMb) == $w)
496
            && ($tkPriv(inMenubutton) == $w)} {
497
        tkMenuFirstEntry [$tkPriv(postedMb) cget -menu]
498
    } else {
499
        tkMenuUnpost {}
500
    }
501
}
502
 
503
# tkMenuMotion --
504
# This procedure is called to handle mouse motion events for menus.
505
# It does two things.  First, it resets the active element in the
506
# menu, if the mouse is over the menu.  Second, if a mouse button
507
# is down, it posts and unposts cascade entries to match the mouse
508
# position.
509
#
510
# Arguments:
511
# menu -                The menu window.
512
# x -                   The x position of the mouse.
513
# y -                   The y position of the mouse.
514
# state -               Modifier state (tells whether buttons are down).
515
 
516
proc tkMenuMotion {menu x y state} {
517
    global tkPriv
518
    if {$menu == $tkPriv(window)} {
519
        if {[$menu cget -type] == "menubar"} {
520
            if {[info exists tkPriv(focus)] && \
521
                    ([string compare $menu $tkPriv(focus)] != 0)} {
522
                $menu activate @$x,$y
523
                tkGenerateMenuSelect $menu
524
            }
525
        } else {
526
            $menu activate @$x,$y
527
            tkGenerateMenuSelect $menu
528
        }
529
    }
530
    if {($state & 0x1f00) != 0} {
531
        $menu postcascade active
532
    }
533
}
534
 
535
# tkMenuButtonDown --
536
# Handles button presses in menus.  There are a couple of tricky things
537
# here:
538
# 1. Change the posted cascade entry (if any) to match the mouse position.
539
# 2. If there is a posted menubutton, must grab to the menubutton;  this
540
#    overrrides the implicit grab on button press, so that the menu
541
#    button can track mouse motions over other menubuttons and change
542
#    the posted menu.
543
# 3. If there's no posted menubutton (e.g. because we're a torn-off menu
544
#    or one of its descendants) must grab to the top-level menu so that
545
#    we can track mouse motions across the entire menu hierarchy.
546
#
547
# Arguments:
548
# menu -                The menu window.
549
 
550
proc tkMenuButtonDown menu {
551
    global tkPriv
552
    global tcl_platform
553
    $menu postcascade active
554
    if {$tkPriv(postedMb) != ""} {
555
        grab -global $tkPriv(postedMb)
556
    } else {
557
        while {([$menu cget -type] == "normal")
558
                && ([winfo class [winfo parent $menu]] == "Menu")
559
                && [winfo ismapped [winfo parent $menu]]} {
560
            set menu [winfo parent $menu]
561
        }
562
 
563
        if {$tkPriv(menuBar) == {}} {
564
            set tkPriv(menuBar) $menu
565
            set tkPriv(cursor) [$menu cget -cursor]
566
            $menu configure -cursor arrow
567
        }
568
 
569
        # Don't update grab information if the grab window isn't changing.
570
        # Otherwise, we'll get an error when we unpost the menus and
571
        # restore the grab, since the old grab window will not be viewable
572
        # anymore.
573
 
574
        if {$menu != [grab current $menu]} {
575
            tkSaveGrabInfo $menu
576
        }
577
 
578
        # Must re-grab even if the grab window hasn't changed, in order
579
        # to release the implicit grab from the button press.
580
 
581
        if {$tcl_platform(platform) == "unix"} {
582
            grab -global $menu
583
        }
584
    }
585
}
586
 
587
# tkMenuLeave --
588
# This procedure is invoked to handle Leave events for a menu.  It
589
# deactivates everything unless the active element is a cascade element
590
# and the mouse is now over the submenu.
591
#
592
# Arguments:
593
# menu -                The menu window.
594
# rootx, rooty -        Root coordinates of mouse.
595
# state -               Modifier state.
596
 
597
proc tkMenuLeave {menu rootx rooty state} {
598
    global tkPriv
599
    set tkPriv(window) {}
600
    if {[$menu index active] == "none"} {
601
        return
602
    }
603
    if {([$menu type active] == "cascade")
604
            && ([winfo containing $rootx $rooty]
605
            == [$menu entrycget active -menu])} {
606
        return
607
    }
608
    $menu activate none
609
    tkGenerateMenuSelect $menu
610
}
611
 
612
# tkMenuInvoke --
613
# This procedure is invoked when button 1 is released over a menu.
614
# It invokes the appropriate menu action and unposts the menu if
615
# it came from a menubutton.
616
#
617
# Arguments:
618
# w -                   Name of the menu widget.
619
# buttonRelease -       1 means this procedure is called because of
620
#                       a button release;  0 means because of keystroke.
621
 
622
proc tkMenuInvoke {w buttonRelease} {
623
    global tkPriv
624
 
625
    if {$buttonRelease && ($tkPriv(window) == "")} {
626
        # Mouse was pressed over a menu without a menu button, then
627
        # dragged off the menu (possibly with a cascade posted) and
628
        # released.  Unpost everything and quit.
629
 
630
        $w postcascade none
631
        $w activate none
632
        event generate $w <<MenuSelect>>
633
        tkMenuUnpost $w
634
        return
635
    }
636
    if {[$w type active] == "cascade"} {
637
        $w postcascade active
638
        set menu [$w entrycget active -menu]
639
        tkMenuFirstEntry $menu
640
    } elseif {[$w type active] == "tearoff"} {
641
        tkMenuUnpost $w
642
        tkTearOffMenu $w
643
    } elseif {[$w cget -type] == "menubar"} {
644
        $w postcascade none
645
        $w activate none
646
        event generate $w <<MenuSelect>>
647
        tkMenuUnpost $w
648
    } else {
649
        tkMenuUnpost $w
650
        uplevel #0 [list $w invoke active]
651
    }
652
}
653
 
654
# tkMenuEscape --
655
# This procedure is invoked for the Cancel (or Escape) key.  It unposts
656
# the given menu and, if it is the top-level menu for a menu button,
657
# unposts the menu button as well.
658
#
659
# Arguments:
660
# menu -                Name of the menu window.
661
 
662
proc tkMenuEscape menu {
663
    set parent [winfo parent $menu]
664
    if {([winfo class $parent] != "Menu")} {
665
        tkMenuUnpost $menu
666
    } elseif {([$parent cget -type] == "menubar")} {
667
        tkMenuUnpost $menu
668
        tkRestoreOldGrab
669
    } else {
670
        tkMenuNextMenu $menu left
671
    }
672
}
673
 
674
# The following routines handle arrow keys. Arrow keys behave
675
# differently depending on whether the menu is a menu bar or not.
676
 
677
proc tkMenuUpArrow {menu} {
678
    if {[$menu cget -type] == "menubar"} {
679
        tkMenuNextMenu $menu left
680
    } else {
681
        tkMenuNextEntry $menu -1
682
    }
683
}
684
 
685
proc tkMenuDownArrow {menu} {
686
    if {[$menu cget -type] == "menubar"} {
687
        tkMenuNextMenu $menu right
688
    } else {
689
        tkMenuNextEntry $menu 1
690
    }
691
}
692
 
693
proc tkMenuLeftArrow {menu} {
694
    if {[$menu cget -type] == "menubar"} {
695
        tkMenuNextEntry $menu -1
696
    } else {
697
        tkMenuNextMenu $menu left
698
    }
699
}
700
 
701
proc tkMenuRightArrow {menu} {
702
    if {[$menu cget -type] == "menubar"} {
703
        tkMenuNextEntry $menu 1
704
    } else {
705
        tkMenuNextMenu $menu right
706
    }
707
}
708
 
709
# tkMenuNextMenu --
710
# This procedure is invoked to handle "left" and "right" traversal
711
# motions in menus.  It traverses to the next menu in a menu bar,
712
# or into or out of a cascaded menu.
713
#
714
# Arguments:
715
# menu -                The menu that received the keyboard
716
#                       event.
717
# direction -           Direction in which to move: "left" or "right"
718
 
719
proc tkMenuNextMenu {menu direction} {
720
    global tkPriv
721
 
722
    # First handle traversals into and out of cascaded menus.
723
 
724
    if {$direction == "right"} {
725
        set count 1
726
        set parent [winfo parent $menu]
727
        set class [winfo class $parent]
728
        if {[$menu type active] == "cascade"} {
729
            $menu postcascade active
730
            set m2 [$menu entrycget active -menu]
731
            if {$m2 != ""} {
732
                tkMenuFirstEntry $m2
733
            }
734
            return
735
        } else {
736
            set parent [winfo parent $menu]
737
            while {($parent != ".")} {
738
                if {([winfo class $parent] == "Menu")
739
                        && ([$parent cget -type] == "menubar")} {
740
                    tk_menuSetFocus $parent
741
                    tkMenuNextEntry $parent 1
742
                    return
743
                }
744
                set parent [winfo parent $parent]
745
            }
746
        }
747
    } else {
748
        set count -1
749
        set m2 [winfo parent $menu]
750
        if {[winfo class $m2] == "Menu"} {
751
            if {[$m2 cget -type] != "menubar"} {
752
                $menu activate none
753
                tkGenerateMenuSelect $menu
754
                tk_menuSetFocus $m2
755
 
756
                # This code unposts any posted submenu in the parent.
757
 
758
                set tmp [$m2 index active]
759
                $m2 activate none
760
                $m2 activate $tmp
761
                return
762
            }
763
        }
764
    }
765
 
766
    # Can't traverse into or out of a cascaded menu.  Go to the next
767
    # or previous menubutton, if that makes sense.
768
 
769
    set m2 [winfo parent $menu]
770
    if {[winfo class $m2] == "Menu"} {
771
        if {[$m2 cget -type] == "menubar"} {
772
            tk_menuSetFocus $m2
773
            tkMenuNextEntry $m2 -1
774
            return
775
        }
776
    }
777
 
778
    set w $tkPriv(postedMb)
779
    if {$w == ""} {
780
        return
781
    }
782
    set buttons [winfo children [winfo parent $w]]
783
    set length [llength $buttons]
784
    set i [expr {[lsearch -exact $buttons $w] + $count}]
785
    while 1 {
786
        while {$i < 0} {
787
            incr i $length
788
        }
789
        while {$i >= $length} {
790
            incr i -$length
791
        }
792
        set mb [lindex $buttons $i]
793
        if {([winfo class $mb] == "Menubutton")
794
                && ([$mb cget -state] != "disabled")
795
                && ([$mb cget -menu] != "")
796
                && ([[$mb cget -menu] index last] != "none")} {
797
            break
798
        }
799
        if {$mb == $w} {
800
            return
801
        }
802
        incr i $count
803
    }
804
    tkMbPost $mb
805
    tkMenuFirstEntry [$mb cget -menu]
806
}
807
 
808
# tkMenuNextEntry --
809
# Activate the next higher or lower entry in the posted menu,
810
# wrapping around at the ends.  Disabled entries are skipped.
811
#
812
# Arguments:
813
# menu -                        Menu window that received the keystroke.
814
# count -                       1 means go to the next lower entry,
815
#                               -1 means go to the next higher entry.
816
 
817
proc tkMenuNextEntry {menu count} {
818
    global tkPriv
819
 
820
    if {[$menu index last] == "none"} {
821
        return
822
    }
823
    set length [expr {[$menu index last]+1}]
824
    set quitAfter $length
825
    set active [$menu index active]
826
    if {$active == "none"} {
827
        set i 0
828
    } else {
829
        set i [expr {$active + $count}]
830
    }
831
    while 1 {
832
        if {$quitAfter <= 0} {
833
            # We've tried every entry in the menu.  Either there are
834
            # none, or they're all disabled.  Just give up.
835
 
836
            return
837
        }
838
        while {$i < 0} {
839
            incr i $length
840
        }
841
        while {$i >= $length} {
842
            incr i -$length
843
        }
844
        if {[catch {$menu entrycget $i -state} state] == 0} {
845
            if {$state != "disabled"} {
846
                break
847
            }
848
        }
849
        if {$i == $active} {
850
            return
851
        }
852
        incr i $count
853
        incr quitAfter -1
854
    }
855
    $menu activate $i
856
    tkGenerateMenuSelect $menu
857
    if {[$menu type $i] == "cascade"} {
858
        set cascade [$menu entrycget $i -menu]
859
        if {[string compare $cascade ""] != 0} {
860
            $menu postcascade $i
861
            tkMenuFirstEntry $cascade
862
        }
863
    }
864
}
865
 
866
# tkMenuFind --
867
# This procedure searches the entire window hierarchy under w for
868
# a menubutton that isn't disabled and whose underlined character
869
# is "char" or an entry in a menubar that isn't disabled and whose
870
# underlined character is "char".
871
# It returns the name of that window, if found, or an
872
# empty string if no matching window was found.  If "char" is an
873
# empty string then the procedure returns the name of the first
874
# menubutton found that isn't disabled.
875
#
876
# Arguments:
877
# w -                           Name of window where key was typed.
878
# char -                        Underlined character to search for;
879
#                               may be either upper or lower case, and
880
#                               will match either upper or lower case.
881
 
882
proc tkMenuFind {w char} {
883
    global tkPriv
884
    set char [string tolower $char]
885
    set windowlist [winfo child $w]
886
 
887
    foreach child $windowlist {
888
        switch [winfo class $child] {
889
            Menu {
890
                if {[$child cget -type] == "menubar"} {
891
                    if {$char == ""} {
892
                        return $child
893
                    }
894
                    set last [$child index last]
895
                    for {set i [$child cget -tearoff]} {$i <= $last} {incr i} {
896
                        if {[$child type $i] == "separator"} {
897
                            continue
898
                        }
899
                        set char2 [string index [$child entrycget $i -label] \
900
                                [$child entrycget $i -underline]]
901
                        if {([string compare $char [string tolower $char2]] \
902
                                == 0) || ($char == "")} {
903
                            if {[$child entrycget $i -state] != "disabled"} {
904
                                return $child
905
                            }
906
                        }
907
                    }
908
                }
909
            }
910
        }
911
    }
912
 
913
    foreach child $windowlist {
914
        switch [winfo class $child] {
915
            Menubutton {
916
                set char2 [string index [$child cget -text] \
917
                        [$child cget -underline]]
918
                if {([string compare $char [string tolower $char2]] == 0)
919
                        || ($char == "")} {
920
                    if {[$child cget -state] != "disabled"} {
921
                        return $child
922
                    }
923
                }
924
            }
925
 
926
            default {
927
                set match [tkMenuFind $child $char]
928
                if {$match != ""} {
929
                    return $match
930
                }
931
            }
932
        }
933
    }
934
    return {}
935
}
936
 
937
# tkTraverseToMenu --
938
# This procedure implements keyboard traversal of menus.  Given an
939
# ASCII character "char", it looks for a menubutton with that character
940
# underlined.  If one is found, it posts the menubutton's menu
941
#
942
# Arguments:
943
# w -                           Window in which the key was typed (selects
944
#                               a toplevel window).
945
# char -                        Character that selects a menu.  The case
946
#                               is ignored.  If an empty string, nothing
947
#                               happens.
948
 
949
proc tkTraverseToMenu {w char} {
950
    global tkPriv
951
    if {$char == ""} {
952
        return
953
    }
954
    while {[winfo class $w] == "Menu"} {
955
        if {([$w cget -type] != "menubar") && ($tkPriv(postedMb) == "")} {
956
            return
957
        }
958
        if {[$w cget -type] == "menubar"} {
959
            break
960
        }
961
        set w [winfo parent $w]
962
    }
963
    set w [tkMenuFind [winfo toplevel $w] $char]
964
    if {$w != ""} {
965
        if {[winfo class $w] == "Menu"} {
966
            tk_menuSetFocus $w
967
            set tkPriv(window) $w
968
            tkSaveGrabInfo $w
969
            grab -global $w
970
            tkTraverseWithinMenu $w $char
971
        } else {
972
            tkMbPost $w
973
            tkMenuFirstEntry [$w cget -menu]
974
        }
975
    }
976
}
977
 
978
# tkFirstMenu --
979
# This procedure traverses to the first menubutton in the toplevel
980
# for a given window, and posts that menubutton's menu.
981
#
982
# Arguments:
983
# w -                           Name of a window.  Selects which toplevel
984
#                               to search for menubuttons.
985
 
986
proc tkFirstMenu w {
987
    set w [tkMenuFind [winfo toplevel $w] ""]
988
    if {$w != ""} {
989
        if {[winfo class $w] == "Menu"} {
990
            tk_menuSetFocus $w
991
            set tkPriv(window) $w
992
            tkSaveGrabInfo $w
993
            grab -global $w
994
            tkMenuFirstEntry $w
995
        } else {
996
            tkMbPost $w
997
            tkMenuFirstEntry [$w cget -menu]
998
        }
999
    }
1000
}
1001
 
1002
# tkTraverseWithinMenu
1003
# This procedure implements keyboard traversal within a menu.  It
1004
# searches for an entry in the menu that has "char" underlined.  If
1005
# such an entry is found, it is invoked and the menu is unposted.
1006
#
1007
# Arguments:
1008
# w -                           The name of the menu widget.
1009
# char -                        The character to look for;  case is
1010
#                               ignored.  If the string is empty then
1011
#                               nothing happens.
1012
 
1013
proc tkTraverseWithinMenu {w char} {
1014
    if {$char == ""} {
1015
        return
1016
    }
1017
    set char [string tolower $char]
1018
    set last [$w index last]
1019
    if {$last == "none"} {
1020
        return
1021
    }
1022
    for {set i 0} {$i <= $last} {incr i} {
1023
        if {[catch {set char2 [string index \
1024
                [$w entrycget $i -label] \
1025
                [$w entrycget $i -underline]]}]} {
1026
            continue
1027
        }
1028
        if {[string compare $char [string tolower $char2]] == 0} {
1029
            if {[$w type $i] == "cascade"} {
1030
                $w activate $i
1031
                $w postcascade active
1032
                event generate $w <<MenuSelect>>
1033
                set m2 [$w entrycget $i -menu]
1034
                if {$m2 != ""} {
1035
                    tkMenuFirstEntry $m2
1036
                }
1037
            } else {
1038
                tkMenuUnpost $w
1039
                uplevel #0 [list $w invoke $i]
1040
            }
1041
            return
1042
        }
1043
    }
1044
}
1045
 
1046
# tkMenuFirstEntry --
1047
# Given a menu, this procedure finds the first entry that isn't
1048
# disabled or a tear-off or separator, and activates that entry.
1049
# However, if there is already an active entry in the menu (e.g.,
1050
# because of a previous call to tkPostOverPoint) then the active
1051
# entry isn't changed.  This procedure also sets the input focus
1052
# to the menu.
1053
#
1054
# Arguments:
1055
# menu -                Name of the menu window (possibly empty).
1056
 
1057
proc tkMenuFirstEntry menu {
1058
    if {$menu == ""} {
1059
        return
1060
    }
1061
    tk_menuSetFocus $menu
1062
    if {[$menu index active] != "none"} {
1063
        return
1064
    }
1065
    set last [$menu index last]
1066
    if {$last == "none"} {
1067
        return
1068
    }
1069
    for {set i 0} {$i <= $last} {incr i} {
1070
        if {([catch {set state [$menu entrycget $i -state]}] == 0)
1071
                && ($state != "disabled") && ([$menu type $i] != "tearoff")} {
1072
            $menu activate $i
1073
            tkGenerateMenuSelect $menu
1074
            if {[$menu type $i] == "cascade"} {
1075
                set cascade [$menu entrycget $i -menu]
1076
                if {[string compare $cascade ""] != 0} {
1077
                    $menu postcascade $i
1078
                    tkMenuFirstEntry $cascade
1079
                }
1080
            }
1081
            return
1082
        }
1083
    }
1084
}
1085
 
1086
# tkMenuFindName --
1087
# Given a menu and a text string, return the index of the menu entry
1088
# that displays the string as its label.  If there is no such entry,
1089
# return an empty string.  This procedure is tricky because some names
1090
# like "active" have a special meaning in menu commands, so we can't
1091
# always use the "index" widget command.
1092
#
1093
# Arguments:
1094
# menu -                Name of the menu widget.
1095
# s -                   String to look for.
1096
 
1097
proc tkMenuFindName {menu s} {
1098
    set i ""
1099
    if {![regexp {^active$|^last$|^none$|^[0-9]|^@} $s]} {
1100
        catch {set i [$menu index $s]}
1101
        return $i
1102
    }
1103
    set last [$menu index last]
1104
    if {$last == "none"} {
1105
        return
1106
    }
1107
    for {set i 0} {$i <= $last} {incr i} {
1108
        if {![catch {$menu entrycget $i -label} label]} {
1109
            if {$label == $s} {
1110
                return $i
1111
            }
1112
        }
1113
    }
1114
    return ""
1115
}
1116
 
1117
# tkPostOverPoint --
1118
# This procedure posts a given menu such that a given entry in the
1119
# menu is centered over a given point in the root window.  It also
1120
# activates the given entry.
1121
#
1122
# Arguments:
1123
# menu -                Menu to post.
1124
# x, y -                Root coordinates of point.
1125
# entry -               Index of entry within menu to center over (x,y).
1126
#                       If omitted or specified as {}, then the menu's
1127
#                       upper-left corner goes at (x,y).
1128
 
1129
proc tkPostOverPoint {menu x y {entry {}}}  {
1130
    global tcl_platform
1131
 
1132
    if {$entry != {}} {
1133
        if {$entry == [$menu index last]} {
1134
            incr y [expr {-([$menu yposition $entry] \
1135
                    + [winfo reqheight $menu])/2}]
1136
        } else {
1137
            incr y [expr {-([$menu yposition $entry] \
1138
                    + [$menu yposition [expr {$entry+1}]])/2}]
1139
        }
1140
        incr x [expr {-[winfo reqwidth $menu]/2}]
1141
    }
1142
    $menu post $x $y
1143
    if {($entry != {}) && ([$menu entrycget $entry -state] != "disabled")} {
1144
        $menu activate $entry
1145
        tkGenerateMenuSelect $menu
1146
    }
1147
}
1148
 
1149
# tkSaveGrabInfo --
1150
# Sets the variables tkPriv(oldGrab) and tkPriv(grabStatus) to record
1151
# the state of any existing grab on the w's display.
1152
#
1153
# Arguments:
1154
# w -                   Name of a window;  used to select the display
1155
#                       whose grab information is to be recorded.
1156
 
1157
proc tkSaveGrabInfo w {
1158
    global tkPriv
1159
    set tkPriv(oldGrab) [grab current $w]
1160
    if {$tkPriv(oldGrab) != ""} {
1161
        set tkPriv(grabStatus) [grab status $tkPriv(oldGrab)]
1162
    }
1163
}
1164
 
1165
# tkRestoreOldGrab --
1166
# Restores the grab to what it was before TkSaveGrabInfo was called.
1167
#
1168
 
1169
proc tkRestoreOldGrab {} {
1170
    global tkPriv
1171
 
1172
    if {$tkPriv(oldGrab) != ""} {
1173
 
1174
        # Be careful restoring the old grab, since it's window may not
1175
        # be visible anymore.
1176
 
1177
        catch {
1178
            if {$tkPriv(grabStatus) == "global"} {
1179
                grab set -global $tkPriv(oldGrab)
1180
            } else {
1181
                grab set $tkPriv(oldGrab)
1182
            }
1183
        }
1184
        set tkPriv(oldGrab) ""
1185
    }
1186
}
1187
 
1188
proc tk_menuSetFocus {menu} {
1189
    global tkPriv
1190
    if {![info exists tkPriv(focus)] || [string length $tkPriv(focus)] == 0} {
1191
        set tkPriv(focus) [focus]
1192
    }
1193
    focus $menu
1194
}
1195
 
1196
proc tkGenerateMenuSelect {menu} {
1197
    global tkPriv
1198
 
1199
    if {([string compare $tkPriv(activeMenu) $menu] == 0) \
1200
            && ([string compare $tkPriv(activeItem) [$menu index active]] \
1201
            == 0)} {
1202
        return
1203
    }
1204
 
1205
    set tkPriv(activeMenu) $menu
1206
    set tkPriv(activeItem) [$menu index active]
1207
    event generate $menu <<MenuSelect>>
1208
}
1209
 
1210
# tk_popup --
1211
# This procedure pops up a menu and sets things up for traversing
1212
# the menu and its submenus.
1213
#
1214
# Arguments:
1215
# menu -                Name of the menu to be popped up.
1216
# x, y -                Root coordinates at which to pop up the
1217
#                       menu.
1218
# entry -               Index of a menu entry to center over (x,y).
1219
#                       If omitted or specified as {}, then menu's
1220
#                       upper-left corner goes at (x,y).
1221
 
1222
proc tk_popup {menu x y {entry {}}} {
1223
    global tkPriv
1224
    global tcl_platform
1225
    if {($tkPriv(popup) != "") || ($tkPriv(postedMb) != "")} {
1226
        tkMenuUnpost {}
1227
    }
1228
    tkPostOverPoint $menu $x $y $entry
1229
    if {$tcl_platform(platform) == "unix"} {
1230
        tkSaveGrabInfo $menu
1231
        grab -global $menu
1232
        set tkPriv(popup) $menu
1233
        tk_menuSetFocus $menu
1234
    }
1235
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.