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