1. /*
  2. * @(#)JMenu.java 1.145 01/02/09
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.Component;
  12. import java.awt.Container;
  13. import java.awt.Dimension;
  14. import java.awt.Frame;
  15. import java.awt.Graphics;
  16. import java.awt.Point;
  17. import java.awt.Polygon;
  18. import java.awt.Rectangle;
  19. import java.awt.Toolkit;
  20. import java.awt.event.*;
  21. import java.beans.*;
  22. import java.util.*;
  23. import java.io.Serializable;
  24. import java.io.ObjectOutputStream;
  25. import java.io.ObjectInputStream;
  26. import java.io.IOException;
  27. import javax.swing.event.*;
  28. import javax.swing.plaf.*;
  29. import javax.swing.plaf.basic.*;
  30. import javax.accessibility.*;
  31. /**
  32. * An implementation of a menu -- a popup window containing
  33. * <code>JMenuItem</code>s that
  34. * is displayed when the user selects an item on the <code>JMenuBar</code>.
  35. * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can
  36. * also contain <code>JSeparator</code>s.
  37. * <p>
  38. * In essence, a menu is a button with an associated <code>JPopupMenu</code>.
  39. * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the
  40. * "button" is on the <code>JMenuBar</code>, the menu is a top-level window.
  41. * If the "button" is another menu item, then the <code>JPopupMenu</code> is
  42. * "pull-right" menu.
  43. * <p>
  44. * For information and examples of using menus see
  45. * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
  46. * a section in <em>The Java Tutorial.</em>
  47. * For the keyboard keys used by this component in the standard Look and
  48. * Feel (L&F) renditions, see the
  49. * <a href="doc-files/Key-Index.html#JMenu">JMenu</a> key assignments.
  50. * <p>
  51. * <strong>Warning:</strong>
  52. * Serialized objects of this class will not be compatible with
  53. * future Swing releases. The current serialization support is appropriate
  54. * for short term storage or RMI between applications running the same
  55. * version of Swing. A future release of Swing will provide support for
  56. * long term persistence.
  57. *
  58. * @beaninfo
  59. * attribute: isContainer true
  60. * description: A popup window containing menu items displayed in a menu bar.
  61. *
  62. * @version 1.145 02/09/01
  63. * @author Georges Saab
  64. * @author David Karlton
  65. * @author Arnaud Weber
  66. * @see JMenuItem
  67. * @see JSeparator
  68. * @see JMenuBar
  69. * @see JPopupMenu
  70. */
  71. public class JMenu extends JMenuItem implements Accessible,MenuElement
  72. {
  73. /**
  74. * @see #getUIClassID
  75. * @see #readObject
  76. */
  77. private static final String uiClassID = "MenuUI";
  78. /*
  79. * The popup menu portion of the menu.
  80. */
  81. private JPopupMenu popupMenu;
  82. /*
  83. * The button's model listeners. Default is <code>null</code>.
  84. */
  85. private ChangeListener menuChangeListener = null;
  86. /*
  87. * Only one <code>MenuEvent</code> is needed for each menu since the
  88. * event's only state is the source property. The source of events
  89. * generated is always "this". Default is <code>null</code>.
  90. */
  91. private MenuEvent menuEvent = null;
  92. /* Registry of listeners created for <code>Action-JMenuItem</code>
  93. * linkage. This is needed so that references can
  94. * be cleaned up at remove time to allow garbage collection
  95. * Default is <code>null</code>.
  96. */
  97. private static Hashtable listenerRegistry = null;
  98. /*
  99. * Used by the look and feel (L&F) code to handle
  100. * implementation specific menu behaviors.
  101. */
  102. private int delay;
  103. /**
  104. * Set to true when a KEY_PRESSED event is received and the menu is
  105. * selected, and false when a KEY_RELEASED (or focus lost) is received.
  106. * If processKeyEvent is invoked with a KEY_TYPED or KEY_RELEASED event,
  107. * and this is false, a MenuKeyEvent is NOT created. This is needed to
  108. * avoid activating a menuitem when the menu and menuitem share the
  109. * same mnemonic.
  110. */
  111. private boolean receivedKeyPressed;
  112. /* Diagnostic aids -- should be false for production builds. */
  113. private static final boolean TRACE = false; // trace creates and disposes
  114. private static final boolean VERBOSE = false; // show reuse hits/misses
  115. private static final boolean DEBUG = false; // show bad params, misc.
  116. /**
  117. * Constructs a new <code>JMenu</code> with no text.
  118. */
  119. public JMenu() {
  120. this("");
  121. }
  122. /**
  123. * Constructs a new <code>JMenu</code> with the supplied string
  124. * as its text.
  125. *
  126. * @param s the text for the menu label
  127. */
  128. public JMenu(String s) {
  129. super(s);
  130. }
  131. /**
  132. * Constructs a menu whose properties are taken from the
  133. * <code>Action</code> supplied.
  134. * @param a an <code>Action</code>
  135. *
  136. * @since 1.3
  137. */
  138. public JMenu(Action a) {
  139. this();
  140. setAction(a);
  141. }
  142. /**
  143. * Constructs a new <code>JMenu</code> with the supplied string as
  144. * its text and specified as a tear-off menu or not.
  145. *
  146. * @param s the text for the menu label
  147. * @param b can the menu be torn off (not yet implemented)
  148. */
  149. public JMenu(String s, boolean b) {
  150. this(s);
  151. }
  152. /**
  153. * Notification from the <code>UIFactory</code> that the L&F has changed.
  154. * Called to replace the UI with the latest version from the
  155. * <code>UIFactory</code>.
  156. *
  157. * @see JComponent#updateUI
  158. */
  159. public void updateUI() {
  160. setUI((MenuItemUI)UIManager.getUI(this));
  161. if ( popupMenu != null )
  162. {
  163. popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));
  164. }
  165. }
  166. /**
  167. * Returns the name of the L&F class that renders this component.
  168. *
  169. * @return the string "MenuUI"
  170. * @see JComponent#getUIClassID
  171. * @see UIDefaults#getUI
  172. */
  173. public String getUIClassID() {
  174. return uiClassID;
  175. }
  176. // public void repaint(long tm, int x, int y, int width, int height) {
  177. // Thread.currentThread().dumpStack();
  178. // super.repaint(tm,x,y,width,height);
  179. // }
  180. /**
  181. * Sets the data model for the "menu button" -- the label
  182. * that the user clicks to open or close the menu.
  183. *
  184. * @param newModel the <code>ButtonModel</code>
  185. * @see #getModel
  186. * @beaninfo
  187. * description: The menu's model
  188. * bound: true
  189. * expert: true
  190. * hidden: true
  191. */
  192. public void setModel(ButtonModel newModel) {
  193. ButtonModel oldModel = getModel();
  194. super.setModel(newModel);
  195. if (oldModel != null && menuChangeListener != null) {
  196. oldModel.removeChangeListener(menuChangeListener);
  197. menuChangeListener = null;
  198. }
  199. model = newModel;
  200. if (newModel != null) {
  201. menuChangeListener = createMenuChangeListener();
  202. newModel.addChangeListener(menuChangeListener);
  203. }
  204. }
  205. /**
  206. * Returns true if the menu is currently selected (highlighted).
  207. *
  208. * @return true if the menu is selected, else false
  209. */
  210. public boolean isSelected() {
  211. return getModel().isSelected();
  212. }
  213. /**
  214. * Sets the selection status of the menu.
  215. *
  216. * @param b true to select (highlight) the menu; false to de-select
  217. * the menu
  218. * @beaninfo
  219. * description: When the menu is selected, its popup child is shown.
  220. * expert: true
  221. * hidden: true
  222. */
  223. public void setSelected(boolean b) {
  224. ButtonModel model = getModel();
  225. boolean oldValue = model.isSelected();
  226. if ((accessibleContext != null) && (oldValue != b)) {
  227. if (b) {
  228. accessibleContext.firePropertyChange(
  229. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  230. null, AccessibleState.SELECTED);
  231. } else {
  232. accessibleContext.firePropertyChange(
  233. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  234. AccessibleState.SELECTED, null);
  235. }
  236. }
  237. if (b != model.isSelected()) {
  238. getModel().setSelected(b);
  239. }
  240. }
  241. /**
  242. * Returns true if the menu's popup window is visible.
  243. *
  244. * @return true if the menu is visible, else false
  245. */
  246. public boolean isPopupMenuVisible() {
  247. ensurePopupMenuCreated();
  248. return popupMenu.isVisible();
  249. }
  250. /**
  251. * Sets the visibility of the menu's popup. If the menu is
  252. * not enabled, this method will have no effect.
  253. *
  254. * @param b a boolean value -- true to make the menu visible,
  255. * false to hide it
  256. * @beaninfo
  257. * description: The popup menu's visibility
  258. * expert: true
  259. * hidden: true
  260. */
  261. public void setPopupMenuVisible(boolean b) {
  262. if (!isEnabled())
  263. return;
  264. if (DEBUG) {
  265. System.out.println("in JMenu.setPopupMenuVisible " + b);
  266. // Thread.dumpStack();
  267. }
  268. boolean isVisible = isPopupMenuVisible();
  269. if (b != isVisible) {
  270. ensurePopupMenuCreated();
  271. // Set location of popupMenu (pulldown or pullright)
  272. // Perhaps this should be dictated by L&F
  273. if ((b==true) && isShowing()) {
  274. Point p = getPopupMenuOrigin();
  275. getPopupMenu().show(this, p.x, p.y);
  276. } else {
  277. getPopupMenu().setVisible(false);
  278. }
  279. }
  280. }
  281. /**
  282. * Computes the origin for the <code>JMenu</code>'s popup menu.
  283. *
  284. * @return a <code>Point</code> in the coordinate space of the
  285. * menu which should be used as the origin
  286. * of the <code>JMenu</code>'s popup menu
  287. */
  288. protected Point getPopupMenuOrigin() {
  289. int x = 0;
  290. int y = 0;
  291. JPopupMenu pm = getPopupMenu();
  292. // Figure out the sizes needed to caclulate the menu position
  293. Dimension screenSize =Toolkit.getDefaultToolkit().getScreenSize();
  294. Dimension s = getSize();
  295. Dimension pmSize = pm.getSize();
  296. // For the first time the menu is popped up,
  297. // the size has not yet been initiated
  298. if (pmSize.width==0) {
  299. pmSize = pm.getPreferredSize();
  300. }
  301. Point position = getLocationOnScreen();
  302. Container parent = getParent();
  303. if (parent instanceof JPopupMenu) {
  304. // We are a submenu (pull-right)
  305. if( SwingUtilities.isLeftToRight(this) ) {
  306. // First determine x:
  307. if (position.x+s.width + pmSize.width < screenSize.width) {
  308. x = s.width; // Prefer placement to the right
  309. } else {
  310. x = 0-pmSize.width; // Otherwise place to the left
  311. }
  312. } else {
  313. // First determine x:
  314. if (position.x < pmSize.width) {
  315. x = s.width; // Prefer placement to the right
  316. } else {
  317. x = 0-pmSize.width; // Otherwise place to the left
  318. }
  319. }
  320. // Then the y:
  321. if (position.y+pmSize.height < screenSize.height) {
  322. y = 0; // Prefer dropping down
  323. } else {
  324. y = s.height-pmSize.height; // Otherwise drop 'up'
  325. }
  326. } else {
  327. // We are a toplevel menu (pull-down)
  328. if( SwingUtilities.isLeftToRight(this) ) {
  329. // First determine the x:
  330. if (position.x+pmSize.width < screenSize.width) {
  331. x = 0; // Prefer extending to right
  332. } else {
  333. x = s.width-pmSize.width; // Otherwise extend to left
  334. }
  335. } else {
  336. // First determine the x:
  337. if (position.x+s.width < pmSize.width) {
  338. x = 0; // Prefer extending to right
  339. } else {
  340. x = s.width-pmSize.width; // Otherwise extend to left
  341. }
  342. }
  343. // Then the y:
  344. if (position.y+s.height+pmSize.height < screenSize.height) {
  345. y = s.height; // Prefer dropping down
  346. } else {
  347. y = 0-pmSize.height; // Otherwise drop 'up'
  348. }
  349. }
  350. return new Point(x,y);
  351. }
  352. /**
  353. * Returns the suggested delay, in milliseconds, before submenus
  354. * are popped up or down.
  355. * Each look and feel (L&F) may determine its own policy for
  356. * observing the <code>delay</code> property.
  357. * In most cases, the delay is not observed for top level menus
  358. * or while dragging. The default for <code>delay</code> is 0.
  359. * This method is a property of the look and feel code and is used
  360. * to manage the idiosyncracies of the various UI implementations.
  361. *
  362. *
  363. * @return the <code>delay</code> property
  364. */
  365. public int getDelay() {
  366. return delay;
  367. }
  368. /**
  369. * Sets the suggested delay before the menu's <code>PopupMenu</code>
  370. * is popped up or down. Each look and feel (L&F) may determine
  371. * it's own policy for observing the delay property. In most cases,
  372. * the delay is not observed for top level menus or while dragging.
  373. * This method is a property of the look and feel code and is used
  374. * to manage the idiosyncracies of the various UI implementations.
  375. *
  376. * @param d the number of milliseconds to delay
  377. * @exception IllegalArgumentException if <code>d</code>
  378. * is less than 0
  379. * @beaninfo
  380. * description: The delay between menu selection and making the popup menu visible
  381. * expert: true
  382. */
  383. public void setDelay(int d) {
  384. if (d < 0)
  385. throw new IllegalArgumentException("Delay must be a positive integer");
  386. delay = d;
  387. }
  388. /**
  389. * The window-closing listener for the popup.
  390. *
  391. * @see WinListener
  392. */
  393. protected WinListener popupListener;
  394. private void ensurePopupMenuCreated() {
  395. if (popupMenu == null) {
  396. final JMenu thisMenu = this;
  397. this.popupMenu = new JPopupMenu();
  398. popupMenu.setInvoker(this);
  399. popupListener = createWinListener(popupMenu);
  400. popupMenu.addPopupMenuListener(new PopupMenuListener() {
  401. public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
  402. }
  403. public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
  404. }
  405. public void popupMenuCanceled(PopupMenuEvent e) {
  406. fireMenuCanceled();
  407. }
  408. });
  409. }
  410. }
  411. /**
  412. * Sets the location of the popup component.
  413. *
  414. * @param x the x coordinate of the popup's new position
  415. * @param y the y coordinate of the popup's new position
  416. */
  417. public void setMenuLocation(int x, int y) {
  418. if (popupMenu != null)
  419. popupMenu.setLocation(x, y);
  420. }
  421. /**
  422. * Appends a menu item to the end of this menu.
  423. * Returns the menu item added.
  424. *
  425. * @param menuItem the <code>JMenuitem</code> to be added
  426. * @return the <code>JMenuItem</code> added
  427. */
  428. public JMenuItem add(JMenuItem menuItem) {
  429. AccessibleContext ac = menuItem.getAccessibleContext();
  430. ac.setAccessibleParent(this);
  431. ensurePopupMenuCreated();
  432. return popupMenu.add(menuItem);
  433. }
  434. /**
  435. * Appends a component to the end of this menu.
  436. * Returns the component added.
  437. *
  438. * @param c the <code>Component</code> to add
  439. * @return the <code>Component</code> added
  440. */
  441. public Component add(Component c) {
  442. if (c instanceof JComponent) {
  443. AccessibleContext ac = ((JComponent) c).getAccessibleContext();
  444. if (ac != null) {
  445. ac.setAccessibleParent(this);
  446. }
  447. }
  448. ensurePopupMenuCreated();
  449. popupMenu.add(c);
  450. return c;
  451. }
  452. /**
  453. * Adds the specified component to this container at the given
  454. * position. If <code>index</code> equals -1, the component will
  455. * be appended to the end.
  456. * @param c the <code>Component</code> to add
  457. * @param index the position at which to insert the component
  458. * @return the <code>Component</code> added
  459. * @see #remove
  460. * @see java.awt.Container#add(Component, int)
  461. */
  462. public Component add(Component c, int index) {
  463. if (c instanceof JComponent) {
  464. AccessibleContext ac = ((JComponent) c).getAccessibleContext();
  465. if (ac != null) {
  466. ac.setAccessibleParent(this);
  467. }
  468. }
  469. ensurePopupMenuCreated();
  470. popupMenu.add(c, index);
  471. return c;
  472. }
  473. /**
  474. * Creates a new menu item with the specified text and appends
  475. * it to the end of this menu.
  476. *
  477. * @param s the string for the menu item to be added
  478. */
  479. public JMenuItem add(String s) {
  480. return add(new JMenuItem(s));
  481. }
  482. /**
  483. * Creates a new menu item attached to the specified
  484. * <code>Action</code> object and appends it to the end of this menu.
  485. * As of JDK 1.3, this is no longer the preferred method for adding
  486. * <code>Actions</code> to
  487. * a container. Instead it is recommended to configure a control with
  488. * an action using <code>setAction</code>,
  489. * and then add that control directly
  490. * to the <code>Container</code>.
  491. *
  492. * @param a the <code>Action</code> for the menu item to be added
  493. * @see Action
  494. */
  495. public JMenuItem add(Action a) {
  496. JMenuItem mi = createActionComponent(a);
  497. mi.setAction(a);
  498. add(mi);
  499. return mi;
  500. }
  501. /**
  502. * Factory method which creates the <code>JMenuItem</code> for
  503. * <code>Action</code>s added to the <code>JMenu</code>.
  504. * As of JDK 1.3, this is no
  505. * longer the preferred method. Instead it is recommended to configure
  506. * a control with an action using <code>setAction</code>,
  507. * and then adding that
  508. * control directly to the <code>Container</code>.
  509. *
  510. * @param a the <code>Action</code> for the menu item to be added
  511. * @return the new menu item
  512. * @see Action
  513. */
  514. protected JMenuItem createActionComponent(Action a) {
  515. JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  516. (Icon)a.getValue(Action.SMALL_ICON)){
  517. protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
  518. PropertyChangeListener pcl = createActionChangeListener(this);
  519. if (pcl == null) {
  520. pcl = super.createActionPropertyChangeListener(a);
  521. }
  522. return pcl;
  523. }
  524. };
  525. mi.setHorizontalTextPosition(JButton.RIGHT);
  526. mi.setVerticalTextPosition(JButton.CENTER);
  527. mi.setEnabled(a.isEnabled());
  528. return mi;
  529. }
  530. /**
  531. * Returns a properly configured <code>PropertyChangeListener</code>
  532. * which updates the control as changes to the <code>Action</code> occur.
  533. * As of JDK 1.3, this is no longer the preferred method for adding
  534. * <code>Action</code>s to a <code>Container</code>.
  535. * Instead it is recommended to configure a control with
  536. * an action using <code>setAction</code>, and then add that
  537. * control directly
  538. * to the <code>Container</code>.
  539. */
  540. protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
  541. return new ActionChangedListener(b);
  542. }
  543. private class ActionChangedListener implements PropertyChangeListener {
  544. JMenuItem menuItem;
  545. ActionChangedListener(JMenuItem mi) {
  546. super();
  547. setTarget(mi);
  548. }
  549. public void propertyChange(PropertyChangeEvent e) {
  550. String propertyName = e.getPropertyName();
  551. if (e.getPropertyName().equals(Action.NAME)) {
  552. String text = (String) e.getNewValue();
  553. menuItem.setText(text);
  554. } else if (propertyName.equals("enabled")) {
  555. Boolean enabledState = (Boolean) e.getNewValue();
  556. menuItem.setEnabled(enabledState.booleanValue());
  557. } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  558. Icon icon = (Icon) e.getNewValue();
  559. menuItem.setIcon(icon);
  560. menuItem.invalidate();
  561. menuItem.repaint();
  562. }
  563. }
  564. public void setTarget(JMenuItem b) {
  565. this.menuItem = b;
  566. }
  567. }
  568. /**
  569. * Appends a new separator to the end of the menu.
  570. */
  571. public void addSeparator()
  572. {
  573. ensurePopupMenuCreated();
  574. popupMenu.addSeparator();
  575. }
  576. /**
  577. * Inserts a new menu item with the specified text at a
  578. * given position.
  579. *
  580. * @param s the text for the menu item to add
  581. * @param pos an integer specifying the position at which to add the
  582. * new menu item
  583. * @exception IllegalArgumentException when the value of
  584. * <code>pos</code> < 0
  585. */
  586. public void insert(String s, int pos) {
  587. if (pos < 0) {
  588. throw new IllegalArgumentException("index less than zero.");
  589. }
  590. ensurePopupMenuCreated();
  591. popupMenu.insert(new JMenuItem(s), pos);
  592. }
  593. /**
  594. * Inserts the specified <code>JMenuitem</code> at a given position.
  595. *
  596. * @param mi the <code>JMenuitem</code> to add
  597. * @param pos an integer specifying the position at which to add the
  598. * new <code>JMenuitem</code>
  599. * @return the new menu item
  600. * @exception IllegalArgumentException if the value of
  601. * <code>pos</code> < 0
  602. */
  603. public JMenuItem insert(JMenuItem mi, int pos) {
  604. if (pos < 0) {
  605. throw new IllegalArgumentException("index less than zero.");
  606. }
  607. AccessibleContext ac = mi.getAccessibleContext();
  608. ac.setAccessibleParent(this);
  609. ensurePopupMenuCreated();
  610. popupMenu.insert(mi, pos);
  611. return mi;
  612. }
  613. /**
  614. * Inserts a new menu item attached to the specified <code>Action</code>
  615. * object at a given position.
  616. *
  617. * @param a the <code>Action</code> object for the menu item to add
  618. * @param pos an integer specifying the position at which to add the
  619. * new menu item
  620. * @exception IllegalArgumentException if the value of
  621. * <code>pos</code> < 0
  622. */
  623. public JMenuItem insert(Action a, int pos) {
  624. if (pos < 0) {
  625. throw new IllegalArgumentException("index less than zero.");
  626. }
  627. ensurePopupMenuCreated();
  628. JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  629. (Icon)a.getValue(Action.SMALL_ICON));
  630. mi.setHorizontalTextPosition(JButton.RIGHT);
  631. mi.setVerticalTextPosition(JButton.CENTER);
  632. mi.setEnabled(a.isEnabled());
  633. mi.setAction(a);
  634. popupMenu.insert(mi, pos);
  635. return mi;
  636. }
  637. /**
  638. * Inserts a separator at the specified position.
  639. *
  640. * @param index an integer specifying the position at which to
  641. * insert the menu separator
  642. * @exception IllegalArgumentException if the value of
  643. * <code>index</code> < 0
  644. */
  645. public void insertSeparator(int index) {
  646. if (index < 0) {
  647. throw new IllegalArgumentException("index less than zero.");
  648. }
  649. ensurePopupMenuCreated();
  650. popupMenu.insert( new JPopupMenu.Separator(), index );
  651. }
  652. /**
  653. * Returns the <code>JMenuItem</code> at the specified position.
  654. * If the component at <code>pos</code> is not a menu item,
  655. * <code>null</code> is returned.
  656. * This method is included for AWT compatibility.
  657. *
  658. * @param pos an integer specifying the position
  659. * @exception IllegalArgumentException if the value of
  660. * <code>pos</code> < 0
  661. * @return the menu item at the specified position; or <code>null</code>
  662. * if the item as the specified position is not a menu item
  663. */
  664. public JMenuItem getItem(int pos) {
  665. if (pos < 0) {
  666. throw new IllegalArgumentException("index less than zero.");
  667. }
  668. Component c = getMenuComponent(pos);
  669. if (c instanceof JMenuItem) {
  670. JMenuItem mi = (JMenuItem) c;
  671. return mi;
  672. }
  673. // 4173633
  674. return null;
  675. }
  676. /**
  677. * Returns the number of items on the menu, including separators.
  678. * This method is included for AWT compatibility.
  679. *
  680. * @return an integer equal to the number of items on the menu
  681. * @see #getMenuComponentCount
  682. */
  683. public int getItemCount() {
  684. return getMenuComponentCount();
  685. }
  686. /**
  687. * Returns true if the menu can be torn off. This method is not
  688. * yet implemented.
  689. *
  690. * @return true if the menu can be torn off, else false
  691. * @exception Error if invoked -- this method is not yet implemented
  692. */
  693. public boolean isTearOff() {
  694. throw new Error("boolean isTearOff() {} not yet implemented");
  695. }
  696. /**
  697. * Removes the specified menu item from this menu. If there is no
  698. * popup menu, this method will have no effect.
  699. *
  700. * @param item the <code>JMenuItem</code> to be removed from the menu
  701. */
  702. public void remove(JMenuItem item) {
  703. if (popupMenu != null)
  704. popupMenu.remove(item);
  705. }
  706. /**
  707. * Removes the menu item at the specified index from this menu.
  708. *
  709. * @param pos the position of the item to be removed
  710. * @exception IllegalArgumentException if the value of
  711. * <code>pos</code> < 0, or if <code>pos</code>
  712. * is greater than the number of menu items
  713. */
  714. public void remove(int pos) {
  715. if (pos < 0) {
  716. throw new IllegalArgumentException("index less than zero.");
  717. }
  718. if (pos > getItemCount()) {
  719. throw new IllegalArgumentException("index greater than the number of items.");
  720. }
  721. if (popupMenu != null)
  722. popupMenu.remove(pos);
  723. }
  724. /**
  725. * Removes the component <code>c</code> from this menu.
  726. *
  727. * @param c the component to be removed
  728. */
  729. public void remove(Component c) {
  730. if (popupMenu != null)
  731. popupMenu.remove(c);
  732. }
  733. /**
  734. * Removes all menu items from this menu.
  735. */
  736. public void removeAll() {
  737. if (popupMenu != null)
  738. popupMenu.removeAll();
  739. }
  740. /**
  741. * Returns the number of components on the menu.
  742. *
  743. * @return an integer containing the number of components on the menu
  744. */
  745. public int getMenuComponentCount() {
  746. int componentCount = 0;
  747. if (popupMenu != null)
  748. componentCount = popupMenu.getComponentCount();
  749. return componentCount;
  750. }
  751. /**
  752. * Returns the component at position <code>n</code>.
  753. *
  754. * @param n the position of the component to be returned
  755. * @return the component requested, or <code>null</code>
  756. * if there is no popup menu
  757. *
  758. */
  759. public Component getMenuComponent(int n) {
  760. if (popupMenu != null)
  761. return popupMenu.getComponent(n);
  762. return null;
  763. }
  764. /**
  765. * Returns an array of <code>Component</code>s of the menu's
  766. * subcomponents. Note that this returns all <code>Component</code>s
  767. * in the popup menu, including separators.
  768. *
  769. * @return an array of <code>Component</code>s or an empty array
  770. * if there is no popup menu
  771. */
  772. public Component[] getMenuComponents() {
  773. if (popupMenu != null)
  774. return popupMenu.getComponents();
  775. return new Component[0];
  776. }
  777. /**
  778. * Returns true if the menu is a 'top-level menu', that is, if it is
  779. * the direct child of a menubar.
  780. *
  781. * @return true if the menu is activated from the menu bar;
  782. * false if the menu is activated from a menu item
  783. * on another menu
  784. */
  785. public boolean isTopLevelMenu() {
  786. if (getParent() instanceof JMenuBar)
  787. return true;
  788. return false;
  789. }
  790. /**
  791. * Returns true if the specified component exists in the
  792. * submenu hierarchy.
  793. *
  794. * @param c the <code>Component</code> to be tested
  795. * @return true if the <code>Component</code> exists, false otherwise
  796. */
  797. public boolean isMenuComponent(Component c) {
  798. // Are we in the MenuItem part of the menu
  799. if (c == this)
  800. return true;
  801. // Are we in the PopupMenu?
  802. if (c instanceof JPopupMenu) {
  803. JPopupMenu comp = (JPopupMenu) c;
  804. if (comp == this.getPopupMenu())
  805. return true;
  806. }
  807. // Are we in a Component on the PopupMenu
  808. int ncomponents = this.getMenuComponentCount();
  809. Component[] component = this.getMenuComponents();
  810. for (int i = 0 ; i < ncomponents ; i++) {
  811. Component comp = component[i];
  812. // Are we in the current component?
  813. if (comp == c)
  814. return true;
  815. // Hmmm, what about Non-menu containers?
  816. // Recursive call for the Menu case
  817. if (comp instanceof JMenu) {
  818. JMenu subMenu = (JMenu) comp;
  819. if (subMenu.isMenuComponent(c))
  820. return true;
  821. }
  822. }
  823. return false;
  824. }
  825. /*
  826. * Returns a point in the coordinate space of this menu's popupmenu
  827. * which corresponds to the point <code>p</code> in the menu's
  828. * coordinate space.
  829. *
  830. * @param p the point to be translated
  831. * @return the point in the coordinate space of this menu's popupmenu
  832. */
  833. private Point translateToPopupMenu(Point p) {
  834. return translateToPopupMenu(p.x, p.y);
  835. }
  836. /*
  837. * Returns a point in the coordinate space of this menu's popupmenu
  838. * which corresponds to the point (x,y) in the menu's coordinate space.
  839. *
  840. * @param x the x coordinate of the point to be translated
  841. * @param y the y coordinate of the point to be translated
  842. * @return the point in the coordinate space of this menu's popupmenu
  843. */
  844. private Point translateToPopupMenu(int x, int y) {
  845. int newX;
  846. int newY;
  847. if (getParent() instanceof JPopupMenu) {
  848. newX = x - getSize().width;
  849. newY = y;
  850. } else {
  851. newX = x;
  852. newY = y - getSize().height;
  853. }
  854. return new Point(newX, newY);
  855. }
  856. /**
  857. * Returns the popupmenu associated with this menu. If there is
  858. * no popupmenu, it will create one.
  859. */
  860. public JPopupMenu getPopupMenu() {
  861. ensurePopupMenuCreated();
  862. return popupMenu;
  863. }
  864. /**
  865. * Adds a listener for menu events.
  866. *
  867. * @param l the listener to be added
  868. */
  869. public void addMenuListener(MenuListener l) {
  870. listenerList.add(MenuListener.class, l);
  871. }
  872. /**
  873. * Removes a listener for menu events.
  874. *
  875. * @param l the listener to be removed
  876. */
  877. public void removeMenuListener(MenuListener l) {
  878. listenerList.remove(MenuListener.class, l);
  879. }
  880. /**
  881. * Notifies all listeners that have registered interest for
  882. * notification on this event type. The event instance
  883. * is lazily created using the parameters passed into
  884. * the fire method.
  885. *
  886. * @exception Error if there is a <code>null</code> listener
  887. * @see EventListenerList
  888. */
  889. protected void fireMenuSelected() {
  890. if (DEBUG) {
  891. System.out.println("In JMenu.fireMenuSelected");
  892. }
  893. // Guaranteed to return a non-null array
  894. Object[] listeners = listenerList.getListenerList();
  895. // Process the listeners last to first, notifying
  896. // those that are interested in this event
  897. for (int i = listeners.length-2; i>=0; i-=2) {
  898. if (listeners[i]==MenuListener.class) {
  899. if (listeners[i+1]== null) {
  900. throw new Error(getText() +" has a NULL Listener!! " + i);
  901. } else {
  902. // Lazily create the event:
  903. if (menuEvent == null)
  904. menuEvent = new MenuEvent(this);
  905. ((MenuListener)listeners[i+1]).menuSelected(menuEvent);
  906. }
  907. }
  908. }
  909. }
  910. /**
  911. * Notifies all listeners that have registered interest for
  912. * notification on this event type. The event instance
  913. * is lazily created using the parameters passed into
  914. * the fire method.
  915. *
  916. * @exception Error if there is a <code>null</code> listener
  917. * @see EventListenerList
  918. */
  919. protected void fireMenuDeselected() {
  920. if (DEBUG) {
  921. System.out.println("In JMenu.fireMenuDeselected");
  922. }
  923. // Guaranteed to return a non-null array
  924. Object[] listeners = listenerList.getListenerList();
  925. // Process the listeners last to first, notifying
  926. // those that are interested in this event
  927. for (int i = listeners.length-2; i>=0; i-=2) {
  928. if (listeners[i]==MenuListener.class) {
  929. if (listeners[i+1]== null) {
  930. throw new Error(getText() +" has a NULL Listener!! " + i);
  931. } else {
  932. // Lazily create the event:
  933. if (menuEvent == null)
  934. menuEvent = new MenuEvent(this);
  935. ((MenuListener)listeners[i+1]).menuDeselected(menuEvent);
  936. }
  937. }
  938. }
  939. }
  940. /**
  941. * Notifies all listeners that have registered interest for
  942. * notification on this event type. The event instance
  943. * is lazily created using the parameters passed into
  944. * the fire method.
  945. *
  946. * @exception Error if there is a <code>null</code> listener
  947. * @see EventListenerList
  948. */
  949. protected void fireMenuCanceled() {
  950. if (DEBUG) {
  951. System.out.println("In JMenu.fireMenuCanceled");
  952. }
  953. // Guaranteed to return a non-null array
  954. Object[] listeners = listenerList.getListenerList();
  955. // Process the listeners last to first, notifying
  956. // those that are interested in this event
  957. for (int i = listeners.length-2; i>=0; i-=2) {
  958. if (listeners[i]==MenuListener.class) {
  959. if (listeners[i+1]== null) {
  960. throw new Error(getText() +" has a NULL Listener!! "
  961. + i);
  962. } else {
  963. // Lazily create the event:
  964. if (menuEvent == null)
  965. menuEvent = new MenuEvent(this);
  966. ((MenuListener)listeners[i+1]).menuCanceled(menuEvent);
  967. }
  968. }
  969. }
  970. }
  971. class MenuChangeListener implements ChangeListener, Serializable {
  972. boolean isSelected = false;
  973. public void stateChanged(ChangeEvent e) {
  974. ButtonModel model = (ButtonModel) e.getSource();
  975. boolean modelSelected = model.isSelected();
  976. if (modelSelected != isSelected) {
  977. if (modelSelected == true) {
  978. fireMenuSelected();
  979. } else {
  980. fireMenuDeselected();
  981. }
  982. isSelected = modelSelected;
  983. }
  984. }
  985. }
  986. private ChangeListener createMenuChangeListener() {
  987. return new MenuChangeListener();
  988. }
  989. /**
  990. * Creates a window-closing listener for the popup.
  991. *
  992. * @param p the <code>JPopupMenu</code>
  993. * @return the new window-closing listener
  994. *
  995. * @see WinListener
  996. */
  997. protected WinListener createWinListener(JPopupMenu p) {
  998. return new WinListener(p);
  999. }
  1000. /**
  1001. * A listener class that watches for a popup window closing.
  1002. * When the popup is closing, the listener deselects the menu.
  1003. * <p>
  1004. * <strong>Warning:</strong>
  1005. * Serialized objects of this class will not be compatible with
  1006. * future Swing releases. The current serialization support is appropriate
  1007. * for short term storage or RMI between applications running the same
  1008. * version of Swing. A future release of Swing will provide support for
  1009. * long term persistence.
  1010. */
  1011. protected class WinListener extends WindowAdapter implements Serializable {
  1012. JPopupMenu popupMenu;
  1013. /**
  1014. * Create the window listener for the specified popup.
  1015. */
  1016. public WinListener(JPopupMenu p) {
  1017. this.popupMenu = p;
  1018. }
  1019. /**
  1020. * Deselect the menu when the popup is closed from outside.
  1021. */
  1022. public void windowClosing(WindowEvent e) {
  1023. setSelected(false);
  1024. }
  1025. }
  1026. /**
  1027. * Messaged when the menubar selection changes to activate or
  1028. * deactivate this menu.
  1029. * Overrides <code>JMenuItem.menuSelectionChanged</code>.
  1030. *
  1031. * @param isIncluded true if this menu is active, false if
  1032. * it is not
  1033. */
  1034. public void menuSelectionChanged(boolean isIncluded) {
  1035. if (DEBUG) {
  1036. System.out.println("In JMenu.menuSelectionChanged to " + isIncluded);
  1037. }
  1038. setSelected(isIncluded);
  1039. }
  1040. /**
  1041. * Returns an array of <code>MenuElement</code>s containing the submenu
  1042. * for this menu component. If popup menu is <code>null</code> returns
  1043. * an empty array. This method is required to conform to the
  1044. * <code>MenuElement</code> interface. Note that since
  1045. * <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
  1046. * interface, this array will only contain <code>JMenuItem</code>s.
  1047. *
  1048. * @return an array of <code>MenuElement</code> objects
  1049. */
  1050. public MenuElement[] getSubElements() {
  1051. if(popupMenu == null)
  1052. return new MenuElement[0];
  1053. else {
  1054. MenuElement result[] = new MenuElement[1];
  1055. result[0] = popupMenu;
  1056. return result;
  1057. }
  1058. }
  1059. // implements javax.swing.MenuElement
  1060. /**
  1061. * Returns the <code>java.awt.Component</code> used to
  1062. * paint this <code>MenuElement</code>.
  1063. * The returned component is used to convert events and detect if
  1064. * an event is inside a menu component.
  1065. */
  1066. public Component getComponent() {
  1067. return this;
  1068. }
  1069. /**
  1070. * <code>setAccelerator</code> is not defined for <code>JMenu</code>.
  1071. * Use <code>setMnemonic</code> instead.
  1072. * @param keyStroke the keystroke combination which will invoke
  1073. * the <code>JMenuItem</code>'s actionlisteners
  1074. * without navigating the menu hierarchy
  1075. * @exception Error if invoked -- this method is not defined for JMenu.
  1076. * Use <code>setMnemonic</code> instead
  1077. *
  1078. * @beaninfo
  1079. * description: The keystroke combination which will invoke the JMenuItem's
  1080. * actionlisteners without navigating the menu hierarchy
  1081. * hidden: true
  1082. */
  1083. public void setAccelerator(KeyStroke keyStroke) {
  1084. throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
  1085. }
  1086. /**
  1087. * Processes any focus events, such as
  1088. * <code>FocusEvent.FOCUS_GAINED</code> or
  1089. * <code>FocusEvent.FOCUS_LOST</code>.
  1090. *
  1091. * @param e the <code>FocusEvent</code>
  1092. * @see FocusEvent
  1093. */
  1094. protected void processFocusEvent(FocusEvent e) {
  1095. switch(e.getID()) {
  1096. case FocusEvent.FOCUS_LOST:
  1097. receivedKeyPressed = false;
  1098. break;
  1099. default:
  1100. break;
  1101. }
  1102. super.processFocusEvent(e);
  1103. }
  1104. /**
  1105. * Processes key stroke events for this menu, such as mnemonics and
  1106. * accelerators.
  1107. * @param e the key event to be processed
  1108. */
  1109. protected void processKeyEvent(KeyEvent e) {
  1110. if (DEBUG) {
  1111. System.out.println("in JMenu.processKeyEvent for " + getText() +
  1112. " " + KeyStroke.getKeyStrokeForEvent(e));
  1113. System.out.println("Event consumption = " + e.isConsumed());
  1114. Thread.dumpStack();
  1115. }
  1116. boolean createMenuEvent = false;
  1117. switch (e.getID()) {
  1118. case KeyEvent.KEY_PRESSED:
  1119. if (isSelected()) {
  1120. createMenuEvent = receivedKeyPressed = true;
  1121. }
  1122. else {
  1123. receivedKeyPressed = false;
  1124. }
  1125. break;
  1126. case KeyEvent.KEY_RELEASED:
  1127. if (receivedKeyPressed) {
  1128. receivedKeyPressed = false;
  1129. createMenuEvent = true;
  1130. }
  1131. break;
  1132. default:
  1133. createMenuEvent = receivedKeyPressed;
  1134. break;
  1135. }
  1136. if (createMenuEvent) {
  1137. MenuSelectionManager.defaultManager().processKeyEvent(e);
  1138. }
  1139. if(e.isConsumed()) {
  1140. return;
  1141. }
  1142. /* The "if" block below fixes bug #4108907.
  1143. Without this code, opened menus that
  1144. weren't interested in TAB key events (most menus are not) would
  1145. allow such events to propagate up until a component was found
  1146. that was interested in the event. This would often result in
  1147. the focus being moved to another component as a result of the
  1148. TAB, while the menu stayed open. The behavior that is most
  1149. probably desired is that menus are modal, and thus consume
  1150. all keyboard events while they are open. This is implemented
  1151. by the inner "if" clause. But if the desired behavior on TABs
  1152. is that the menu should close and allow the focus to move,
  1153. the "else" clause takes care of that. Note that this is probably
  1154. not the right way to implement that behavior; instead, the menu
  1155. should unpost whenever it looses focus, which would also fix
  1156. another bug: 4156858.
  1157. The fact that one has to special-case TABS here in JMenu code
  1158. also offends me...
  1159. hania 23 July 1998 */
  1160. if(isSelected() && (e.getKeyCode() == KeyEvent.VK_TAB
  1161. || e.getKeyChar() == '\t')) {
  1162. if ((Boolean) UIManager.get("Menu.consumesTabs") == Boolean.TRUE) {
  1163. e.consume();
  1164. return;
  1165. } else {
  1166. MenuSelectionManager.defaultManager().clearSelectedPath();
  1167. }
  1168. }
  1169. super.processKeyEvent(e);
  1170. }
  1171. /**
  1172. * Programatically performs a "click". This overrides the method
  1173. * <code>AbstractButton.doClick</code> in order to make the menu pop up.
  1174. * @param pressTime indicates the number of milliseconds the
  1175. * button was pressed for
  1176. */
  1177. public void doClick(int pressTime) {
  1178. MenuElement me[] = buildMenuElementArray(this);
  1179. MenuSelectionManager.defaultManager().setSelectedPath(me);
  1180. }
  1181. /*
  1182. * Build an array of menu elements - from <code>PopupMenu</code> to
  1183. * the root <code>JMenuBar</code>.
  1184. * @param leaf the leaf node from which to start building up the array
  1185. * @return the array of menu items
  1186. */
  1187. private MenuElement[] buildMenuElementArray(JMenu leaf) {
  1188. Vector elements = new Vector();
  1189. Component current = leaf.getPopupMenu();
  1190. JPopupMenu pop;
  1191. JMenu menu;
  1192. JMenuBar bar;
  1193. while (true) {
  1194. if (current instanceof JPopupMenu) {
  1195. pop = (JPopupMenu) current;
  1196. elements.insertElementAt(pop, 0);
  1197. current = pop.getInvoker();
  1198. } else if (current instanceof JMenu) {
  1199. menu = (JMenu) current;
  1200. elements.insertElementAt(menu, 0);
  1201. current = menu.getParent();
  1202. } else if (current instanceof JMenuBar) {
  1203. bar = (JMenuBar) current;
  1204. elements.insertElementAt(bar, 0);
  1205. MenuElement me[] = new MenuElement[elements.size()];
  1206. elements.copyInto(me);
  1207. return me;
  1208. }
  1209. }
  1210. }
  1211. /**
  1212. * See <code>readObject</code> and <code>writeObject</code> in
  1213. * <code>JComponent</code> for more
  1214. * information about serialization in Swing.
  1215. */
  1216. private void writeObject(ObjectOutputStream s) throws IOException {
  1217. s.defaultWriteObject();
  1218. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  1219. ui.installUI(this);
  1220. }
  1221. }
  1222. /**
  1223. * Returns a string representation of this <code>JMenu</code>. This
  1224. * method is intended to be used only for debugging purposes, and the
  1225. * content and format of the returned string may vary between
  1226. * implementations. The returned string may be empty but may not
  1227. * be <code>null</code>.
  1228. *
  1229. * @return a string representation of this JMenu.
  1230. */
  1231. protected String paramString() {
  1232. return super.paramString();
  1233. }
  1234. /////////////////
  1235. // Accessibility support
  1236. ////////////////
  1237. /**
  1238. * Gets the AccessibleContext associated with this JMenu.
  1239. * For JMenus, the AccessibleContext takes the form of an
  1240. * AccessibleJMenu.
  1241. * A new AccessibleJMenu instance is created if necessary.
  1242. *
  1243. * @return an AccessibleJMenu that serves as the
  1244. * AccessibleContext of this JMenu
  1245. */
  1246. public AccessibleContext getAccessibleContext() {
  1247. if (accessibleContext == null) {
  1248. accessibleContext = new AccessibleJMenu();
  1249. }
  1250. return accessibleContext;
  1251. }
  1252. /**
  1253. * This class implements accessibility support for the
  1254. * <code>JMenu</code> class. It provides an implementation of the
  1255. * Java Accessibility API appropriate to menu user-interface elements.
  1256. * <p>
  1257. * <strong>Warning:</strong>
  1258. * Serialized objects of this class will not be compatible with
  1259. * future Swing releases. The current serialization support is appropriate
  1260. * for short term storage or RMI between applications running the same
  1261. * version of Swing. A future release of Swing will provide support for
  1262. * long term persistence.
  1263. */
  1264. protected class AccessibleJMenu extends AccessibleJMenuItem
  1265. implements AccessibleSelection {
  1266. /**
  1267. * Returns the number of accessible children in the object. If all
  1268. * of the children of this object implement Accessible, than this
  1269. * method should return the number of children of this object.
  1270. *
  1271. * @return the number of accessible children in the object.
  1272. */
  1273. public int getAccessibleChildrenCount() {
  1274. Component[] children = getMenuComponents();
  1275. int count = 0;
  1276. for (int j = 0; j < children.length; j++) {
  1277. if (children[j] instanceof Accessible) {
  1278. count++;
  1279. }
  1280. }
  1281. return count;
  1282. }
  1283. /**
  1284. * Returns the nth Accessible child of the object.
  1285. *
  1286. * @param i zero-based index of child
  1287. * @return the nth Accessible child of the object
  1288. */
  1289. public Accessible getAccessibleChild(int i) {
  1290. Component[] children = getMenuComponents();
  1291. int count = 0;
  1292. for (int j = 0; j < children.length; j++) {
  1293. if (children[j] instanceof Accessible) {
  1294. if (count == i) {
  1295. if (children[j] instanceof JComponent) {
  1296. // FIXME: [[[WDW - probably should set this when
  1297. // the component is added to the menu. I tried
  1298. // to do this in most cases, but the separators
  1299. // added by addSeparator are hard to get to.]]]
  1300. AccessibleContext ac = ((Accessible) children[j]).getAccessibleContext();
  1301. ac.setAccessibleParent(JMenu.this);
  1302. }
  1303. return (Accessible) children[j];
  1304. } else {
  1305. count++;
  1306. }
  1307. }
  1308. }
  1309. return null;
  1310. }
  1311. /**
  1312. * Get the role of this object.
  1313. *
  1314. * @return an instance of AccessibleRole describing the role of the
  1315. * object
  1316. * @see AccessibleRole
  1317. */
  1318. public AccessibleRole getAccessibleRole() {
  1319. return AccessibleRole.MENU;
  1320. }
  1321. /**
  1322. * Get the AccessibleSelection associated with this object. In the
  1323. * implementation of the Java Accessibility API for this class,
  1324. * return this object, which is responsible for implementing the
  1325. * AccessibleSelection interface on behalf of itself.
  1326. *
  1327. * @return this object
  1328. */
  1329. public AccessibleSelection getAccessibleSelection() {
  1330. return this;
  1331. }
  1332. /**
  1333. * Returns 1 if a sub-menu is currently selected in this menu.
  1334. *
  1335. * @return 1 if a menu is currently selected, else 0
  1336. */
  1337. public int getAccessibleSelectionCount() {
  1338. MenuElement me[] =
  1339. MenuSelectionManager.defaultManager().getSelectedPath();
  1340. if (me != null) {
  1341. for (int i = 0; i < me.length; i++) {
  1342. if (me[i] == JMenu.this) { // this menu is selected
  1343. if (i+1 < me.length) {
  1344. return 1;
  1345. }
  1346. }
  1347. }
  1348. }
  1349. return 0;
  1350. }
  1351. /**
  1352. * Returns the currently selected sub-menu if one is selected,
  1353. * otherwise null (there can only be one selection, and it can
  1354. * only be a sub-menu, as otherwise menu items don't remain
  1355. * selected).
  1356. */
  1357. public Accessible getAccessibleSelection(int i) {
  1358. // if i is a sub-menu & popped, return it
  1359. if (i < 0 || i >= getItemCount()) {
  1360. return null;
  1361. }
  1362. MenuElement me[] =
  1363. MenuSelectionManager.defaultManager().getSelectedPath();
  1364. if (me != null) {
  1365. for (int j = 0; j < me.length; j++) {
  1366. if (me[j] == JMenu.this) { // this menu is selected
  1367. // so find the next JMenuItem in the MenuElement
  1368. // array, and return it!
  1369. while (++j < me.length) {
  1370. if (me[j] instanceof JMenuItem) {
  1371. return (Accessible) me[j];
  1372. }
  1373. }
  1374. }
  1375. }
  1376. }
  1377. return null;
  1378. }
  1379. /**
  1380. * Returns true if the current child of this object is selected
  1381. * (that is, if this child is a popped-up submenu).
  1382. *
  1383. * @param i the zero-based index of the child in this Accessible
  1384. * object.
  1385. * @see AccessibleContext#getAccessibleChild
  1386. */
  1387. public boolean isAccessibleChildSelected(int i) {
  1388. // if i is a sub-menu and is pop-ed up, return true, else false
  1389. MenuElement me[] =
  1390. MenuSelectionManager.defaultManager().getSelectedPath();
  1391. if (me != null) {
  1392. JMenuItem mi = JMenu.this.getItem(i);
  1393. for (int j = 0; j < me.length; j++) {
  1394. if (me[j] == mi) {
  1395. return true;
  1396. }
  1397. }
  1398. }
  1399. return false;
  1400. }
  1401. /**
  1402. * Selects the <code>i</code>th menu in the menu.
  1403. * If that item is a submenu,
  1404. * it will pop up in response. If a different item is already
  1405. * popped up, this will force it to close. If this is a sub-menu
  1406. * that is already popped up (selected), this method has no
  1407. * effect.
  1408. *
  1409. * @param i the index of the item to be selected
  1410. * @see #getAccessibleStateSet
  1411. */
  1412. public void addAccessibleSelection(int i) {
  1413. if (i < 0 || i >= getItemCount()) {
  1414. return;
  1415. }
  1416. JMenuItem mi = getItem(i);
  1417. if (mi != null) {
  1418. if (mi instanceof JMenu) {
  1419. MenuElement me[] = buildMenuElementArray((JMenu) mi);
  1420. MenuSelectionManager.defaultManager().setSelectedPath(me);
  1421. } else {
  1422. mi.doClick();
  1423. MenuSelectionManager.defaultManager().setSelectedPath(null);
  1424. }
  1425. }
  1426. }
  1427. /**
  1428. * Removes the nth item from the selection. In general, menus
  1429. * can only have one item within them selected at a time
  1430. * (e.g. one sub-menu popped open).
  1431. *
  1432. * @param i the zero-based index of the selected item
  1433. */
  1434. public void removeAccessibleSelection(int i) {
  1435. if (i < 0 || i >= getItemCount()) {
  1436. return;
  1437. }
  1438. JMenuItem mi = getItem(i);
  1439. if (mi != null && mi instanceof JMenu) {
  1440. if (((JMenu) mi).isSelected()) {
  1441. MenuElement old[] =
  1442. MenuSelectionManager.defaultManager().getSelectedPath();
  1443. MenuElement me[] = new MenuElement[old.length-2];
  1444. for (int j = 0; j < old.length -2; j++) {
  1445. me[j] = old[j];
  1446. }
  1447. MenuSelectionManager.defaultManager().setSelectedPath(me);
  1448. }
  1449. }
  1450. }
  1451. /**
  1452. * Clears the selection in the object, so that nothing in the
  1453. * object is selected. This will close any open sub-menu.
  1454. */
  1455. public void clearAccessibleSelection() {
  1456. // if this menu is selected, reset selection to only go
  1457. // to this menu; else do nothing
  1458. MenuElement old[] =
  1459. MenuSelectionManager.defaultManager().getSelectedPath();
  1460. if (old != null) {
  1461. for (int j = 0; j < old.length; j++) {
  1462. if (old[j] == JMenu.this) { // menu is in the selection!
  1463. MenuElement me[] = new MenuElement[j+1];
  1464. System.arraycopy(old, 0, me, 0, j);
  1465. me[j] = JMenu.this.getPopupMenu();
  1466. MenuSelectionManager.defaultManager().setSelectedPath(me);
  1467. }
  1468. }
  1469. }
  1470. }
  1471. /**
  1472. * Normally causes every selected item in the object to be selected
  1473. * if the object supports multiple selections. This method
  1474. * makes no sense in a menu bar, and so does nothing.
  1475. */
  1476. public void selectAllAccessibleSelection() {
  1477. }
  1478. } // inner class AccessibleJMenu
  1479. }