JMenu.java 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. /* JMenu.java --
  2. Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package javax.swing;
  32. import java.awt.Component;
  33. import java.awt.Dimension;
  34. import java.awt.GraphicsConfiguration;
  35. import java.awt.GraphicsDevice;
  36. import java.awt.GraphicsEnvironment;
  37. import java.awt.Insets;
  38. import java.awt.Point;
  39. import java.awt.Rectangle;
  40. import java.awt.Toolkit;
  41. import java.awt.event.KeyEvent;
  42. import java.awt.event.WindowAdapter;
  43. import java.awt.event.WindowEvent;
  44. import java.beans.PropertyChangeEvent;
  45. import java.beans.PropertyChangeListener;
  46. import java.io.Serializable;
  47. import java.util.ArrayList;
  48. import java.util.EventListener;
  49. import javax.accessibility.Accessible;
  50. import javax.accessibility.AccessibleContext;
  51. import javax.accessibility.AccessibleRole;
  52. import javax.accessibility.AccessibleSelection;
  53. import javax.swing.event.ChangeEvent;
  54. import javax.swing.event.ChangeListener;
  55. import javax.swing.event.MenuEvent;
  56. import javax.swing.event.MenuListener;
  57. import javax.swing.plaf.MenuItemUI;
  58. /**
  59. * This class represents a menu that can be added to a menu bar or
  60. * can be a submenu in some other menu. When JMenu is selected it
  61. * displays JPopupMenu containing its menu items.
  62. *
  63. * <p>
  64. * JMenu's fires MenuEvents when this menu's selection changes. If this menu
  65. * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is
  66. * deselected or cancelled, then fireMenuDeselectedEvent() or
  67. * fireMenuCancelledEvent() is invoked, respectivelly.
  68. * </p>
  69. */
  70. public class JMenu extends JMenuItem implements Accessible, MenuElement
  71. {
  72. /**
  73. * Receives notifications when the JMenu's ButtonModel is changed and
  74. * fires menuSelected or menuDeselected events when appropriate.
  75. */
  76. private class MenuChangeListener
  77. implements ChangeListener
  78. {
  79. /**
  80. * Indicates the last selected state.
  81. */
  82. private boolean selected;
  83. /**
  84. * Receives notification when the JMenu's ButtonModel changes.
  85. */
  86. public void stateChanged(ChangeEvent ev)
  87. {
  88. ButtonModel m = (ButtonModel) ev.getSource();
  89. boolean s = m.isSelected();
  90. if (s != selected)
  91. {
  92. if (s)
  93. fireMenuSelected();
  94. else
  95. fireMenuDeselected();
  96. selected = s;
  97. }
  98. }
  99. }
  100. private static final long serialVersionUID = 4227225638931828014L;
  101. /** A Popup menu associated with this menu, which pops up when menu is selected */
  102. private JPopupMenu popupMenu = null;
  103. /** Whenever menu is selected or deselected the MenuEvent is fired to
  104. menu's registered listeners. */
  105. private MenuEvent menuEvent = new MenuEvent(this);
  106. /*Amount of time, in milliseconds, that should pass before popupMenu
  107. associated with this menu appears or disappers */
  108. private int delay;
  109. /* PopupListener */
  110. protected WinListener popupListener;
  111. /**
  112. * Location at which popup menu associated with this menu will be
  113. * displayed
  114. */
  115. private Point menuLocation;
  116. /**
  117. * The ChangeListener for the ButtonModel.
  118. *
  119. * @see MenuChangeListener
  120. */
  121. private ChangeListener menuChangeListener;
  122. /**
  123. * Creates a new JMenu object.
  124. */
  125. public JMenu()
  126. {
  127. super();
  128. setOpaque(false);
  129. }
  130. /**
  131. * Creates a new <code>JMenu</code> with the specified label.
  132. *
  133. * @param text label for this menu
  134. */
  135. public JMenu(String text)
  136. {
  137. super(text);
  138. popupMenu = new JPopupMenu();
  139. popupMenu.setInvoker(this);
  140. setOpaque(false);
  141. }
  142. /**
  143. * Creates a new <code>JMenu</code> object.
  144. *
  145. * @param action Action that is used to create menu item tha will be
  146. * added to the menu.
  147. */
  148. public JMenu(Action action)
  149. {
  150. super(action);
  151. createActionChangeListener(this);
  152. popupMenu = new JPopupMenu();
  153. popupMenu.setInvoker(this);
  154. setOpaque(false);
  155. }
  156. /**
  157. * Creates a new <code>JMenu</code> with specified label and an option
  158. * for this menu to be tear-off menu.
  159. *
  160. * @param text label for this menu
  161. * @param tearoff true if this menu should be tear-off and false otherwise
  162. */
  163. public JMenu(String text, boolean tearoff)
  164. {
  165. // FIXME: tearoff not implemented
  166. this(text);
  167. }
  168. /**
  169. * Adds specified menu item to this menu
  170. *
  171. * @param item Menu item to add to this menu
  172. *
  173. * @return Menu item that was added
  174. */
  175. public JMenuItem add(JMenuItem item)
  176. {
  177. return getPopupMenu().add(item);
  178. }
  179. /**
  180. * Adds specified component to this menu.
  181. *
  182. * @param component Component to add to this menu
  183. *
  184. * @return Component that was added
  185. */
  186. public Component add(Component component)
  187. {
  188. getPopupMenu().insert(component, -1);
  189. return component;
  190. }
  191. /**
  192. * Adds specified component to this menu at the given index
  193. *
  194. * @param component Component to add
  195. * @param index Position of this menu item in the menu
  196. *
  197. * @return Component that was added
  198. */
  199. public Component add(Component component, int index)
  200. {
  201. return getPopupMenu().add(component, index);
  202. }
  203. /**
  204. * Adds JMenuItem constructed with the specified label to this menu
  205. *
  206. * @param text label for the menu item that will be added
  207. *
  208. * @return Menu Item that was added to this menu
  209. */
  210. public JMenuItem add(String text)
  211. {
  212. return add(new JMenuItem(text));
  213. }
  214. /**
  215. * Adds JMenuItem constructed using properties from specified action.
  216. *
  217. * @param action action to construct the menu item with
  218. *
  219. * @return Menu Item that was added to this menu
  220. */
  221. public JMenuItem add(Action action)
  222. {
  223. JMenuItem i = createActionComponent(action);
  224. i.setAction(action);
  225. add(i);
  226. return i;
  227. }
  228. /**
  229. * Removes given menu item from this menu. Nothing happens if
  230. * this menu doesn't contain specified menu item.
  231. *
  232. * @param item Menu Item which needs to be removed
  233. */
  234. public void remove(JMenuItem item)
  235. {
  236. getPopupMenu().remove(item);
  237. }
  238. /**
  239. * Removes component at the specified index from this menu
  240. *
  241. * @param index Position of the component that needs to be removed in the menu
  242. */
  243. public void remove(int index)
  244. {
  245. if (index < 0 || (index > 0 && getMenuComponentCount() == 0))
  246. throw new IllegalArgumentException();
  247. if (getMenuComponentCount() > 0)
  248. popupMenu.remove(index);
  249. }
  250. /**
  251. * Removes given component from this menu.
  252. *
  253. * @param component Component to remove
  254. */
  255. public void remove(Component component)
  256. {
  257. int index = getPopupMenu().getComponentIndex(component);
  258. if (index >= 0)
  259. getPopupMenu().remove(index);
  260. }
  261. /**
  262. * Removes all menu items from the menu
  263. */
  264. public void removeAll()
  265. {
  266. if (popupMenu != null)
  267. popupMenu.removeAll();
  268. }
  269. /**
  270. * Creates JMenuItem with the specified text and inserts it in the
  271. * at the specified index
  272. *
  273. * @param text label for the new menu item
  274. * @param index index at which to insert newly created menu item.
  275. */
  276. public void insert(String text, int index)
  277. {
  278. this.insert(new JMenuItem(text), index);
  279. }
  280. /**
  281. * Creates JMenuItem with the specified text and inserts it in the
  282. * at the specified index. IllegalArgumentException is thrown
  283. * if index is less than 0
  284. *
  285. * @param item menu item to insert
  286. * @param index index at which to insert menu item.
  287. * @return Menu item that was added to the menu
  288. */
  289. public JMenuItem insert(JMenuItem item, int index)
  290. {
  291. if (index < 0)
  292. throw new IllegalArgumentException("index less than zero");
  293. getPopupMenu().insert(item, index);
  294. return item;
  295. }
  296. /**
  297. * Creates JMenuItem with the associated action and inserts it to the menu
  298. * at the specified index. IllegalArgumentException is thrown
  299. * if index is less than 0
  300. *
  301. * @param action Action for the new menu item
  302. * @param index index at which to insert newly created menu item.
  303. * @return Menu item that was added to the menu
  304. */
  305. public JMenuItem insert(Action action, int index)
  306. {
  307. JMenuItem item = new JMenuItem(action);
  308. this.insert(item, index);
  309. return item;
  310. }
  311. /**
  312. * This method sets this menuItem's UI to the UIManager's default for the
  313. * current look and feel.
  314. */
  315. public void updateUI()
  316. {
  317. setUI((MenuItemUI) UIManager.getUI(this));
  318. }
  319. /**
  320. * This method returns a name to identify which look and feel class will be
  321. * the UI delegate for the menu.
  322. *
  323. * @return The Look and Feel classID. "MenuUI"
  324. */
  325. public String getUIClassID()
  326. {
  327. return "MenuUI";
  328. }
  329. /**
  330. * Sets model for this menu.
  331. *
  332. * @param model model to set
  333. */
  334. public void setModel(ButtonModel model)
  335. {
  336. ButtonModel oldModel = getModel();
  337. if (oldModel != null && menuChangeListener != null)
  338. oldModel.removeChangeListener(menuChangeListener);
  339. super.setModel(model);
  340. if (model != null)
  341. {
  342. if (menuChangeListener == null)
  343. menuChangeListener = new MenuChangeListener();
  344. model.addChangeListener(menuChangeListener);
  345. }
  346. }
  347. /**
  348. * Returns true if the menu is selected and false otherwise
  349. *
  350. * @return true if the menu is selected and false otherwise
  351. */
  352. public boolean isSelected()
  353. {
  354. return super.isSelected();
  355. }
  356. /**
  357. * Changes this menu selected state if selected is true and false otherwise
  358. * This method fires menuEvents to menu's registered listeners.
  359. *
  360. * @param selected true if the menu should be selected and false otherwise
  361. */
  362. public void setSelected(boolean selected)
  363. {
  364. ButtonModel m = getModel();
  365. if (selected != m.isSelected())
  366. m.setSelected(selected);
  367. }
  368. /**
  369. * Checks if PopupMenu associated with this menu is visible
  370. *
  371. * @return true if the popup associated with this menu is currently visible
  372. * on the screen and false otherwise.
  373. */
  374. public boolean isPopupMenuVisible()
  375. {
  376. return getPopupMenu().isVisible();
  377. }
  378. /**
  379. * Sets popup menu visibility
  380. *
  381. * @param popup true if popup should be visible and false otherwise
  382. */
  383. public void setPopupMenuVisible(boolean popup)
  384. {
  385. if (popup != isPopupMenuVisible() && (isEnabled() || ! popup))
  386. {
  387. if (popup && isShowing())
  388. {
  389. // Set location as determined by getPopupLocation().
  390. Point loc = menuLocation == null ? getPopupMenuOrigin()
  391. : menuLocation;
  392. getPopupMenu().show(this, loc.x, loc.y);
  393. }
  394. else
  395. getPopupMenu().setVisible(false);
  396. }
  397. }
  398. /**
  399. * Returns origin point of the popup menu. This takes the screen bounds
  400. * into account and places the popup where it fits best.
  401. *
  402. * @return the origin of the popup menu
  403. */
  404. protected Point getPopupMenuOrigin()
  405. {
  406. // The menu's screen location and size.
  407. Point screenLoc = getLocationOnScreen();
  408. Dimension size = getSize();
  409. // Determine the popup's size.
  410. JPopupMenu popup = getPopupMenu();
  411. Dimension popupSize = popup.getSize();
  412. if (popupSize.width == 0 || popupSize.height == 0)
  413. popupSize = popup.getPreferredSize();
  414. // Determine screen bounds.
  415. Toolkit tk = Toolkit.getDefaultToolkit();
  416. Rectangle screenBounds = new Rectangle(tk.getScreenSize());
  417. GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  418. GraphicsDevice gd = ge.getDefaultScreenDevice();
  419. GraphicsConfiguration gc = gd.getDefaultConfiguration();
  420. Insets screenInsets = tk.getScreenInsets(gc);
  421. screenBounds.x -= screenInsets.left;
  422. screenBounds.width -= screenInsets.left + screenInsets.right;
  423. screenBounds.y -= screenInsets.top;
  424. screenBounds.height -= screenInsets.top + screenInsets.bottom;
  425. screenLoc.x -= screenInsets.left;
  426. screenLoc.y -= screenInsets.top;
  427. Point point = new Point();
  428. if (isTopLevelMenu())
  429. {
  430. // If menu in the menu bar.
  431. int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
  432. int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
  433. // Determine X location.
  434. if (getComponentOrientation().isLeftToRight())
  435. {
  436. // Prefer popup to the right.
  437. point.x = xOffset;
  438. // Check if it fits, otherwise place popup wherever it fits.
  439. if (screenLoc.x + point.x + popupSize.width
  440. > screenBounds.width + screenBounds.width
  441. && screenBounds.width - size.width
  442. < 2 * (screenLoc.x - screenBounds.x))
  443. // Popup to the right if there's not enough room.
  444. point.x = size.width - xOffset - popupSize.width;
  445. }
  446. else
  447. {
  448. // Prefer popup to the left.
  449. point.x = size.width - xOffset - popupSize.width;
  450. if (screenLoc.x + point.x < screenBounds.x
  451. && screenBounds.width - size.width
  452. > 2 * (screenLoc.x - screenBounds.x))
  453. // Popup to the left if there's not enough room.
  454. point.x = xOffset;
  455. }
  456. // Determine Y location. Prefer popping down.
  457. point.y = size.height + yOffset;
  458. if (screenLoc.y + point.y + popupSize.height >= screenBounds.height
  459. && screenBounds.height - size.height
  460. < 2 * (screenLoc.y - screenBounds.y))
  461. // Position above if there's not enough room below.
  462. point.y = - yOffset - popupSize.height;
  463. }
  464. else
  465. {
  466. // If submenu.
  467. int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
  468. int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
  469. // Determine X location.
  470. if (getComponentOrientation().isLeftToRight())
  471. {
  472. // Prefer popup to the right.
  473. point.x = size.width + xOffset;
  474. if (screenLoc.x + point.x + popupSize.width
  475. >= screenBounds.x + screenBounds.width
  476. && screenBounds.width - size.width
  477. < 2 * (screenLoc.x - screenBounds.x))
  478. // Position to the left if there's not enough room on the right.
  479. point.x = - xOffset - popupSize.width;
  480. }
  481. else
  482. {
  483. // Prefer popup on the left side.
  484. point.x = - xOffset - popupSize.width;
  485. if (screenLoc.x + point.x < screenBounds.x
  486. && screenBounds.width - size.width
  487. > 2 * (screenLoc.x - screenBounds.x))
  488. // Popup to the right if there's not enough room.
  489. point.x = size.width + xOffset;
  490. }
  491. // Determine Y location. Prefer popping down.
  492. point.y = yOffset;
  493. if (screenLoc.y + point.y + popupSize.height
  494. >= screenBounds.y + screenBounds.height
  495. && screenBounds.height - size.height
  496. < 2 * (screenLoc.y - screenBounds.y))
  497. // Pop up if there's not enough room below.
  498. point.y = size.height - yOffset - popupSize.height;
  499. }
  500. return point;
  501. }
  502. /**
  503. * Returns delay property.
  504. *
  505. * @return delay property, indicating number of milliseconds before
  506. * popup menu associated with the menu appears or disappears after
  507. * menu was selected or deselected respectively
  508. */
  509. public int getDelay()
  510. {
  511. return delay;
  512. }
  513. /**
  514. * Sets delay property for this menu. If given time for the delay
  515. * property is negative, then IllegalArgumentException is thrown
  516. *
  517. * @param delay number of milliseconds before
  518. * popup menu associated with the menu appears or disappears after
  519. * menu was selected or deselected respectively
  520. */
  521. public void setDelay(int delay)
  522. {
  523. if (delay < 0)
  524. throw new IllegalArgumentException("delay less than 0");
  525. this.delay = delay;
  526. }
  527. /**
  528. * Sets location at which popup menu should be displayed
  529. * The location given is relative to this menu item
  530. *
  531. * @param x x-coordinate of the menu location
  532. * @param y y-coordinate of the menu location
  533. */
  534. public void setMenuLocation(int x, int y)
  535. {
  536. menuLocation = new Point(x, y);
  537. if (popupMenu != null)
  538. popupMenu.setLocation(x, y);
  539. }
  540. /**
  541. * Creates and returns JMenuItem associated with the given action
  542. *
  543. * @param action Action to use for creation of JMenuItem
  544. *
  545. * @return JMenuItem that was creted with given action
  546. */
  547. protected JMenuItem createActionComponent(Action action)
  548. {
  549. return new JMenuItem(action);
  550. }
  551. /**
  552. * Creates ActionChangeListener to listen for PropertyChangeEvents occuring
  553. * in the action that is associated with this menu
  554. *
  555. * @param item menu that contains action to listen to
  556. *
  557. * @return The PropertyChangeListener
  558. */
  559. protected PropertyChangeListener createActionChangeListener(JMenuItem item)
  560. {
  561. return new ActionChangedListener(item);
  562. }
  563. /**
  564. * Adds separator to the end of the menu items in the menu.
  565. */
  566. public void addSeparator()
  567. {
  568. getPopupMenu().addSeparator();
  569. }
  570. /**
  571. * Inserts separator in the menu at the specified index.
  572. *
  573. * @param index Index at which separator should be inserted
  574. */
  575. public void insertSeparator(int index)
  576. {
  577. if (index < 0)
  578. throw new IllegalArgumentException("index less than 0");
  579. getPopupMenu().insert(new JPopupMenu.Separator(), index);
  580. }
  581. /**
  582. * Returns menu item located at the specified index in the menu
  583. *
  584. * @param index Index at which to look for the menu item
  585. *
  586. * @return menu item located at the specified index in the menu
  587. */
  588. public JMenuItem getItem(int index)
  589. {
  590. if (index < 0)
  591. throw new IllegalArgumentException("index less than 0");
  592. if (getItemCount() == 0)
  593. return null;
  594. Component c = popupMenu.getComponentAtIndex(index);
  595. if (c instanceof JMenuItem)
  596. return (JMenuItem) c;
  597. else
  598. return null;
  599. }
  600. /**
  601. * Returns number of items in the menu including separators.
  602. *
  603. * @return number of items in the menu
  604. *
  605. * @see #getMenuComponentCount()
  606. */
  607. public int getItemCount()
  608. {
  609. return getMenuComponentCount();
  610. }
  611. /**
  612. * Checks if this menu is a tear-off menu.
  613. *
  614. * @return true if this menu is a tear-off menu and false otherwise
  615. */
  616. public boolean isTearOff()
  617. {
  618. // NOT YET IMPLEMENTED
  619. throw new Error("The method isTearOff() has not yet been implemented.");
  620. }
  621. /**
  622. * Returns number of menu components in this menu
  623. *
  624. * @return number of menu components in this menu
  625. */
  626. public int getMenuComponentCount()
  627. {
  628. return getPopupMenu().getComponentCount();
  629. }
  630. /**
  631. * Returns menu component located at the givent index
  632. * in the menu
  633. *
  634. * @param index index at which to get the menu component in the menu
  635. *
  636. * @return Menu Component located in the menu at the specified index
  637. */
  638. public Component getMenuComponent(int index)
  639. {
  640. if (getPopupMenu() == null || getMenuComponentCount() == 0)
  641. return null;
  642. return popupMenu.getComponentAtIndex(index);
  643. }
  644. /**
  645. * Return components belonging to this menu
  646. *
  647. * @return components belonging to this menu
  648. */
  649. public Component[] getMenuComponents()
  650. {
  651. return getPopupMenu().getComponents();
  652. }
  653. /**
  654. * Checks if this menu is a top level menu. The menu is top
  655. * level menu if it is inside the menu bar. While if the menu
  656. * inside some other menu, it is considered to be a pull-right menu.
  657. *
  658. * @return true if this menu is top level menu, and false otherwise
  659. */
  660. public boolean isTopLevelMenu()
  661. {
  662. return getParent() instanceof JMenuBar;
  663. }
  664. /**
  665. * Checks if given component exists in this menu. The submenus of
  666. * this menu are checked as well
  667. *
  668. * @param component Component to look for
  669. *
  670. * @return true if the given component exists in this menu, and false otherwise
  671. */
  672. public boolean isMenuComponent(Component component)
  673. {
  674. return false;
  675. }
  676. /**
  677. * Returns popup menu associated with the menu.
  678. *
  679. * @return popup menu associated with the menu.
  680. */
  681. public JPopupMenu getPopupMenu()
  682. {
  683. if (popupMenu == null)
  684. {
  685. popupMenu = new JPopupMenu();
  686. popupMenu.setInvoker(this);
  687. }
  688. return popupMenu;
  689. }
  690. /**
  691. * Adds MenuListener to the menu
  692. *
  693. * @param listener MenuListener to add
  694. */
  695. public void addMenuListener(MenuListener listener)
  696. {
  697. listenerList.add(MenuListener.class, listener);
  698. }
  699. /**
  700. * Removes MenuListener from the menu
  701. *
  702. * @param listener MenuListener to remove
  703. */
  704. public void removeMenuListener(MenuListener listener)
  705. {
  706. listenerList.remove(MenuListener.class, listener);
  707. }
  708. /**
  709. * Returns all registered <code>MenuListener</code> objects.
  710. *
  711. * @return an array of listeners
  712. *
  713. * @since 1.4
  714. */
  715. public MenuListener[] getMenuListeners()
  716. {
  717. return (MenuListener[]) listenerList.getListeners(MenuListener.class);
  718. }
  719. /**
  720. * This method fires MenuEvents to all menu's MenuListeners. In this case
  721. * menuSelected() method of MenuListeners is called to indicated that the menu
  722. * was selected.
  723. */
  724. protected void fireMenuSelected()
  725. {
  726. MenuListener[] listeners = getMenuListeners();
  727. for (int index = 0; index < listeners.length; ++index)
  728. listeners[index].menuSelected(menuEvent);
  729. }
  730. /**
  731. * This method fires MenuEvents to all menu's MenuListeners. In this case
  732. * menuDeselected() method of MenuListeners is called to indicated that the menu
  733. * was deselected.
  734. */
  735. protected void fireMenuDeselected()
  736. {
  737. EventListener[] ll = listenerList.getListeners(MenuListener.class);
  738. for (int i = 0; i < ll.length; i++)
  739. ((MenuListener) ll[i]).menuDeselected(menuEvent);
  740. }
  741. /**
  742. * This method fires MenuEvents to all menu's MenuListeners. In this case
  743. * menuSelected() method of MenuListeners is called to indicated that the menu
  744. * was cancelled. The menu is cancelled when it's popup menu is close without selection.
  745. */
  746. protected void fireMenuCanceled()
  747. {
  748. EventListener[] ll = listenerList.getListeners(MenuListener.class);
  749. for (int i = 0; i < ll.length; i++)
  750. ((MenuListener) ll[i]).menuCanceled(menuEvent);
  751. }
  752. /**
  753. * Creates WinListener that listens to the menu;s popup menu.
  754. *
  755. * @param popup JPopupMenu to listen to
  756. *
  757. * @return The WinListener
  758. */
  759. protected WinListener createWinListener(JPopupMenu popup)
  760. {
  761. return new WinListener(popup);
  762. }
  763. /**
  764. * Method of the MenuElementInterface. It reacts to the selection
  765. * changes in the menu. If this menu was selected, then it
  766. * displayes popup menu associated with it and if this menu was
  767. * deselected it hides the popup menu.
  768. *
  769. * @param changed true if the menu was selected and false otherwise
  770. */
  771. public void menuSelectionChanged(boolean changed)
  772. {
  773. // if this menu selection is true, then activate this menu and
  774. // display popup associated with this menu
  775. setSelected(changed);
  776. }
  777. /**
  778. * Method of MenuElement interface. Returns sub components of
  779. * this menu.
  780. *
  781. * @return array containing popupMenu that is associated with this menu
  782. */
  783. public MenuElement[] getSubElements()
  784. {
  785. return new MenuElement[] { popupMenu };
  786. }
  787. /**
  788. * @return Returns reference to itself
  789. */
  790. public Component getComponent()
  791. {
  792. return this;
  793. }
  794. /**
  795. * This method is overriden with empty implementation, s.t the
  796. * accelerator couldn't be set for the menu. The mnemonic should
  797. * be used for the menu instead.
  798. *
  799. * @param keystroke accelerator for this menu
  800. */
  801. public void setAccelerator(KeyStroke keystroke)
  802. {
  803. throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
  804. }
  805. /**
  806. * This method process KeyEvent occuring when the menu is visible
  807. *
  808. * @param event The KeyEvent
  809. */
  810. protected void processKeyEvent(KeyEvent event)
  811. {
  812. MenuSelectionManager.defaultManager().processKeyEvent(event);
  813. }
  814. /**
  815. * Programatically performs click
  816. *
  817. * @param time Number of milliseconds for which this menu stays pressed
  818. */
  819. public void doClick(int time)
  820. {
  821. getModel().setArmed(true);
  822. getModel().setPressed(true);
  823. try
  824. {
  825. java.lang.Thread.sleep(time);
  826. }
  827. catch (java.lang.InterruptedException e)
  828. {
  829. // probably harmless
  830. }
  831. getModel().setPressed(false);
  832. getModel().setArmed(false);
  833. popupMenu.show(this, this.getWidth(), 0);
  834. }
  835. /**
  836. * A string that describes this JMenu. Normally only used
  837. * for debugging.
  838. *
  839. * @return A string describing this JMenu
  840. */
  841. protected String paramString()
  842. {
  843. return super.paramString();
  844. }
  845. public AccessibleContext getAccessibleContext()
  846. {
  847. if (accessibleContext == null)
  848. accessibleContext = new AccessibleJMenu();
  849. return accessibleContext;
  850. }
  851. /**
  852. * Implements support for assisitive technologies for <code>JMenu</code>.
  853. */
  854. protected class AccessibleJMenu extends AccessibleJMenuItem
  855. implements AccessibleSelection
  856. {
  857. private static final long serialVersionUID = -8131864021059524309L;
  858. protected AccessibleJMenu()
  859. {
  860. // Nothing to do here.
  861. }
  862. /**
  863. * Returns the number of accessible children of this object.
  864. *
  865. * @return the number of accessible children of this object
  866. */
  867. public int getAccessibleChildrenCount()
  868. {
  869. Component[] children = getMenuComponents();
  870. int count = 0;
  871. for (int i = 0; i < children.length; i++)
  872. {
  873. if (children[i] instanceof Accessible)
  874. count++;
  875. }
  876. return count;
  877. }
  878. /**
  879. * Returns the accessible child with the specified <code>index</code>.
  880. *
  881. * @param index the index of the child to fetch
  882. *
  883. * @return the accessible child with the specified <code>index</code>
  884. */
  885. public Accessible getAccessibleChild(int index)
  886. {
  887. Component[] children = getMenuComponents();
  888. int count = 0;
  889. Accessible found = null;
  890. for (int i = 0; i < children.length; i++)
  891. {
  892. if (children[i] instanceof Accessible)
  893. {
  894. if (count == index)
  895. {
  896. found = (Accessible) children[i];
  897. break;
  898. }
  899. count++;
  900. }
  901. }
  902. return found;
  903. }
  904. /**
  905. * Returns the accessible selection of this object. AccessibleJMenus handle
  906. * their selection themselves, so we always return <code>this</code> here.
  907. *
  908. * @return the accessible selection of this object
  909. */
  910. public AccessibleSelection getAccessibleSelection()
  911. {
  912. return this;
  913. }
  914. /**
  915. * Returns the selected accessible child with the specified
  916. * <code>index</code>.
  917. *
  918. * @param index the index of the accessible selected child to return
  919. *
  920. * @return the selected accessible child with the specified
  921. * <code>index</code>
  922. */
  923. public Accessible getAccessibleSelection(int index)
  924. {
  925. Accessible selected = null;
  926. // Only one item can be selected, which must therefore have index == 0.
  927. if (index == 0)
  928. {
  929. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  930. MenuElement[] me = msm.getSelectedPath();
  931. if (me != null)
  932. {
  933. for (int i = 0; i < me.length; i++)
  934. {
  935. if (me[i] == JMenu.this)
  936. {
  937. // This JMenu is selected, find and return the next
  938. // JMenuItem in the path.
  939. do
  940. {
  941. if (me[i] instanceof Accessible)
  942. {
  943. selected = (Accessible) me[i];
  944. break;
  945. }
  946. i++;
  947. } while (i < me.length);
  948. }
  949. if (selected != null)
  950. break;
  951. }
  952. }
  953. }
  954. return selected;
  955. }
  956. /**
  957. * Returns <code>true</code> if the accessible child with the specified
  958. * index is selected, <code>false</code> otherwise.
  959. *
  960. * @param index the index of the accessible child to check
  961. *
  962. * @return <code>true</code> if the accessible child with the specified
  963. * index is selected, <code>false</code> otherwise
  964. */
  965. public boolean isAccessibleChildSelected(int index)
  966. {
  967. boolean selected = false;
  968. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  969. MenuElement[] me = msm.getSelectedPath();
  970. if (me != null)
  971. {
  972. Accessible toBeFound = getAccessibleChild(index);
  973. for (int i = 0; i < me.length; i++)
  974. {
  975. if (me[i] == toBeFound)
  976. {
  977. selected = true;
  978. break;
  979. }
  980. }
  981. }
  982. return selected;
  983. }
  984. /**
  985. * Returns the accessible role of this object, which is
  986. * {@link AccessibleRole#MENU} for the AccessibleJMenu.
  987. *
  988. * @return the accessible role of this object
  989. */
  990. public AccessibleRole getAccessibleRole()
  991. {
  992. return AccessibleRole.MENU;
  993. }
  994. /**
  995. * Returns the number of selected accessible children. This will be
  996. * <code>0</code> if no item is selected, or <code>1</code> if an item
  997. * is selected. AccessibleJMenu can have maximum 1 selected item.
  998. *
  999. * @return the number of selected accessible children
  1000. */
  1001. public int getAccessibleSelectionCount()
  1002. {
  1003. int count = 0;
  1004. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  1005. MenuElement[] me = msm.getSelectedPath();
  1006. if (me != null)
  1007. {
  1008. for (int i = 0; i < me.length; i++)
  1009. {
  1010. if (me[i] == JMenu.this)
  1011. {
  1012. if (i + 1 < me.length)
  1013. {
  1014. count = 1;
  1015. break;
  1016. }
  1017. }
  1018. }
  1019. }
  1020. return count;
  1021. }
  1022. /**
  1023. * Selects the accessible child with the specified index.
  1024. *
  1025. * @param index the index of the accessible child to select
  1026. */
  1027. public void addAccessibleSelection(int index)
  1028. {
  1029. Accessible child = getAccessibleChild(index);
  1030. if (child != null && child instanceof JMenuItem)
  1031. {
  1032. JMenuItem mi = (JMenuItem) child;
  1033. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  1034. msm.setSelectedPath(createPath(JMenu.this));
  1035. }
  1036. }
  1037. /**
  1038. * Removes the item with the specified index from the selection.
  1039. *
  1040. * @param index the index of the selected item to remove from the selection
  1041. */
  1042. public void removeAccessibleSelection(int index)
  1043. {
  1044. Accessible child = getAccessibleChild(index);
  1045. if (child != null)
  1046. {
  1047. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  1048. MenuElement[] oldSelection = msm.getSelectedPath();
  1049. for (int i = 0; i < oldSelection.length; i++)
  1050. {
  1051. if (oldSelection[i] == child)
  1052. {
  1053. // Found the specified child in the selection. Remove it
  1054. // from the selection.
  1055. MenuElement[] newSel = new MenuElement[i - 1];
  1056. System.arraycopy(oldSelection, 0, newSel, 0, i - 1);
  1057. msm.setSelectedPath(newSel);
  1058. break;
  1059. }
  1060. }
  1061. }
  1062. }
  1063. /**
  1064. * Removes all possibly selected accessible children of this object from
  1065. * the selection.
  1066. */
  1067. public void clearAccessibleSelection()
  1068. {
  1069. MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  1070. MenuElement[] oldSelection = msm.getSelectedPath();
  1071. for (int i = 0; i < oldSelection.length; i++)
  1072. {
  1073. if (oldSelection[i] == JMenu.this)
  1074. {
  1075. // Found this menu in the selection. Remove all children from
  1076. // the selection.
  1077. MenuElement[] newSel = new MenuElement[i];
  1078. System.arraycopy(oldSelection, 0, newSel, 0, i);
  1079. msm.setSelectedPath(newSel);
  1080. break;
  1081. }
  1082. }
  1083. }
  1084. /**
  1085. * AccessibleJMenu don't support multiple selection, so this method
  1086. * does nothing.
  1087. */
  1088. public void selectAllAccessibleSelection()
  1089. {
  1090. // Nothing to do here.
  1091. }
  1092. }
  1093. protected class WinListener extends WindowAdapter implements Serializable
  1094. {
  1095. private static final long serialVersionUID = -6415815570638474823L;
  1096. /**
  1097. * Creates a new <code>WinListener</code>.
  1098. *
  1099. * @param popup the popup menu which is observed
  1100. */
  1101. public WinListener(JPopupMenu popup)
  1102. {
  1103. // TODO: What should we do with the popup argument?
  1104. }
  1105. /**
  1106. * Receives notification when the popup menu is closing and deselects
  1107. * the menu.
  1108. *
  1109. * @param event the window event
  1110. */
  1111. public void windowClosing(WindowEvent event)
  1112. {
  1113. setSelected(false);
  1114. }
  1115. }
  1116. /**
  1117. * This class listens to PropertyChangeEvents occuring in menu's action
  1118. */
  1119. private class ActionChangedListener implements PropertyChangeListener
  1120. {
  1121. /** menu item associated with the action */
  1122. private JMenuItem menuItem;
  1123. /** Creates new ActionChangedListener and adds it to menuItem's action */
  1124. public ActionChangedListener(JMenuItem menuItem)
  1125. {
  1126. this.menuItem = menuItem;
  1127. Action a = menuItem.getAction();
  1128. if (a != null)
  1129. a.addPropertyChangeListener(this);
  1130. }
  1131. /**This method is invoked when some change occures in menuItem's action*/
  1132. public void propertyChange(PropertyChangeEvent evt)
  1133. {
  1134. // FIXME: Need to implement
  1135. }
  1136. }
  1137. /**
  1138. * Creates an array to be feeded as argument to
  1139. * {@link MenuSelectionManager#setSelectedPath(MenuElement[])} for the
  1140. * specified element. This has the effect of selecting the specified element
  1141. * and all its parents.
  1142. *
  1143. * @param leaf the leaf element for which to create the selected path
  1144. *
  1145. * @return the selected path array
  1146. */
  1147. MenuElement[] createPath(JMenu leaf)
  1148. {
  1149. ArrayList path = new ArrayList();
  1150. MenuElement[] array = null;
  1151. Component current = leaf.getPopupMenu();
  1152. while (true)
  1153. {
  1154. if (current instanceof JPopupMenu)
  1155. {
  1156. JPopupMenu popupMenu = (JPopupMenu) current;
  1157. path.add(0, popupMenu);
  1158. current = popupMenu.getInvoker();
  1159. }
  1160. else if (current instanceof JMenu)
  1161. {
  1162. JMenu menu = (JMenu) current;
  1163. path.add(0, menu);
  1164. current = menu.getParent();
  1165. }
  1166. else if (current instanceof JMenuBar)
  1167. {
  1168. JMenuBar menuBar = (JMenuBar) current;
  1169. path.add(0, menuBar);
  1170. array = new MenuElement[path.size()];
  1171. array = (MenuElement[]) path.toArray(array);
  1172. break;
  1173. }
  1174. }
  1175. return array;
  1176. }
  1177. }