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