|
|
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: # Variables used by menu buttons: ! 30: # $tk_priv(posted@$screen) - keeps track of the menubutton whose menu is ! 31: # currently posted (or empty string, if none). ! 32: # $tk_priv(inMenuButton@$screen)- ! 33: # if non-null, identifies menu button ! 34: # containing mouse pointer. ! 35: # $tk_priv(relief@$screen) - keeps track of original relief of posted ! 36: # menu button, so it can be restored later. ! 37: # $tk_priv(dragging@$screen) - if non-null, identifies menu button whose ! 38: # menu is currently being dragged in a tear-off ! 39: # operation. ! 40: # $tk_priv(focus@$screen) - records old focus window so focus can be ! 41: # returned there after keyboard traversal ! 42: # to menu. ! 43: # ! 44: # Variables used by menus: ! 45: # $tk_priv(x@$screen) and $tk_priv(y@$screen) are used to keep ! 46: # track of the position of the mouse cursor in the menu window ! 47: # during dragging of tear-off menus. $tk_priv(window) keeps track ! 48: # of the menu containing the mouse, if any. ! 49: ! 50: proc tk_menus {w args} { ! 51: global tk_priv ! 52: ! 53: if {$args == ""} { ! 54: if [catch {set result [set tk_priv(menusFor$w)]}] { ! 55: return "" ! 56: } ! 57: return $result ! 58: } ! 59: ! 60: if {$args == "{}"} { ! 61: catch {unset tk_priv(menusFor$w)} ! 62: return "" ! 63: } ! 64: ! 65: append tk_priv(menusFor$w) " $args" ! 66: } ! 67: ! 68: # The procedure below is publically available. It takes any number of ! 69: # arguments taht are names of widgets or classes. It sets up bindings ! 70: # for the widgets or classes so that keyboard menu traversal is possible ! 71: # when the input focus is in those widgets or classes. ! 72: ! 73: proc tk_bindForTraversal args { ! 74: foreach w $args { ! 75: bind $w <Alt-KeyPress> {tk_traverseToMenu %W %A} ! 76: bind $w <F10> {tk_firstMenu %W} ! 77: } ! 78: } ! 79: ! 80: # The procedure below does all of the work of posting a menu (including ! 81: # unposting any other menu that might currently be posted). The "w" ! 82: # argument is the name of the menubutton for the menu to be posted. ! 83: # Note: if $w is disabled then the procedure does nothing. ! 84: ! 85: proc tk_mbPost {w} { ! 86: global tk_priv ! 87: if {[lindex [$w config -state] 4] == "disabled"} { ! 88: return ! 89: } ! 90: set screen [winfo screen $w] ! 91: if {![info exists tk_priv(posted@$screen)]} { ! 92: set tk_priv(posted@$screen) {} ! 93: } ! 94: if {![info exists tk_priv(focus@$screen)]} { ! 95: set tk_priv(focus@$screen) {} ! 96: } ! 97: set cur $tk_priv(posted@$screen) ! 98: if {$cur == $w} { ! 99: return ! 100: } ! 101: if {$cur != ""} {tk_mbUnpost $w} ! 102: set tk_priv(relief@$screen) [lindex [$w config -relief] 4] ! 103: $w config -relief raised ! 104: set tk_priv(cursor@$screen) [lindex [$w config -cursor] 4] ! 105: $w config -cursor arrow ! 106: $w post ! 107: catch {grab -global $w} ! 108: set tk_priv(posted@$screen) $w ! 109: if {$tk_priv(focus@$screen) == ""} { ! 110: set tk_priv(focus@$screen) [focus -query $w] ! 111: } ! 112: set menu [lindex [$w config -menu] 4] ! 113: focus $menu ! 114: } ! 115: ! 116: # The procedure below does all the work of unposting the menubutton that's ! 117: # currently posted. It takes no arguments. ! 118: ! 119: proc tk_mbUnpost {w} { ! 120: global tk_priv ! 121: set screen [winfo screen $w] ! 122: if {![info exists tk_priv(posted@$screen)]} { ! 123: set tk_priv(posted@$screen) {} ! 124: } ! 125: if {![info exists tk_priv(focus@$screen)]} { ! 126: set tk_priv(focus@$screen) {} ! 127: } ! 128: set mb $tk_priv(posted@$screen) ! 129: if {$mb != ""} { ! 130: $mb config -relief $tk_priv(relief@$screen) ! 131: $mb config -cursor $tk_priv(cursor@$screen) ! 132: $mb unpost ! 133: catch {grab -off $mb} ! 134: set menu [lindex [$mb config -menu] 4] ! 135: focus $tk_priv(focus@$screen) ! 136: set tk_priv(focus@$screen) "" ! 137: set tk_priv(posted@$screen) {} ! 138: } ! 139: } ! 140: ! 141: # The procedure below is invoked to implement keyboard traversal to ! 142: # a menu button. It takes two arguments: the name of a window where ! 143: # a keystroke originated, and the ascii character that was typed. ! 144: # This procedure finds a menu bar by looking upward for a top-level ! 145: # window, then looking for a window underneath that named "menu". ! 146: # Then it searches through all the subwindows of "menu" for a menubutton ! 147: # with an underlined character matching char. If one is found, it ! 148: # posts that menu. ! 149: ! 150: proc tk_traverseToMenu {w char} { ! 151: if {$char == ""} { ! 152: return ! 153: } ! 154: set char [string tolower $char] ! 155: ! 156: foreach mb [tk_getMenuButtons $w] { ! 157: if {[winfo class $mb] == "Menubutton"} { ! 158: set char2 [string index [lindex [$mb config -text] 4] \ ! 159: [lindex [$mb config -underline] 4]] ! 160: if {[string compare $char [string tolower $char2]] == 0} { ! 161: tk_mbPost $mb ! 162: [lindex [$mb config -menu] 4] activate 0 ! 163: return ! 164: } ! 165: } ! 166: } ! 167: } ! 168: ! 169: # The procedure below is used to implement keyboard traversal within ! 170: # the posted menu. It takes two arguments: the name of the menu to ! 171: # be traversed within, and an ASCII character. It searches for an ! 172: # entry in the menu that has that character underlined. If such an ! 173: # entry is found, it is invoked and the menu is unposted. ! 174: ! 175: proc tk_traverseWithinMenu {w char} { ! 176: if {$char == ""} { ! 177: return ! 178: } ! 179: set char [string tolower $char] ! 180: set last [$w index last] ! 181: for {set i 0} {$i <= $last} {incr i} { ! 182: if [catch {set char2 [string index \ ! 183: [lindex [$w entryconfig $i -label] 4] \ ! 184: [lindex [$w entryconfig $i -underline] 4]]}] { ! 185: continue ! 186: } ! 187: if {[string compare $char [string tolower $char2]] == 0} { ! 188: tk_mbUnpost $w ! 189: $w invoke $i ! 190: return ! 191: } ! 192: } ! 193: } ! 194: ! 195: # The procedure below takes a single argument, which is the name of ! 196: # a window. It returns a list containing path names for all of the ! 197: # menu buttons associated with that window's top-level window, or an ! 198: # empty list if there are none. ! 199: ! 200: proc tk_getMenuButtons {w} { ! 201: global tk_priv ! 202: set top [winfo toplevel $w] ! 203: if [catch {set buttons [set tk_priv(menusFor$top)]}] { ! 204: return "" ! 205: } ! 206: return $buttons ! 207: } ! 208: ! 209: # The procedure below is used to traverse to the next or previous ! 210: # menu in a menu bar. It takes one argument, which is a count of ! 211: # how many menu buttons forward or backward (if negative) to move. ! 212: # If there is no posted menu then this procedure has no effect. ! 213: ! 214: proc tk_nextMenu {w count} { ! 215: global tk_priv ! 216: set screen [winfo screen $w] ! 217: if {![info exists tk_priv(posted@$screen)]} { ! 218: set tk_priv(posted@$screen) {} ! 219: } ! 220: if {$tk_priv(posted@$screen) == ""} { ! 221: return ! 222: } ! 223: set buttons [tk_getMenuButtons $tk_priv(posted@$screen)] ! 224: set length [llength $buttons] ! 225: for {set i 0} 1 {incr i} { ! 226: if {$i >= $length} { ! 227: return ! 228: } ! 229: if {[lindex $buttons $i] == $tk_priv(posted@$screen)} { ! 230: break ! 231: } ! 232: } ! 233: incr i $count ! 234: while 1 { ! 235: while {$i < 0} { ! 236: incr i $length ! 237: } ! 238: while {$i >= $length} { ! 239: incr i -$length ! 240: } ! 241: set mb [lindex $buttons $i] ! 242: if {[lindex [$mb configure -state] 4] != "disabled"} { ! 243: break ! 244: } ! 245: incr i $count ! 246: } ! 247: tk_mbUnpost $w ! 248: tk_mbPost $mb ! 249: [lindex [$mb config -menu] 4] activate 0 ! 250: } ! 251: ! 252: # The procedure below is used to traverse to the next or previous entry ! 253: # in the posted menu. It takes one argument, which is 1 to go to the ! 254: # next entry or -1 to go to the previous entry. Disabled entries are ! 255: # skipped in this process. ! 256: ! 257: proc tk_nextMenuEntry {w count} { ! 258: global tk_priv ! 259: set screen [winfo screen $w] ! 260: if {![info exists tk_priv(posted@$screen)]} { ! 261: set tk_priv(posted@$screen) {} ! 262: } ! 263: if {$tk_priv(posted@$screen) == ""} { ! 264: return ! 265: } ! 266: set menu [lindex [$tk_priv(posted@$screen) config -menu] 4] ! 267: set length [expr [$menu index last]+1] ! 268: set i [$menu index active] ! 269: if {$i == "none"} { ! 270: set i 0 ! 271: } else { ! 272: incr i $count ! 273: } ! 274: while 1 { ! 275: while {$i < 0} { ! 276: incr i $length ! 277: } ! 278: while {$i >= $length} { ! 279: incr i -$length ! 280: } ! 281: if {[catch {$menu entryconfigure $i -state} state] == 0} { ! 282: if {[lindex $state 4] != "disabled"} { ! 283: break ! 284: } ! 285: } ! 286: incr i $count ! 287: } ! 288: $menu activate $i ! 289: } ! 290: ! 291: # The procedure below invokes the active entry in the posted menu, ! 292: # if there is one. Otherwise it does nothing. ! 293: ! 294: proc tk_invokeMenu {w} { ! 295: set i [$w index active] ! 296: if {$i != "none"} { ! 297: tk_mbUnpost $w ! 298: update idletasks ! 299: $w invoke $i ! 300: } ! 301: } ! 302: ! 303: # The procedure below is invoked to keyboard-traverse to the first ! 304: # menu for a given source window. The source window is passed as ! 305: # parameter. ! 306: ! 307: proc tk_firstMenu {w} { ! 308: set mb [lindex [tk_getMenuButtons $w] 0] ! 309: if {$mb != ""} { ! 310: tk_mbPost $mb ! 311: [lindex [$mb config -menu] 4] activate 0 ! 312: } ! 313: } ! 314: ! 315: # The procedure below is invoked when a button-1-down event is ! 316: # received by a menu button. If the mouse is in the menu button ! 317: # then it posts the button's menu. If the mouse isn't in the ! 318: # button's menu, then it deactivates any active entry in the menu. ! 319: # Remember, event-sharing can cause this procedure to be invoked ! 320: # for two different menu buttons on the same event. ! 321: ! 322: proc tk_mbButtonDown {w x y} { ! 323: global tk_priv ! 324: set screen [winfo screen $w] ! 325: if {![info exists tk_priv(inMenuButton@$screen)]} { ! 326: set tk_priv(inMenuButton@$screen) {} ! 327: } ! 328: if {![info exists tk_priv(posted@$screen)]} { ! 329: set tk_priv(posted@$screen) {} ! 330: } ! 331: if {[lindex [$w config -state] 4] == "disabled"} { ! 332: return ! 333: } ! 334: if {$tk_priv(inMenuButton@$screen) == $w} { ! 335: tk_mbPost $w ! 336: } ! 337: if {$tk_priv(posted@$screen) != ""} then { ! 338: set menu [lindex [$tk_priv(posted@$screen) config -menu] 4] ! 339: if {![info exists tk_priv(window@$screen)]} { ! 340: set tk_priv(window@$screen) {} ! 341: } ! 342: if {$tk_priv(window@$screen) != $menu} { ! 343: $menu activate none ! 344: } ! 345: } ! 346: } ! 347: ! 348: proc tk_mbButtonUp {w x y} { ! 349: global tk_priv ! 350: set screen [winfo screen $w] ! 351: if {![info exists tk_priv(inMenuButton@$screen)]} { ! 352: set tk_priv(inMenuButton@$screen) {} ! 353: } ! 354: if {![info exists tk_priv(posted@$screen)]} { ! 355: set tk_priv(posted@$screen) {} ! 356: } ! 357: if {($tk_priv(inMenuButton@$screen) != "") && ! 358: ($tk_priv(posted@$screen) != "")} { ! 359: [lindex [$tk_priv(posted@$screen) config -menu] 4] activate 0 ! 360: } else { ! 361: tk_mbUnpost $w ! 362: } ! 363: } ! 364: ! 365: proc tk_mbButtonEnter {w m} { ! 366: global tk_priv ! 367: set screen [winfo screen $w] ! 368: set tk_priv(inMenuButton@$screen) $w ! 369: if {[lindex [$w config -state] 4] != "disabled"} { ! 370: $w config -state active ! 371: } ! 372: } ! 373: ! 374: proc tk_mbButtonLeave {w} { ! 375: global tk_priv ! 376: set screen [winfo screen $w] ! 377: set tk_priv(inMenuButton@$screen) {} ! 378: if {[lindex [$w config -state] 4] != "disabled"} { ! 379: $w config -state normal ! 380: } ! 381: } ! 382: ! 383: # In the binding below, it's important to ignore grab-related entries ! 384: # and exits because they lag reality and can cause menus to chase ! 385: # their own tail, repeatedly posting and unposting. ! 386: ! 387: proc tk_mbButton1Enter {w m} { ! 388: global tk_priv ! 389: set screen [winfo screen $w] ! 390: set tk_priv(inMenuButton@$screen) $w ! 391: if {([lindex [$w config -state] 4] != "disabled") ! 392: && ("$m" != "NotifyGrab") && ("$m" != "NotifyUngrab")} { ! 393: $w config -state active ! 394: tk_mbPost $w ! 395: } ! 396: } ! 397: ! 398: ! 399: proc tk_mbButton2Down {w x y} { ! 400: global tk_priv ! 401: set screen [winfo screen $w] ! 402: if {![info exists tk_priv(inMenuButton@$screen)]} { ! 403: set tk_priv(inMenuButton@$screen) {} ! 404: } ! 405: if {![info exists tk_priv(posted@$screen)]} { ! 406: set tk_priv(posted@$screen) {} ! 407: } ! 408: if {($tk_priv(posted@$screen) == "") ! 409: && ([lindex [$w config -state] 4] != "disabled")} { ! 410: set tk_priv(dragging@$screen) $w ! 411: [lindex [$w config -menu] 4] post $x $y ! 412: } ! 413: } ! 414: ! 415: ! 416: proc tk_mbButton2Motion {w x y} { ! 417: global tk_priv ! 418: set screen [winfo screen $w] ! 419: if {![info exists tk_priv(dragging@$screen)]} { ! 420: set tk_priv(dragging@$screen) {} ! 421: } ! 422: if {$tk_priv(dragging@$screen) != ""} { ! 423: [lindex [$tk_priv(dragging@$screen) config -menu] 4] post $x $y ! 424: } ! 425: } ! 426: ! 427: proc tk_mbButton2Up {w x y} { ! 428: global tk_priv ! 429: set screen [winfo screen $w] ! 430: set tk_priv(dragging@$screen) "" ! 431: } ! 432: ! 433: ! 434: proc tk_menuEnter {w y} { ! 435: global tk_priv ! 436: set screen [winfo screen $w] ! 437: set tk_priv(window@$screen) $w ! 438: $w activate @$y ! 439: } ! 440: ! 441: proc tk_menuLeave {w} { ! 442: global tk_priv ! 443: set screen [winfo screen $w] ! 444: set tk_priv(window@$screen) {} ! 445: $w activate none ! 446: } ! 447: ! 448: proc tk_menuMotion {w y} { ! 449: global tk_priv ! 450: set screen [winfo screen $w] ! 451: if {![info exists tk_priv(window@$screen)]} { ! 452: set tk_priv(window@$screen) {} ! 453: } ! 454: if {$tk_priv(window@$screen) != ""} { ! 455: $w activate @$y ! 456: } ! 457: } ! 458: ! 459: proc tk_menuUp {w y} { ! 460: tk_menuMotion $w $y ! 461: tk_invokeMenu $w ! 462: } ! 463: ! 464: proc tk_menu2Down {w x y} { ! 465: global tk_priv ! 466: set screen [winfo screen $w] ! 467: set tk_priv(x@$screen) $x ! 468: set tk_priv(y@$screen) $y ! 469: } ! 470: ! 471: proc tk_menu2Motion {w x y} { ! 472: global tk_priv ! 473: set screen [winfo screen $w] ! 474: if {$tk_priv(posted@$screen) == ""} { ! 475: $w post [expr $x-$tk_priv(x@$screen)] [expr $y-$tk_priv(y@$screen)] ! 476: } ! 477: } ! 478:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.