menu.tcl 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  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 by 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 <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 <ButtonPress> {
  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 <KeyPress> {
  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-KeyPress> {
  171. tk::TraverseToMenu %W %A
  172. }
  173. bind all <F10> {
  174. tk::FirstMenu %W
  175. }
  176. } else {
  177. bind Menubutton <Alt-KeyPress> {
  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. # If this looks like an option menubutton then post the menu so
  264. # that the current entry is on top of the mouse. Otherwise post
  265. # the menu just below the menubutton, as for a pull-down.
  266. update idletasks
  267. if {[catch {
  268. switch [$w cget -direction] {
  269. above {
  270. set x [winfo rootx $w]
  271. set y [expr {[winfo rooty $w] - [winfo reqheight $menu]}]
  272. # if we go offscreen to the top, show as 'below'
  273. if {$y < [winfo vrooty $w]} {
  274. set y [expr {[winfo vrooty $w] + [winfo rooty $w] + [winfo reqheight $w]}]
  275. }
  276. PostOverPoint $menu $x $y
  277. }
  278. below {
  279. set x [winfo rootx $w]
  280. set y [expr {[winfo rooty $w] + [winfo height $w]}]
  281. # if we go offscreen to the bottom, show as 'above'
  282. set mh [winfo reqheight $menu]
  283. if {($y + $mh) > ([winfo vrooty $w] + [winfo vrootheight $w])} {
  284. set y [expr {[winfo vrooty $w] + [winfo vrootheight $w] + [winfo rooty $w] - $mh}]
  285. }
  286. PostOverPoint $menu $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 [MenuFindName $menu [$w cget -text]]
  292. if {$entry eq ""} {
  293. set entry 0
  294. }
  295. if {[$w cget -indicatoron]} {
  296. if {$entry == [$menu index last]} {
  297. incr y [expr {-([$menu yposition $entry] \
  298. + [winfo reqheight $menu])/2}]
  299. } else {
  300. incr y [expr {-([$menu yposition $entry] \
  301. + [$menu yposition [expr {$entry+1}]])/2}]
  302. }
  303. }
  304. PostOverPoint $menu $x $y
  305. if {$entry ne "" \
  306. && [$menu entrycget $entry -state] ne "disabled"} {
  307. $menu activate $entry
  308. GenerateMenuSelect $menu
  309. }
  310. }
  311. right {
  312. set x [expr {[winfo rootx $w] + [winfo width $w]}]
  313. set y [expr {(2 * [winfo rooty $w] + [winfo height $w]) / 2}]
  314. set entry [MenuFindName $menu [$w cget -text]]
  315. if {$entry eq ""} {
  316. set entry 0
  317. }
  318. if {[$w cget -indicatoron]} {
  319. if {$entry == [$menu index last]} {
  320. incr y [expr {-([$menu yposition $entry] \
  321. + [winfo reqheight $menu])/2}]
  322. } else {
  323. incr y [expr {-([$menu yposition $entry] \
  324. + [$menu yposition [expr {$entry+1}]])/2}]
  325. }
  326. }
  327. PostOverPoint $menu $x $y
  328. if {$entry ne "" \
  329. && [$menu entrycget $entry -state] ne "disabled"} {
  330. $menu activate $entry
  331. GenerateMenuSelect $menu
  332. }
  333. }
  334. default {
  335. if {[$w cget -indicatoron]} {
  336. if {$y eq ""} {
  337. set x [expr {[winfo rootx $w] + [winfo width $w]/2}]
  338. set y [expr {[winfo rooty $w] + [winfo height $w]/2}]
  339. }
  340. PostOverPoint $menu $x $y [MenuFindName $menu [$w cget -text]]
  341. } else {
  342. PostOverPoint $menu [winfo rootx $w] [expr {[winfo rooty $w]+[winfo height $w]}]
  343. }
  344. }
  345. }
  346. } msg opt]} {
  347. # Error posting menu (e.g. bogus -postcommand). Unpost it and
  348. # reflect the error.
  349. MenuUnpost {}
  350. return -options $opt $msg
  351. }
  352. set Priv(tearoff) $tearoff
  353. if {$tearoff != 0} {
  354. focus $menu
  355. if {[winfo viewable $w]} {
  356. SaveGrabInfo $w
  357. grab -global $w
  358. }
  359. }
  360. }
  361. # ::tk::MenuUnpost --
  362. # This procedure unposts a given menu, plus all of its ancestors up
  363. # to (and including) a menubutton, if any. It also restores various
  364. # values to what they were before the menu was posted, and releases
  365. # a grab if there's a menubutton involved. Special notes:
  366. # 1. It's important to unpost all menus before releasing the grab, so
  367. # that any Enter-Leave events (e.g. from menu back to main
  368. # application) have mode NotifyGrab.
  369. # 2. Be sure to enclose various groups of commands in "catch" so that
  370. # the procedure will complete even if the menubutton or the menu
  371. # or the grab window has been deleted.
  372. #
  373. # Arguments:
  374. # menu - Name of a menu to unpost. Ignored if there
  375. # is a posted menubutton.
  376. proc ::tk::MenuUnpost menu {
  377. variable ::tk::Priv
  378. set mb $Priv(postedMb)
  379. # Restore focus right away (otherwise X will take focus away when
  380. # the menu is unmapped and under some window managers (e.g. olvwm)
  381. # we'll lose the focus completely).
  382. catch {focus $Priv(focus)}
  383. set Priv(focus) ""
  384. # Unpost menu(s) and restore some stuff that's dependent on
  385. # what was posted.
  386. after cancel [array get Priv menuActivatedTimer]
  387. unset -nocomplain Priv(menuActivated)
  388. after cancel [array get Priv menuDeactivatedTimer]
  389. unset -nocomplain Priv(menuDeactivated)
  390. catch {
  391. if {$mb ne ""} {
  392. set menu [$mb cget -menu]
  393. $menu unpost
  394. set Priv(postedMb) {}
  395. if {$::tk_strictMotif} {
  396. $mb configure -cursor $Priv(cursor)
  397. }
  398. if {[tk windowingsystem] ne "aqua"} {
  399. $mb configure -relief $Priv(relief)
  400. } else {
  401. $mb configure -state normal
  402. }
  403. } elseif {$Priv(popup) ne ""} {
  404. $Priv(popup) unpost
  405. set Priv(popup) {}
  406. } elseif {[$menu cget -type] ne "menubar" && [$menu cget -type] ne "tearoff"} {
  407. # We're in a cascaded sub-menu from a torn-off menu or popup.
  408. # Unpost all the menus up to the toplevel one (but not
  409. # including the top-level torn-off one) and deactivate the
  410. # top-level torn off menu if there is one.
  411. while {1} {
  412. set parent [winfo parent $menu]
  413. if {[winfo class $parent] ne "Menu" || ![winfo ismapped $parent]} {
  414. break
  415. }
  416. $parent activate none
  417. $parent postcascade none
  418. GenerateMenuSelect $parent
  419. set type [$parent cget -type]
  420. if {$type eq "menubar" || $type eq "tearoff"} {
  421. break
  422. }
  423. set menu $parent
  424. }
  425. if {[$menu cget -type] ne "menubar"} {
  426. $menu unpost
  427. }
  428. }
  429. }
  430. if {($Priv(tearoff) != 0) || $Priv(menuBar) ne ""} {
  431. # Release grab, if any, and restore the previous grab, if there
  432. # was one.
  433. if {$menu ne ""} {
  434. set grab [grab current $menu]
  435. if {$grab ne ""} {
  436. grab release $grab
  437. }
  438. }
  439. RestoreOldGrab
  440. if {$Priv(menuBar) ne ""} {
  441. if {$::tk_strictMotif} {
  442. $Priv(menuBar) configure -cursor $Priv(cursor)
  443. }
  444. set Priv(menuBar) {}
  445. }
  446. if {[tk windowingsystem] ne "x11"} {
  447. set Priv(tearoff) 0
  448. }
  449. }
  450. }
  451. # ::tk::MbMotion --
  452. # This procedure handles mouse motion events inside menubuttons, and
  453. # also outside menubuttons when a menubutton has a grab (e.g. when a
  454. # menu selection operation is in progress).
  455. #
  456. # Arguments:
  457. # w - The name of the menubutton widget.
  458. # upDown - "down" means button 1 is pressed, "up" means
  459. # it isn't.
  460. # rootx, rooty - Coordinates of mouse, in (virtual?) root window.
  461. proc ::tk::MbMotion {w upDown rootx rooty} {
  462. variable ::tk::Priv
  463. if {$Priv(inMenubutton) eq $w} {
  464. return
  465. }
  466. set new [winfo containing $rootx $rooty]
  467. if {$new ne $Priv(inMenubutton) \
  468. && ($new eq "" || [winfo toplevel $new] eq [winfo toplevel $w])} {
  469. if {$Priv(inMenubutton) ne ""} {
  470. MbLeave $Priv(inMenubutton)
  471. }
  472. if {$new ne "" \
  473. && [winfo class $new] eq "Menubutton" \
  474. && ([$new cget -indicatoron] == 0) \
  475. && ([$w cget -indicatoron] == 0)} {
  476. if {$upDown eq "down"} {
  477. MbPost $new $rootx $rooty
  478. } else {
  479. MbEnter $new
  480. }
  481. }
  482. }
  483. }
  484. # ::tk::MbButtonUp --
  485. # This procedure is invoked to handle button 1 releases for menubuttons.
  486. # If the release happens inside the menubutton then leave its menu
  487. # posted with element 0 activated. Otherwise, unpost the menu.
  488. #
  489. # Arguments:
  490. # w - The name of the menubutton widget.
  491. proc ::tk::MbButtonUp w {
  492. variable ::tk::Priv
  493. set menu [$w cget -menu]
  494. set tearoff [expr {[tk windowingsystem] eq "x11" || \
  495. ($menu ne "" && [$menu cget -type] eq "tearoff")}]
  496. if {($tearoff != 0) && $Priv(postedMb) eq $w \
  497. && $Priv(inMenubutton) eq $w} {
  498. MenuFirstEntry [$Priv(postedMb) cget -menu]
  499. } else {
  500. MenuUnpost {}
  501. }
  502. }
  503. # ::tk::MenuMotion --
  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. proc ::tk::MenuMotion {menu x y state} {
  516. variable ::tk::Priv
  517. if {$menu eq $Priv(window)} {
  518. set activeindex [$menu index active]
  519. if {[$menu cget -type] eq "menubar"} {
  520. if {[info exists Priv(focus)] && $menu ne $Priv(focus)} {
  521. $menu activate @$x,$y
  522. GenerateMenuSelect $menu
  523. }
  524. } else {
  525. $menu activate @$x,$y
  526. GenerateMenuSelect $menu
  527. }
  528. set index [$menu index @$x,$y]
  529. if {[info exists Priv(menuActivated)] \
  530. && $index ne "none" \
  531. && $index ne $activeindex} {
  532. set mode [option get $menu clickToFocus ClickToFocus]
  533. if {[string is false $mode]} {
  534. set delay [expr {[$menu cget -type] eq "menubar" ? 0 : 50}]
  535. if {[$menu type $index] eq "cascade"} {
  536. set Priv(menuActivatedTimer) \
  537. [after $delay [list $menu postcascade active]]
  538. } else {
  539. set Priv(menuDeactivatedTimer) \
  540. [after $delay [list $menu postcascade none]]
  541. }
  542. }
  543. }
  544. }
  545. }
  546. # ::tk::MenuButtonDown --
  547. # Handles button presses in menus. There are a couple of tricky things
  548. # here:
  549. # 1. Change the posted cascade entry (if any) to match the mouse position.
  550. # 2. If there is a posted menubutton, must grab to the menubutton; this
  551. # overrrides the implicit grab on button press, so that the menu
  552. # button can track mouse motions over other menubuttons and change
  553. # the posted menu.
  554. # 3. If there's no posted menubutton (e.g. because we're a torn-off menu
  555. # or one of its descendants) must grab to the top-level menu so that
  556. # we can track mouse motions across the entire menu hierarchy.
  557. #
  558. # Arguments:
  559. # menu - The menu window.
  560. proc ::tk::MenuButtonDown menu {
  561. variable ::tk::Priv
  562. if {![winfo viewable $menu]} {
  563. return
  564. }
  565. if {[$menu index active] eq "none"} {
  566. if {[$menu cget -type] ne "menubar" } {
  567. set Priv(window) {}
  568. }
  569. return
  570. }
  571. $menu postcascade active
  572. if {$Priv(postedMb) ne "" && [winfo viewable $Priv(postedMb)]} {
  573. grab -global $Priv(postedMb)
  574. } else {
  575. while {[$menu cget -type] eq "normal" \
  576. && [winfo class [winfo parent $menu]] eq "Menu" \
  577. && [winfo ismapped [winfo parent $menu]]} {
  578. set menu [winfo parent $menu]
  579. }
  580. if {$Priv(menuBar) eq {}} {
  581. set Priv(menuBar) $menu
  582. if {$::tk_strictMotif} {
  583. set Priv(cursor) [$menu cget -cursor]
  584. $menu configure -cursor arrow
  585. }
  586. if {[$menu type active] eq "cascade"} {
  587. set Priv(menuActivated) 1
  588. }
  589. }
  590. # Don't update grab information if the grab window isn't changing.
  591. # Otherwise, we'll get an error when we unpost the menus and
  592. # restore the grab, since the old grab window will not be viewable
  593. # anymore.
  594. if {$menu ne [grab current $menu]} {
  595. SaveGrabInfo $menu
  596. }
  597. # Must re-grab even if the grab window hasn't changed, in order
  598. # to release the implicit grab from the button press.
  599. if {[tk windowingsystem] eq "x11"} {
  600. grab -global $menu
  601. }
  602. }
  603. }
  604. # ::tk::MenuLeave --
  605. # This procedure is invoked to handle Leave events for a menu. It
  606. # deactivates everything unless the active element is a cascade element
  607. # and the mouse is now over the submenu.
  608. #
  609. # Arguments:
  610. # menu - The menu window.
  611. # rootx, rooty - Root coordinates of mouse.
  612. # state - Modifier state.
  613. proc ::tk::MenuLeave {menu rootx rooty state} {
  614. variable ::tk::Priv
  615. set Priv(window) {}
  616. if {[$menu index active] eq "none"} {
  617. return
  618. }
  619. if {[$menu type active] eq "cascade" \
  620. && [winfo containing $rootx $rooty] eq \
  621. [$menu entrycget active -menu]} {
  622. return
  623. }
  624. $menu activate none
  625. GenerateMenuSelect $menu
  626. }
  627. # ::tk::MenuInvoke --
  628. # This procedure is invoked when button 1 is released over a menu.
  629. # It invokes the appropriate menu action and unposts the menu if
  630. # it came from a menubutton.
  631. #
  632. # Arguments:
  633. # w - Name of the menu widget.
  634. # buttonRelease - 1 means this procedure is called because of
  635. # a button release; 0 means because of keystroke.
  636. proc ::tk::MenuInvoke {w buttonRelease} {
  637. variable ::tk::Priv
  638. if {$buttonRelease && $Priv(window) eq ""} {
  639. # Mouse was pressed over a menu without a menu button, then
  640. # dragged off the menu (possibly with a cascade posted) and
  641. # released. Unpost everything and quit.
  642. $w postcascade none
  643. $w activate none
  644. event generate $w <<MenuSelect>>
  645. MenuUnpost $w
  646. return
  647. }
  648. if {[$w type active] eq "cascade"} {
  649. $w postcascade active
  650. set menu [$w entrycget active -menu]
  651. MenuFirstEntry $menu
  652. } elseif {[$w type active] eq "tearoff"} {
  653. ::tk::TearOffMenu $w
  654. MenuUnpost $w
  655. } elseif {[$w cget -type] eq "menubar"} {
  656. $w postcascade none
  657. set active [$w index active]
  658. set isCascade [string equal [$w type $active] "cascade"]
  659. # Only de-activate the active item if it's a cascade; this prevents
  660. # the annoying "activation flicker" you otherwise get with
  661. # checkbuttons/commands/etc. on menubars
  662. if { $isCascade } {
  663. $w activate none
  664. event generate $w <<MenuSelect>>
  665. }
  666. MenuUnpost $w
  667. # If the active item is not a cascade, invoke it. This enables
  668. # the use of checkbuttons/commands/etc. on menubars (which is legal,
  669. # but not recommended)
  670. if { !$isCascade } {
  671. uplevel #0 [list $w invoke $active]
  672. }
  673. } else {
  674. set active [$w index active]
  675. if {$Priv(popup) eq "" || $active ne "none"} {
  676. MenuUnpost $w
  677. }
  678. uplevel #0 [list $w invoke active]
  679. }
  680. }
  681. # ::tk::MenuEscape --
  682. # This procedure is invoked for the Cancel (or Escape) key. It unposts
  683. # the given menu and, if it is the top-level menu for a menu button,
  684. # unposts the menu button as well.
  685. #
  686. # Arguments:
  687. # menu - Name of the menu window.
  688. proc ::tk::MenuEscape menu {
  689. set parent [winfo parent $menu]
  690. if {[winfo class $parent] ne "Menu"} {
  691. MenuUnpost $menu
  692. } elseif {[$parent cget -type] eq "menubar"} {
  693. MenuUnpost $menu
  694. RestoreOldGrab
  695. } else {
  696. MenuNextMenu $menu left
  697. }
  698. }
  699. # The following routines handle arrow keys. Arrow keys behave
  700. # differently depending on whether the menu is a menu bar or not.
  701. proc ::tk::MenuUpArrow {menu} {
  702. if {[$menu cget -type] eq "menubar"} {
  703. MenuNextMenu $menu left
  704. } else {
  705. MenuNextEntry $menu -1
  706. }
  707. }
  708. proc ::tk::MenuDownArrow {menu} {
  709. if {[$menu cget -type] eq "menubar"} {
  710. MenuNextMenu $menu right
  711. } else {
  712. MenuNextEntry $menu 1
  713. }
  714. }
  715. proc ::tk::MenuLeftArrow {menu} {
  716. if {[$menu cget -type] eq "menubar"} {
  717. MenuNextEntry $menu -1
  718. } else {
  719. MenuNextMenu $menu left
  720. }
  721. }
  722. proc ::tk::MenuRightArrow {menu} {
  723. if {[$menu cget -type] eq "menubar"} {
  724. MenuNextEntry $menu 1
  725. } else {
  726. MenuNextMenu $menu right
  727. }
  728. }
  729. # ::tk::MenuNextMenu --
  730. # This procedure is invoked to handle "left" and "right" traversal
  731. # motions in menus. It traverses to the next menu in a menu bar,
  732. # or into or out of a cascaded menu.
  733. #
  734. # Arguments:
  735. # menu - The menu that received the keyboard
  736. # event.
  737. # direction - Direction in which to move: "left" or "right"
  738. proc ::tk::MenuNextMenu {menu direction} {
  739. variable ::tk::Priv
  740. # First handle traversals into and out of cascaded menus.
  741. if {$direction eq "right"} {
  742. set count 1
  743. set parent [winfo parent $menu]
  744. set class [winfo class $parent]
  745. if {[$menu type active] eq "cascade"} {
  746. $menu postcascade active
  747. set m2 [$menu entrycget active -menu]
  748. if {$m2 ne ""} {
  749. MenuFirstEntry $m2
  750. }
  751. return
  752. } else {
  753. set parent [winfo parent $menu]
  754. while {$parent ne "."} {
  755. if {[winfo class $parent] eq "Menu" \
  756. && [$parent cget -type] eq "menubar"} {
  757. tk_menuSetFocus $parent
  758. MenuNextEntry $parent 1
  759. return
  760. }
  761. set parent [winfo parent $parent]
  762. }
  763. }
  764. } else {
  765. set count -1
  766. set m2 [winfo parent $menu]
  767. if {[winfo class $m2] eq "Menu"} {
  768. $menu activate none
  769. GenerateMenuSelect $menu
  770. tk_menuSetFocus $m2
  771. $m2 postcascade none
  772. if {[$m2 cget -type] ne "menubar"} {
  773. return
  774. }
  775. }
  776. }
  777. # Can't traverse into or out of a cascaded menu. Go to the next
  778. # or previous menubutton, if that makes sense.
  779. set m2 [winfo parent $menu]
  780. if {[winfo class $m2] eq "Menu" && [$m2 cget -type] eq "menubar"} {
  781. tk_menuSetFocus $m2
  782. MenuNextEntry $m2 -1
  783. return
  784. }
  785. set w $Priv(postedMb)
  786. if {$w eq ""} {
  787. return
  788. }
  789. set buttons [winfo children [winfo parent $w]]
  790. set length [llength $buttons]
  791. set i [expr {[lsearch -exact $buttons $w] + $count}]
  792. while {1} {
  793. while {$i < 0} {
  794. incr i $length
  795. }
  796. while {$i >= $length} {
  797. incr i -$length
  798. }
  799. set mb [lindex $buttons $i]
  800. if {[winfo class $mb] eq "Menubutton" \
  801. && [$mb cget -state] ne "disabled" \
  802. && [$mb cget -menu] ne "" \
  803. && [[$mb cget -menu] index last] ne "none"} {
  804. break
  805. }
  806. if {$mb eq $w} {
  807. return
  808. }
  809. incr i $count
  810. }
  811. MbPost $mb
  812. MenuFirstEntry [$mb cget -menu]
  813. }
  814. # ::tk::MenuNextEntry --
  815. # Activate the next higher or lower entry in the posted menu,
  816. # wrapping around at the ends. Disabled entries are skipped.
  817. #
  818. # Arguments:
  819. # menu - Menu window that received the keystroke.
  820. # count - 1 means go to the next lower entry,
  821. # -1 means go to the next higher entry.
  822. proc ::tk::MenuNextEntry {menu count} {
  823. if {[$menu index last] eq "none"} {
  824. return
  825. }
  826. set length [expr {[$menu index last]+1}]
  827. set quitAfter $length
  828. set active [$menu index active]
  829. if {$active eq "none"} {
  830. set i 0
  831. } else {
  832. set i [expr {$active + $count}]
  833. }
  834. while {1} {
  835. if {$quitAfter <= 0} {
  836. # We've tried every entry in the menu. Either there are
  837. # none, or they're all disabled. Just give up.
  838. return
  839. }
  840. while {$i < 0} {
  841. incr i $length
  842. }
  843. while {$i >= $length} {
  844. incr i -$length
  845. }
  846. if {[catch {$menu entrycget $i -state} state] == 0} {
  847. if {$state ne "disabled" && \
  848. ($i!=0 || [$menu cget -type] ne "tearoff" \
  849. || [$menu type 0] ne "tearoff")} {
  850. break
  851. }
  852. }
  853. if {$i == $active} {
  854. return
  855. }
  856. incr i $count
  857. incr quitAfter -1
  858. }
  859. $menu activate $i
  860. GenerateMenuSelect $menu
  861. if {[$menu type $i] eq "cascade" && [$menu cget -type] eq "menubar"} {
  862. set cascade [$menu entrycget $i -menu]
  863. if {$cascade ne ""} {
  864. # Here we auto-post a cascade. This is necessary when
  865. # we traverse left/right in the menubar, but undesirable when
  866. # we traverse up/down in a menu.
  867. $menu postcascade $i
  868. MenuFirstEntry $cascade
  869. }
  870. }
  871. }
  872. # ::tk::MenuFind --
  873. # This procedure searches the entire window hierarchy under w for
  874. # a menubutton that isn't disabled and whose underlined character
  875. # is "char" or an entry in a menubar that isn't disabled and whose
  876. # underlined character is "char".
  877. # It returns the name of that window, if found, or an
  878. # empty string if no matching window was found. If "char" is an
  879. # empty string then the procedure returns the name of the first
  880. # menubutton found that isn't disabled.
  881. #
  882. # Arguments:
  883. # w - Name of window where key was typed.
  884. # char - Underlined character to search for;
  885. # may be either upper or lower case, and
  886. # will match either upper or lower case.
  887. proc ::tk::MenuFind {w char} {
  888. set char [string tolower $char]
  889. set windowlist [winfo child $w]
  890. foreach child $windowlist {
  891. # Don't descend into other toplevels.
  892. if {[winfo toplevel $w] ne [winfo toplevel $child]} {
  893. continue
  894. }
  895. if {[winfo class $child] eq "Menu" && \
  896. [$child cget -type] eq "menubar"} {
  897. if {$char eq ""} {
  898. return $child
  899. }
  900. set last [$child index last]
  901. for {set i [$child cget -tearoff]} {$i <= $last} {incr i} {
  902. if {[$child type $i] eq "separator"} {
  903. continue
  904. }
  905. set char2 [string index [$child entrycget $i -label] \
  906. [$child entrycget $i -underline]]
  907. if {$char eq [string tolower $char2] || $char eq ""} {
  908. if {[$child entrycget $i -state] ne "disabled"} {
  909. return $child
  910. }
  911. }
  912. }
  913. }
  914. }
  915. foreach child $windowlist {
  916. # Don't descend into other toplevels.
  917. if {[winfo toplevel $w] ne [winfo toplevel $child]} {
  918. continue
  919. }
  920. switch -- [winfo class $child] {
  921. Menubutton {
  922. set char2 [string index [$child cget -text] \
  923. [$child cget -underline]]
  924. if {$char eq [string tolower $char2] || $char eq ""} {
  925. if {[$child cget -state] ne "disabled"} {
  926. return $child
  927. }
  928. }
  929. }
  930. default {
  931. set match [MenuFind $child $char]
  932. if {$match ne ""} {
  933. return $match
  934. }
  935. }
  936. }
  937. }
  938. return {}
  939. }
  940. # ::tk::TraverseToMenu --
  941. # This procedure implements keyboard traversal of menus. Given an
  942. # ASCII character "char", it looks for a menubutton with that character
  943. # underlined. If one is found, it posts the menubutton's menu
  944. #
  945. # Arguments:
  946. # w - Window in which the key was typed (selects
  947. # a toplevel window).
  948. # char - Character that selects a menu. The case
  949. # is ignored. If an empty string, nothing
  950. # happens.
  951. proc ::tk::TraverseToMenu {w char} {
  952. variable ::tk::Priv
  953. if {![winfo exists $w] || $char eq ""} {
  954. return
  955. }
  956. while {[winfo class $w] eq "Menu"} {
  957. if {[$w cget -type] eq "menubar"} {
  958. break
  959. } elseif {$Priv(postedMb) eq ""} {
  960. return
  961. }
  962. set w [winfo parent $w]
  963. }
  964. set w [MenuFind [winfo toplevel $w] $char]
  965. if {$w ne ""} {
  966. if {[winfo class $w] eq "Menu"} {
  967. tk_menuSetFocus $w
  968. set Priv(window) $w
  969. SaveGrabInfo $w
  970. grab -global $w
  971. TraverseWithinMenu $w $char
  972. } else {
  973. MbPost $w
  974. MenuFirstEntry [$w cget -menu]
  975. }
  976. }
  977. }
  978. # ::tk::FirstMenu --
  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. proc ::tk::FirstMenu w {
  986. variable ::tk::Priv
  987. set w [MenuFind [winfo toplevel $w] ""]
  988. if {$w ne ""} {
  989. if {[winfo class $w] eq "Menu"} {
  990. tk_menuSetFocus $w
  991. set Priv(window) $w
  992. SaveGrabInfo $w
  993. grab -global $w
  994. MenuFirstEntry $w
  995. } else {
  996. MbPost $w
  997. MenuFirstEntry [$w cget -menu]
  998. }
  999. }
  1000. }
  1001. # ::tk::TraverseWithinMenu
  1002. # This procedure implements keyboard traversal within a menu. It
  1003. # searches for an entry in the menu that has "char" underlined. If
  1004. # such an entry is found, it is invoked and the menu is unposted.
  1005. #
  1006. # Arguments:
  1007. # w - The name of the menu widget.
  1008. # char - The character to look for; case is
  1009. # ignored. If the string is empty then
  1010. # nothing happens.
  1011. proc ::tk::TraverseWithinMenu {w char} {
  1012. if {$char eq ""} {
  1013. return
  1014. }
  1015. set char [string tolower $char]
  1016. set last [$w index last]
  1017. if {$last eq "none"} {
  1018. return
  1019. }
  1020. for {set i 0} {$i <= $last} {incr i} {
  1021. if {[catch {set char2 [string index \
  1022. [$w entrycget $i -label] [$w entrycget $i -underline]]}]} {
  1023. continue
  1024. }
  1025. if {$char eq [string tolower $char2]} {
  1026. if {[$w type $i] eq "cascade"} {
  1027. $w activate $i
  1028. $w postcascade active
  1029. event generate $w <<MenuSelect>>
  1030. set m2 [$w entrycget $i -menu]
  1031. if {$m2 ne ""} {
  1032. MenuFirstEntry $m2
  1033. }
  1034. } else {
  1035. MenuUnpost $w
  1036. uplevel #0 [list $w invoke $i]
  1037. }
  1038. return
  1039. }
  1040. }
  1041. }
  1042. # ::tk::MenuFirstEntry --
  1043. # Given a menu, this procedure finds the first entry that isn't
  1044. # disabled or a tear-off or separator, and activates that entry.
  1045. # However, if there is already an active entry in the menu (e.g.,
  1046. # because of a previous call to tk::PostOverPoint) then the active
  1047. # entry isn't changed. This procedure also sets the input focus
  1048. # to the menu.
  1049. #
  1050. # Arguments:
  1051. # menu - Name of the menu window (possibly empty).
  1052. proc ::tk::MenuFirstEntry menu {
  1053. if {$menu eq ""} {
  1054. return
  1055. }
  1056. tk_menuSetFocus $menu
  1057. if {[$menu index active] ne "none"} {
  1058. return
  1059. }
  1060. set last [$menu index last]
  1061. if {$last eq "none"} {
  1062. return
  1063. }
  1064. for {set i 0} {$i <= $last} {incr i} {
  1065. if {([catch {set state [$menu entrycget $i -state]}] == 0) \
  1066. && $state ne "disabled" && [$menu type $i] ne "tearoff"} {
  1067. $menu activate $i
  1068. GenerateMenuSelect $menu
  1069. # Only post the cascade if the current menu is a menubar;
  1070. # otherwise, if the first entry of the cascade is a cascade,
  1071. # we can get an annoying cascading effect resulting in a bunch of
  1072. # menus getting posted (bug 676)
  1073. if {[$menu type $i] eq "cascade" && [$menu cget -type] eq "menubar"} {
  1074. set cascade [$menu entrycget $i -menu]
  1075. if {$cascade ne ""} {
  1076. $menu postcascade $i
  1077. MenuFirstEntry $cascade
  1078. }
  1079. }
  1080. return
  1081. }
  1082. }
  1083. }
  1084. # ::tk::MenuFindName --
  1085. # Given a menu and a text string, return the index of the menu entry
  1086. # that displays the string as its label. If there is no such entry,
  1087. # return an empty string. This procedure is tricky because some names
  1088. # like "active" have a special meaning in menu commands, so we can't
  1089. # always use the "index" widget command.
  1090. #
  1091. # Arguments:
  1092. # menu - Name of the menu widget.
  1093. # s - String to look for.
  1094. proc ::tk::MenuFindName {menu s} {
  1095. set i ""
  1096. if {![regexp {^active$|^last$|^none$|^[0-9]|^@} $s]} {
  1097. catch {set i [$menu index $s]}
  1098. return $i
  1099. }
  1100. set last [$menu index last]
  1101. if {$last eq "none"} {
  1102. return
  1103. }
  1104. for {set i 0} {$i <= $last} {incr i} {
  1105. if {![catch {$menu entrycget $i -label} label]} {
  1106. if {$label eq $s} {
  1107. return $i
  1108. }
  1109. }
  1110. }
  1111. return ""
  1112. }
  1113. # ::tk::PostOverPoint --
  1114. # This procedure posts a given menu such that a given entry in the
  1115. # menu is centered over a given point in the root window. It also
  1116. # activates the given entry.
  1117. #
  1118. # Arguments:
  1119. # menu - Menu to post.
  1120. # x, y - Root coordinates of point.
  1121. # entry - Index of entry within menu to center over (x,y).
  1122. # If omitted or specified as {}, then the menu's
  1123. # upper-left corner goes at (x,y).
  1124. proc ::tk::PostOverPoint {menu x y {entry {}}} {
  1125. if {$entry ne ""} {
  1126. if {$entry == [$menu index last]} {
  1127. incr y [expr {-([$menu yposition $entry] \
  1128. + [winfo reqheight $menu])/2}]
  1129. } else {
  1130. incr y [expr {-([$menu yposition $entry] \
  1131. + [$menu yposition [expr {$entry+1}]])/2}]
  1132. }
  1133. incr x [expr {-[winfo reqwidth $menu]/2}]
  1134. }
  1135. if {[tk windowingsystem] eq "win32"} {
  1136. # osVersion is not available in safe interps
  1137. set ver 5
  1138. if {[info exists ::tcl_platform(osVersion)]} {
  1139. scan $::tcl_platform(osVersion) %d ver
  1140. }
  1141. # We need to fix some problems with menu posting on Windows,
  1142. # where, if the menu would overlap top or bottom of screen,
  1143. # Windows puts it in the wrong place for us. We must also
  1144. # subtract an extra amount for half the height of the current
  1145. # entry. To be safe we subtract an extra 10.
  1146. # NOTE: this issue appears to have been resolved in the Window
  1147. # manager provided with Vista and Windows 7.
  1148. if {$ver < 6} {
  1149. set yoffset [expr {[winfo screenheight $menu] \
  1150. - $y - [winfo reqheight $menu] - 10}]
  1151. if {$yoffset < [winfo vrooty $menu]} {
  1152. # The bottom of the menu is offscreen, so adjust upwards
  1153. incr y [expr {$yoffset - [winfo vrooty $menu]}]
  1154. }
  1155. # If we're off the top of the screen (either because we were
  1156. # originally or because we just adjusted too far upwards),
  1157. # then make the menu popup on the top edge.
  1158. if {$y < [winfo vrooty $menu]} {
  1159. set y [winfo vrooty $menu]
  1160. }
  1161. }
  1162. }
  1163. $menu post $x $y
  1164. if {$entry ne "" && [$menu entrycget $entry -state] ne "disabled"} {
  1165. $menu activate $entry
  1166. GenerateMenuSelect $menu
  1167. }
  1168. }
  1169. # ::tk::SaveGrabInfo --
  1170. # Sets the variables tk::Priv(oldGrab) and tk::Priv(grabStatus) to record
  1171. # the state of any existing grab on the w's display.
  1172. #
  1173. # Arguments:
  1174. # w - Name of a window; used to select the display
  1175. # whose grab information is to be recorded.
  1176. proc tk::SaveGrabInfo w {
  1177. variable ::tk::Priv
  1178. set Priv(oldGrab) [grab current $w]
  1179. if {$Priv(oldGrab) ne ""} {
  1180. set Priv(grabStatus) [grab status $Priv(oldGrab)]
  1181. }
  1182. }
  1183. # ::tk::RestoreOldGrab --
  1184. # Restores the grab to what it was before TkSaveGrabInfo was called.
  1185. #
  1186. proc ::tk::RestoreOldGrab {} {
  1187. variable ::tk::Priv
  1188. if {$Priv(oldGrab) ne ""} {
  1189. # Be careful restoring the old grab, since it's window may not
  1190. # be visible anymore.
  1191. catch {
  1192. if {$Priv(grabStatus) eq "global"} {
  1193. grab set -global $Priv(oldGrab)
  1194. } else {
  1195. grab set $Priv(oldGrab)
  1196. }
  1197. }
  1198. set Priv(oldGrab) ""
  1199. }
  1200. }
  1201. proc ::tk_menuSetFocus {menu} {
  1202. variable ::tk::Priv
  1203. if {![info exists Priv(focus)] || $Priv(focus) eq ""} {
  1204. set Priv(focus) [focus]
  1205. }
  1206. focus $menu
  1207. }
  1208. proc ::tk::GenerateMenuSelect {menu} {
  1209. variable ::tk::Priv
  1210. if {$Priv(activeMenu) eq $menu \
  1211. && $Priv(activeItem) eq [$menu index active]} {
  1212. return
  1213. }
  1214. set Priv(activeMenu) $menu
  1215. set Priv(activeItem) [$menu index active]
  1216. event generate $menu <<MenuSelect>>
  1217. }
  1218. # ::tk_popup --
  1219. # This procedure pops up a menu and sets things up for traversing
  1220. # the menu and its submenus.
  1221. #
  1222. # Arguments:
  1223. # menu - Name of the menu to be popped up.
  1224. # x, y - Root coordinates at which to pop up the
  1225. # menu.
  1226. # entry - Index of a menu entry to center over (x,y).
  1227. # If omitted or specified as {}, then menu's
  1228. # upper-left corner goes at (x,y).
  1229. proc ::tk_popup {menu x y {entry {}}} {
  1230. variable ::tk::Priv
  1231. if {$Priv(popup) ne "" || $Priv(postedMb) ne ""} {
  1232. tk::MenuUnpost {}
  1233. }
  1234. tk::PostOverPoint $menu $x $y $entry
  1235. if {[tk windowingsystem] eq "x11" && [winfo viewable $menu]} {
  1236. tk::SaveGrabInfo $menu
  1237. grab -global $menu
  1238. set Priv(popup) $menu
  1239. set Priv(window) $menu
  1240. set Priv(menuActivated) 1
  1241. tk_menuSetFocus $menu
  1242. }
  1243. }