menu.tcl 38 KB

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