menu.tcl 37 KB

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