1. /*
  2. * @(#)JPopupMenu.java 1.191 04/05/18
  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.*;
  9. import java.awt.event.*;
  10. import java.io.IOException;
  11. import java.io.ObjectInputStream;
  12. import java.io.ObjectOutputStream;
  13. import java.io.Serializable;
  14. import java.beans.*;
  15. import java.util.Locale;
  16. import java.util.Vector;
  17. import java.util.Hashtable;
  18. import javax.accessibility.*;
  19. import javax.swing.plaf.PopupMenuUI;
  20. import javax.swing.plaf.ComponentUI;
  21. import javax.swing.plaf.basic.BasicComboPopup;
  22. import javax.swing.event.*;
  23. import java.applet.Applet;
  24. /**
  25. * An implementation of a popup menu -- a small window that pops up
  26. * and displays a series of choices. A <code>JPopupMenu</code> is used for the
  27. * menu that appears when the user selects an item on the menu bar.
  28. * It is also used for "pull-right" menu that appears when the
  29. * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
  30. * can also be used anywhere else you want a menu to appear. For
  31. * example, when the user right-clicks in a specified area.
  32. * <p>
  33. * For information and examples of using popup menus, see
  34. * <a
  35. href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
  36. * in <em>The Java Tutorial.</em>
  37. * <p>
  38. * <strong>Warning:</strong>
  39. * Serialized objects of this class will not be compatible with
  40. * future Swing releases. The current serialization support is
  41. * appropriate for short term storage or RMI between applications running
  42. * the same version of Swing. As of 1.4, support for long term storage
  43. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  44. * has been added to the <code>java.beans</code> package.
  45. * Please see {@link java.beans.XMLEncoder}.
  46. *
  47. * @beaninfo
  48. * attribute: isContainer false
  49. * description: A small window that pops up and displays a series of choices.
  50. *
  51. * @version 1.191 @(#)JPopupMenu.java 1.191
  52. * @author Georges Saab
  53. * @author David Karlton
  54. * @author Arnaud Weber
  55. */
  56. public class JPopupMenu extends JComponent implements Accessible,MenuElement {
  57. /**
  58. * @see #getUIClassID
  59. * @see #readObject
  60. */
  61. private static final String uiClassID = "PopupMenuUI";
  62. /**
  63. * Key used in AppContext to determine if light way popups are the default.
  64. */
  65. private static final Object defaultLWPopupEnabledKey =
  66. new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
  67. /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
  68. static boolean popupPostionFixDisabled = false;
  69. static {
  70. popupPostionFixDisabled = java.security.AccessController.doPrivileged(
  71. new sun.security.action.GetPropertyAction(
  72. "javax.swing.adjustPopupLocationToFit","")).equals("false");
  73. }
  74. transient Component invoker;
  75. transient Popup popup;
  76. transient Frame frame;
  77. private int desiredLocationX,desiredLocationY;
  78. private String label = null;
  79. private boolean paintBorder = true;
  80. private Insets margin = null;
  81. /**
  82. * Used to indicate if lightweight popups should be used.
  83. */
  84. private boolean lightWeightPopup = true;
  85. /*
  86. * Model for the selected subcontrol.
  87. */
  88. private SingleSelectionModel selectionModel;
  89. /* Lock object used in place of class object for synchronization.
  90. * (4187686)
  91. */
  92. private static final Object classLock = new Object();
  93. /* diagnostic aids -- should be false for production builds. */
  94. private static final boolean TRACE = false; // trace creates and disposes
  95. private static final boolean VERBOSE = false; // show reuse hits/misses
  96. private static final boolean DEBUG = false; // show bad params, misc.
  97. /**
  98. * Sets the default value of the <code>lightWeightPopupEnabled</code>
  99. * property.
  100. *
  101. * @param aFlag <code>true</code> if popups can be lightweight,
  102. * otherwise <code>false</code>
  103. * @see #getDefaultLightWeightPopupEnabled
  104. * @see #setLightWeightPopupEnabled
  105. */
  106. public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
  107. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  108. Boolean.valueOf(aFlag));
  109. }
  110. /**
  111. * Gets the <code>defaultLightWeightPopupEnabled</code> property,
  112. * which by default is <code>true</code>.
  113. *
  114. * @return the value of the <code>defaultLightWeightPopupEnabled</code>
  115. * property
  116. *
  117. * @see #setDefaultLightWeightPopupEnabled
  118. */
  119. public static boolean getDefaultLightWeightPopupEnabled() {
  120. Boolean b = (Boolean)
  121. SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
  122. if (b == null) {
  123. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  124. Boolean.TRUE);
  125. return true;
  126. }
  127. return b.booleanValue();
  128. }
  129. /**
  130. * Constructs a <code>JPopupMenu</code> without an "invoker".
  131. */
  132. public JPopupMenu() {
  133. this(null);
  134. }
  135. /**
  136. * Constructs a <code>JPopupMenu</code> with the specified title.
  137. *
  138. * @param label the string that a UI may use to display as a title
  139. * for the popup menu.
  140. */
  141. public JPopupMenu(String label) {
  142. this.label = label;
  143. lightWeightPopup = getDefaultLightWeightPopupEnabled();
  144. setSelectionModel(new DefaultSingleSelectionModel());
  145. enableEvents(AWTEvent.MOUSE_EVENT_MASK);
  146. setFocusTraversalKeysEnabled(false);
  147. updateUI();
  148. }
  149. /**
  150. * Returns the look and feel (L&F) object that renders this component.
  151. *
  152. * @return the <code>PopupMenuUI</code> object that renders this component
  153. */
  154. public PopupMenuUI getUI() {
  155. return (PopupMenuUI)ui;
  156. }
  157. /**
  158. * Sets the L&F object that renders this component.
  159. *
  160. * @param ui the new <code>PopupMenuUI</code> L&F object
  161. * @see UIDefaults#getUI
  162. * @beaninfo
  163. * bound: true
  164. * hidden: true
  165. * attribute: visualUpdate true
  166. * description: The UI object that implements the Component's LookAndFeel.
  167. */
  168. public void setUI(PopupMenuUI ui) {
  169. super.setUI(ui);
  170. }
  171. /**
  172. * Resets the UI property to a value from the current look and feel.
  173. *
  174. * @see JComponent#updateUI
  175. */
  176. public void updateUI() {
  177. setUI((PopupMenuUI)UIManager.getUI(this));
  178. }
  179. /**
  180. * Returns the name of the L&F class that renders this component.
  181. *
  182. * @return the string "PopupMenuUI"
  183. * @see JComponent#getUIClassID
  184. * @see UIDefaults#getUI
  185. */
  186. public String getUIClassID() {
  187. return uiClassID;
  188. }
  189. protected void processFocusEvent(FocusEvent evt) {
  190. super.processFocusEvent(evt);
  191. }
  192. /**
  193. * Processes key stroke events such as mnemonics and accelerators.
  194. *
  195. * @param evt the key event to be processed
  196. */
  197. protected void processKeyEvent(KeyEvent evt) {
  198. MenuSelectionManager.defaultManager().processKeyEvent(evt);
  199. if (evt.isConsumed()) {
  200. return;
  201. }
  202. super.processKeyEvent(evt);
  203. }
  204. /**
  205. * Returns the model object that handles single selections.
  206. *
  207. * @return the <code>selectionModel</code> property
  208. * @see SingleSelectionModel
  209. */
  210. public SingleSelectionModel getSelectionModel() {
  211. return selectionModel;
  212. }
  213. /**
  214. * Sets the model object to handle single selections.
  215. *
  216. * @param model the new <code>SingleSelectionModel</code>
  217. * @see SingleSelectionModel
  218. * @beaninfo
  219. * description: The selection model for the popup menu
  220. * expert: true
  221. */
  222. public void setSelectionModel(SingleSelectionModel model) {
  223. selectionModel = model;
  224. }
  225. /**
  226. * Appends the specified menu item to the end of this menu.
  227. *
  228. * @param menuItem the <code>JMenuItem</code> to add
  229. * @return the <code>JMenuItem</code> added
  230. */
  231. public JMenuItem add(JMenuItem menuItem) {
  232. super.add(menuItem);
  233. return menuItem;
  234. }
  235. /**
  236. * Creates a new menu item with the specified text and appends
  237. * it to the end of this menu.
  238. *
  239. * @param s the string for the menu item to be added
  240. */
  241. public JMenuItem add(String s) {
  242. return add(new JMenuItem(s));
  243. }
  244. /**
  245. * Appends a new menu item to the end of the menu which
  246. * dispatches the specified <code>Action</code> object.
  247. *
  248. * As of JDK 1.3, this is no longer the preferred method for adding
  249. * <code>Actions</code> to
  250. * a container. Instead it is recommended to configure a control with
  251. * an action using <code>setAction</code>, and then add that control
  252. * directly to the <code>Container</code>.
  253. *
  254. * @param a the <code>Action</code> to add to the menu
  255. * @return the new menu item
  256. * @see Action
  257. */
  258. public JMenuItem add(Action a) {
  259. JMenuItem mi = createActionComponent(a);
  260. mi.setAction(a);
  261. add(mi);
  262. return mi;
  263. }
  264. /**
  265. * Returns an point which has been adjusted to take into account of the
  266. * desktop bounds, taskbar and multi-monitor configuration.
  267. * <p>
  268. * This adustment may be cancelled by invoking the application with
  269. * -Djavax.swing.adjustPopupLocationToFit=false
  270. */
  271. Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
  272. Point p = new Point(xposition, yposition);
  273. if(popupPostionFixDisabled == true || GraphicsEnvironment.isHeadless())
  274. return p;
  275. Toolkit toolkit = Toolkit.getDefaultToolkit();
  276. Rectangle screenBounds;
  277. Insets screenInsets;
  278. GraphicsConfiguration gc = null;
  279. // Try to find GraphicsConfiguration, that includes mouse
  280. // pointer position
  281. GraphicsEnvironment ge =
  282. GraphicsEnvironment.getLocalGraphicsEnvironment();
  283. GraphicsDevice[] gd = ge.getScreenDevices();
  284. for(int i = 0; i < gd.length; i++) {
  285. if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
  286. GraphicsConfiguration dgc =
  287. gd[i].getDefaultConfiguration();
  288. if(dgc.getBounds().contains(p)) {
  289. gc = dgc;
  290. break;
  291. }
  292. }
  293. }
  294. // If not found and we have invoker, ask invoker about his gc
  295. if(gc == null && getInvoker() != null) {
  296. gc = getInvoker().getGraphicsConfiguration();
  297. }
  298. if(gc != null) {
  299. // If we have GraphicsConfiguration use it to get
  300. // screen bounds and insets
  301. screenInsets = toolkit.getScreenInsets(gc);
  302. screenBounds = gc.getBounds();
  303. } else {
  304. // If we don't have GraphicsConfiguration use primary screen
  305. // and empty insets
  306. screenInsets = new Insets(0, 0, 0, 0);
  307. screenBounds = new Rectangle(toolkit.getScreenSize());
  308. }
  309. int scrWidth = screenBounds.width -
  310. Math.abs(screenInsets.left+screenInsets.right);
  311. int scrHeight = screenBounds.height -
  312. Math.abs(screenInsets.top+screenInsets.bottom);
  313. Dimension size;
  314. size = JPopupMenu.this.getPreferredSize();
  315. if( (p.x + size.width) > screenBounds.x + scrWidth )
  316. p.x = screenBounds.x + scrWidth - size.width;
  317. if( (p.y + size.height) > screenBounds.y + scrHeight)
  318. p.y = screenBounds.y + scrHeight - size.height;
  319. /* Change is made to the desired (X,Y) values, when the
  320. PopupMenu is too tall OR too wide for the screen
  321. */
  322. if( p.x < screenBounds.x )
  323. p.x = screenBounds.x ;
  324. if( p.y < screenBounds.y )
  325. p.y = screenBounds.y;
  326. return p;
  327. }
  328. /**
  329. * Factory method which creates the <code>JMenuItem</code> for
  330. * <code>Actions</code> added to the <code>JPopupMenu</code>.
  331. * As of JDK 1.3, this is no
  332. * longer the preferred method, instead it is recommended to configure
  333. * a control with an action using <code>setAction</code>,
  334. * and then adding that
  335. * control directly to the <code>Container</code>.
  336. *
  337. * @param a the <code>Action</code> for the menu item to be added
  338. * @return the new menu item
  339. * @see Action
  340. *
  341. * @since 1.3
  342. */
  343. protected JMenuItem createActionComponent(Action a) {
  344. JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  345. (Icon)a.getValue(Action.SMALL_ICON)){
  346. protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
  347. PropertyChangeListener pcl = createActionChangeListener(this);
  348. if (pcl == null) {
  349. pcl = super.createActionPropertyChangeListener(a);
  350. }
  351. return pcl;
  352. }
  353. };
  354. mi.setHorizontalTextPosition(JButton.TRAILING);
  355. mi.setVerticalTextPosition(JButton.CENTER);
  356. mi.setEnabled(a.isEnabled());
  357. return mi;
  358. }
  359. /**
  360. * Returns a properly configured <code>PropertyChangeListener</code>
  361. * which updates the control as changes to the <code>Action</code> occur.
  362. * As of JDK 1.3, this is no longer the preferred method for adding
  363. * <code>Actions</code> to
  364. * a container. Instead it is recommended to configure a control with
  365. * an action using <code>setAction</code>, and then add that control
  366. * directly to the <code>Container</code>.
  367. */
  368. protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
  369. return new ActionChangedListener(b);
  370. }
  371. private class ActionChangedListener implements PropertyChangeListener, Serializable {
  372. private JMenuItem menuItem;
  373. public ActionChangedListener(JMenuItem mi) {
  374. super();
  375. setTarget(mi);
  376. }
  377. public void propertyChange(PropertyChangeEvent e) {
  378. String propertyName = e.getPropertyName();
  379. if (e.getPropertyName().equals(Action.NAME)) {
  380. String text = (String) e.getNewValue();
  381. menuItem.setText(text);
  382. } else if (propertyName.equals("enabled")) {
  383. Boolean enabledState = (Boolean) e.getNewValue();
  384. menuItem.setEnabled(enabledState.booleanValue());
  385. } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  386. Icon icon = (Icon) e.getNewValue();
  387. menuItem.setIcon(icon);
  388. menuItem.invalidate();
  389. menuItem.repaint();
  390. }
  391. }
  392. public void setTarget(JMenuItem b) {
  393. this.menuItem = b;
  394. }
  395. }
  396. /**
  397. * Removes the component at the specified index from this popup menu.
  398. *
  399. * @param pos the position of the item to be removed
  400. * @exception IllegalArgumentException if the value of
  401. * <code>pos</code> < 0, or if the value of
  402. * <code>pos</code> is greater than the
  403. * number of items
  404. */
  405. public void remove(int pos) {
  406. if (pos < 0) {
  407. throw new IllegalArgumentException("index less than zero.");
  408. }
  409. if (pos > getComponentCount() -1) {
  410. throw new IllegalArgumentException("index greater than the number of items.");
  411. }
  412. super.remove(pos);
  413. }
  414. /**
  415. * Sets the value of the <code>lightWeightPopupEnabled</code> property,
  416. * which by default is <code>true</code>.
  417. * By default, when a look and feel displays a popup,
  418. * it can choose to
  419. * use a lightweight (all-Java) popup.
  420. * Lightweight popup windows are more efficient than heavyweight
  421. * (native peer) windows,
  422. * but lightweight and heavyweight components do not mix well in a GUI.
  423. * If your application mixes lightweight and heavyweight components,
  424. * you should disable lightweight popups.
  425. * Some look and feels might always use heavyweight popups,
  426. * no matter what the value of this property.
  427. *
  428. * @param aFlag <code>false</code> to disable lightweight popups
  429. * @beaninfo
  430. * description: Determines whether lightweight popups are used when possible
  431. * expert: true
  432. *
  433. * @see #isLightWeightPopupEnabled
  434. */
  435. public void setLightWeightPopupEnabled(boolean aFlag) {
  436. // NOTE: this use to set the flag on a shared JPopupMenu, which meant
  437. // this effected ALL JPopupMenus.
  438. lightWeightPopup = aFlag;
  439. }
  440. /**
  441. * Gets the <code>lightWeightPopupEnabled</code> property.
  442. *
  443. * @return the value of the <code>lightWeightPopupEnabled</code> property
  444. * @see #setLightWeightPopupEnabled
  445. */
  446. public boolean isLightWeightPopupEnabled() {
  447. return lightWeightPopup;
  448. }
  449. /**
  450. * Returns the popup menu's label
  451. *
  452. * @return a string containing the popup menu's label
  453. * @see #setLabel
  454. */
  455. public String getLabel() {
  456. return label;
  457. }
  458. /**
  459. * Sets the popup menu's label. Different look and feels may choose
  460. * to display or not display this.
  461. *
  462. * @param label a string specifying the label for the popup menu
  463. *
  464. * @see #setLabel
  465. * @beaninfo
  466. * description: The label for the popup menu.
  467. * bound: true
  468. */
  469. public void setLabel(String label) {
  470. String oldValue = this.label;
  471. this.label = label;
  472. firePropertyChange("label", oldValue, label);
  473. if (accessibleContext != null) {
  474. accessibleContext.firePropertyChange(
  475. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  476. oldValue, label);
  477. }
  478. invalidate();
  479. repaint();
  480. }
  481. /**
  482. * Appends a new separator at the end of the menu.
  483. */
  484. public void addSeparator() {
  485. add( new JPopupMenu.Separator() );
  486. }
  487. /**
  488. * Inserts a menu item for the specified <code>Action</code> object at
  489. * a given position.
  490. *
  491. * @param a the <code>Action</code> object to insert
  492. * @param index specifies the position at which to insert the
  493. * <code>Action</code>, where 0 is the first
  494. * @exception IllegalArgumentException if <code>index</code> < 0
  495. * @see Action
  496. */
  497. public void insert(Action a, int index) {
  498. JMenuItem mi = createActionComponent(a);
  499. mi.setAction(a);
  500. insert(mi, index);
  501. }
  502. /**
  503. * Inserts the specified component into the menu at a given
  504. * position.
  505. *
  506. * @param component the <code>Component</code> to insert
  507. * @param index specifies the position at which
  508. * to insert the component, where 0 is the first
  509. * @exception IllegalArgumentException if <code>index</code> < 0
  510. */
  511. public void insert(Component component, int index) {
  512. if (index < 0) {
  513. throw new IllegalArgumentException("index less than zero.");
  514. }
  515. int nitems = getComponentCount();
  516. // PENDING(ges): Why not use an array?
  517. Vector tempItems = new Vector();
  518. /* Remove the item at index, nitems-index times
  519. storing them in a temporary vector in the
  520. order they appear on the menu.
  521. */
  522. for (int i = index ; i < nitems; i++) {
  523. tempItems.addElement(getComponent(index));
  524. remove(index);
  525. }
  526. add(component);
  527. /* Add the removed items back to the menu, they are
  528. already in the correct order in the temp vector.
  529. */
  530. for (int i = 0; i < tempItems.size() ; i++) {
  531. add((Component)tempItems.elementAt(i));
  532. }
  533. }
  534. /**
  535. * Adds a <code>PopupMenu</code> listener.
  536. *
  537. * @param l the <code>PopupMenuListener</code> to add
  538. */
  539. public void addPopupMenuListener(PopupMenuListener l) {
  540. listenerList.add(PopupMenuListener.class,l);
  541. }
  542. /**
  543. * Removes a <code>PopupMenu</code> listener.
  544. *
  545. * @param l the <code>PopupMenuListener</code> to remove
  546. */
  547. public void removePopupMenuListener(PopupMenuListener l) {
  548. listenerList.remove(PopupMenuListener.class,l);
  549. }
  550. /**
  551. * Returns an array of all the <code>PopupMenuListener</code>s added
  552. * to this JMenuItem with addPopupMenuListener().
  553. *
  554. * @return all of the <code>PopupMenuListener</code>s added or an empty
  555. * array if no listeners have been added
  556. * @since 1.4
  557. */
  558. public PopupMenuListener[] getPopupMenuListeners() {
  559. return (PopupMenuListener[])listenerList.getListeners(
  560. PopupMenuListener.class);
  561. }
  562. /**
  563. * Adds a <code>MenuKeyListener</code> to the popup menu.
  564. *
  565. * @param l the <code>MenuKeyListener</code> to be added
  566. * @since 1.5
  567. */
  568. public void addMenuKeyListener(MenuKeyListener l) {
  569. listenerList.add(MenuKeyListener.class, l);
  570. }
  571. /**
  572. * Removes a <code>MenuKeyListener</code> from the popup menu.
  573. *
  574. * @param l the <code>MenuKeyListener</code> to be removed
  575. * @since 1.5
  576. */
  577. public void removeMenuKeyListener(MenuKeyListener l) {
  578. listenerList.remove(MenuKeyListener.class, l);
  579. }
  580. /**
  581. * Returns an array of all the <code>MenuKeyListener</code>s added
  582. * to this JPopupMenu with addMenuKeyListener().
  583. *
  584. * @return all of the <code>MenuKeyListener</code>s added or an empty
  585. * array if no listeners have been added
  586. * @since 1.5
  587. */
  588. public MenuKeyListener[] getMenuKeyListeners() {
  589. return (MenuKeyListener[])listenerList.getListeners(
  590. MenuKeyListener.class);
  591. }
  592. /**
  593. * Notifies <code>PopupMenuListener</code>s that this popup menu will
  594. * become visible.
  595. */
  596. protected void firePopupMenuWillBecomeVisible() {
  597. Object[] listeners = listenerList.getListenerList();
  598. PopupMenuEvent e=null;
  599. for (int i = listeners.length-2; i>=0; i-=2) {
  600. if (listeners[i]==PopupMenuListener.class) {
  601. if (e == null)
  602. e = new PopupMenuEvent(this);
  603. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
  604. }
  605. }
  606. }
  607. /**
  608. * Notifies <code>PopupMenuListener</code>s that this popup menu will
  609. * become invisible.
  610. */
  611. protected void firePopupMenuWillBecomeInvisible() {
  612. Object[] listeners = listenerList.getListenerList();
  613. PopupMenuEvent e=null;
  614. for (int i = listeners.length-2; i>=0; i-=2) {
  615. if (listeners[i]==PopupMenuListener.class) {
  616. if (e == null)
  617. e = new PopupMenuEvent(this);
  618. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
  619. }
  620. }
  621. }
  622. /**
  623. * Notifies <code>PopupMenuListeners</code> that this popup menu is
  624. * cancelled.
  625. */
  626. protected void firePopupMenuCanceled() {
  627. Object[] listeners = listenerList.getListenerList();
  628. PopupMenuEvent e=null;
  629. for (int i = listeners.length-2; i>=0; i-=2) {
  630. if (listeners[i]==PopupMenuListener.class) {
  631. if (e == null)
  632. e = new PopupMenuEvent(this);
  633. ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
  634. }
  635. }
  636. }
  637. /**
  638. * Always returns true since popups, by definition, should always
  639. * be on top of all other windows.
  640. * @return true
  641. */
  642. // package private
  643. boolean alwaysOnTop() {
  644. return true;
  645. }
  646. /**
  647. * Lays out the container so that it uses the minimum space
  648. * needed to display its contents.
  649. */
  650. public void pack() {
  651. if(popup != null) {
  652. Dimension pref = getPreferredSize();
  653. if (pref == null || pref.width != getWidth() ||
  654. pref.height != getHeight()) {
  655. popup = getPopup();
  656. } else {
  657. validate();
  658. }
  659. }
  660. }
  661. /**
  662. * Sets the visibility of the popup menu.
  663. *
  664. * @param b true to make the popup visible, or false to
  665. * hide it
  666. * @beaninfo
  667. * bound: true
  668. * description: Makes the popup visible
  669. */
  670. public void setVisible(boolean b) {
  671. if (DEBUG) {
  672. System.out.println("JPopupMenu.setVisible " + b);
  673. }
  674. // Is it a no-op?
  675. if (b == isVisible())
  676. return;
  677. // if closing, first close all Submenus
  678. if (b == false) {
  679. // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
  680. // a protected method and cannot be called from BasicPopupMenuUI directly
  681. // The real solution could be to make
  682. // firePopupMenuCanceled public and call it directly.
  683. Boolean doCanceled = (Boolean)getClientProperty("JPopupMenu.firePopupMenuCanceled");
  684. if (doCanceled != null && doCanceled == Boolean.TRUE) {
  685. putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.FALSE);
  686. firePopupMenuCanceled();
  687. }
  688. getSelectionModel().clearSelection();
  689. } else {
  690. // This is a popup menu with MenuElement children,
  691. // set selection path before popping up!
  692. if (isPopupMenu()) {
  693. if (getSubElements().length > 0) {
  694. MenuElement me[] = new MenuElement[2];
  695. me[0]=(MenuElement)this;
  696. me[1]=getSubElements()[0];
  697. MenuSelectionManager.defaultManager().setSelectedPath(me);
  698. } else {
  699. MenuElement me[] = new MenuElement[1];
  700. me[0]=(MenuElement)this;
  701. MenuSelectionManager.defaultManager().setSelectedPath(me);
  702. }
  703. }
  704. }
  705. if(b) {
  706. firePopupMenuWillBecomeVisible();
  707. popup = getPopup();
  708. firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
  709. } else if(popup != null) {
  710. firePopupMenuWillBecomeInvisible();
  711. popup.hide();
  712. popup = null;
  713. firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
  714. // 4694797: When popup menu is made invisible, selected path
  715. // should be cleared
  716. if (isPopupMenu()) {
  717. MenuSelectionManager.defaultManager().clearSelectedPath();
  718. }
  719. }
  720. }
  721. /**
  722. * Returns a <code>Popup</code> instance from the
  723. * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
  724. * it. If the current <code>popup</code> is non-null,
  725. * this will invoke <code>dispose</code> of it, and then
  726. * <code>show</code> the new one.
  727. * <p>
  728. * This does NOT fire any events, it is up the caller to dispatch
  729. * the necessary events.
  730. */
  731. private Popup getPopup() {
  732. Popup oldPopup = popup;
  733. if (oldPopup != null) {
  734. oldPopup.hide();
  735. }
  736. PopupFactory popupFactory = PopupFactory.getSharedInstance();
  737. if (isLightWeightPopupEnabled()) {
  738. popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
  739. }
  740. else {
  741. popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
  742. }
  743. // adjust the location of the popup
  744. Point p = adjustPopupLocationToFitScreen(desiredLocationX,desiredLocationY);
  745. desiredLocationX = p.x;
  746. desiredLocationY = p.y;
  747. Popup newPopup = getUI().getPopup(this, desiredLocationX,
  748. desiredLocationY);
  749. popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
  750. newPopup.show();
  751. return newPopup;
  752. }
  753. /**
  754. * Returns true if the popup menu is visible (currently
  755. * being displayed).
  756. */
  757. public boolean isVisible() {
  758. if(popup != null)
  759. return true;
  760. else
  761. return false;
  762. }
  763. /**
  764. * Sets the location of the upper left corner of the
  765. * popup menu using x, y coordinates.
  766. *
  767. * @param x the x coordinate of the popup's new position
  768. * in the screen's coordinate space
  769. * @param y the y coordinate of the popup's new position
  770. * in the screen's coordinate space
  771. * @beaninfo
  772. * description: The location of the popup menu.
  773. */
  774. public void setLocation(int x, int y) {
  775. int oldX = desiredLocationX;
  776. int oldY = desiredLocationY;
  777. desiredLocationX = x;
  778. desiredLocationY = y;
  779. if(popup != null && (x != oldX || y != oldY)) {
  780. popup = getPopup();
  781. }
  782. }
  783. /**
  784. * Returns true if the popup menu is a standalone popup menu
  785. * rather than the submenu of a <code>JMenu</code>.
  786. *
  787. * @return true if this menu is a standalone popup menu, otherwise false
  788. */
  789. private boolean isPopupMenu() {
  790. return ((invoker != null) && !(invoker instanceof JMenu));
  791. }
  792. /**
  793. * Returns the component which is the 'invoker' of this
  794. * popup menu.
  795. *
  796. * @return the <code>Component</code> in which the popup menu is displayed
  797. */
  798. public Component getInvoker() {
  799. return this.invoker;
  800. }
  801. /**
  802. * Sets the invoker of this popup menu -- the component in which
  803. * the popup menu menu is to be displayed.
  804. *
  805. * @param invoker the <code>Component</code> in which the popup
  806. * menu is displayed
  807. * @beaninfo
  808. * description: The invoking component for the popup menu
  809. * expert: true
  810. */
  811. public void setInvoker(Component invoker) {
  812. Component oldInvoker = this.invoker;
  813. this.invoker = invoker;
  814. if ((oldInvoker != this.invoker) && (ui != null)) {
  815. ui.uninstallUI(this);
  816. ui.installUI(this);
  817. }
  818. invalidate();
  819. }
  820. /**
  821. * Displays the popup menu at the position x,y in the coordinate
  822. * space of the component invoker.
  823. *
  824. * @param invoker the component in whose space the popup menu is to appear
  825. * @param x the x coordinate in invoker's coordinate space at which
  826. * the popup menu is to be displayed
  827. * @param y the y coordinate in invoker's coordinate space at which
  828. * the popup menu is to be displayed
  829. */
  830. public void show(Component invoker, int x, int y) {
  831. if (DEBUG) {
  832. System.out.println("in JPopupMenu.show " );
  833. }
  834. setInvoker(invoker);
  835. Frame newFrame = getFrame(invoker);
  836. if (newFrame != frame) {
  837. // Use the invoker's frame so that events
  838. // are propagated properly
  839. if (newFrame!=null) {
  840. this.frame = newFrame;
  841. if(popup != null) {
  842. setVisible(false);
  843. }
  844. }
  845. }
  846. Point invokerOrigin;
  847. if (invoker != null) {
  848. invokerOrigin = invoker.getLocationOnScreen();
  849. setLocation(invokerOrigin.x + x,
  850. invokerOrigin.y + y);
  851. } else {
  852. setLocation(x, y);
  853. }
  854. setVisible(true);
  855. }
  856. /**
  857. * Returns the popup menu which is at the root of the menu system
  858. * for this popup menu.
  859. *
  860. * @return the topmost grandparent <code>JPopupMenu</code>
  861. */
  862. JPopupMenu getRootPopupMenu() {
  863. JPopupMenu mp = this;
  864. while((mp!=null) && (mp.isPopupMenu()!=true) &&
  865. (mp.getInvoker() != null) &&
  866. (mp.getInvoker().getParent() != null) &&
  867. (mp.getInvoker().getParent() instanceof JPopupMenu)
  868. ) {
  869. mp = (JPopupMenu) mp.getInvoker().getParent();
  870. }
  871. return mp;
  872. }
  873. /**
  874. * Returns the component at the specified index.
  875. *
  876. * @param i the index of the component, where 0 is the first
  877. * @return the <code>Component</code> at that index
  878. * @deprecated replaced by <code>getComponent(int i)</code>
  879. */
  880. @Deprecated
  881. public Component getComponentAtIndex(int i) {
  882. return getComponent(i);
  883. }
  884. /**
  885. * Returns the index of the specified component.
  886. *
  887. * @param c the <code>Component</code> to find
  888. * @return the index of the component, where 0 is the first;
  889. * or -1 if the component is not found
  890. */
  891. public int getComponentIndex(Component c) {
  892. int ncomponents = this.getComponentCount();
  893. Component[] component = this.getComponents();
  894. for (int i = 0 ; i < ncomponents ; i++) {
  895. Component comp = component[i];
  896. if (comp == c)
  897. return i;
  898. }
  899. return -1;
  900. }
  901. /**
  902. * Sets the size of the Popup window using a <code>Dimension</code> object.
  903. * This is equivalent to <code>setPreferredSize(d)</code>.
  904. *
  905. * @param d the <code>Dimension</code> specifying the new size
  906. * of this component.
  907. * @beaninfo
  908. * description: The size of the popup menu
  909. */
  910. public void setPopupSize(Dimension d) {
  911. Dimension oldSize = getPreferredSize();
  912. setPreferredSize(d);
  913. if (popup != null) {
  914. Dimension newSize = getPreferredSize();
  915. if (!oldSize.equals(newSize)) {
  916. popup = getPopup();
  917. }
  918. }
  919. }
  920. /**
  921. * Sets the size of the Popup window to the specified width and
  922. * height. This is equivalent to
  923. * <code>setPreferredSize(new Dimension(width, height))</code>.
  924. *
  925. * @param width the new width of the Popup in pixels
  926. * @param height the new height of the Popup in pixels
  927. * @beaninfo
  928. * description: The size of the popup menu
  929. */
  930. public void setPopupSize(int width, int height) {
  931. setPopupSize(new Dimension(width, height));
  932. }
  933. /**
  934. * Sets the currently selected component, This will result
  935. * in a change to the selection model.
  936. *
  937. * @param sel the <code>Component</code> to select
  938. * @beaninfo
  939. * description: The selected component on the popup menu
  940. * expert: true
  941. * hidden: true
  942. */
  943. public void setSelected(Component sel) {
  944. SingleSelectionModel model = getSelectionModel();
  945. int index = getComponentIndex(sel);
  946. model.setSelectedIndex(index);
  947. }
  948. /**
  949. * Checks whether the border should be painted.
  950. *
  951. * @return true if the border is painted, false otherwise
  952. * @see #setBorderPainted
  953. */
  954. public boolean isBorderPainted() {
  955. return paintBorder;
  956. }
  957. /**
  958. * Sets whether the border should be painted.
  959. *
  960. * @param b if true, the border is painted.
  961. * @see #isBorderPainted
  962. * @beaninfo
  963. * description: Is the border of the popup menu painted
  964. */
  965. public void setBorderPainted(boolean b) {
  966. paintBorder = b;
  967. repaint();
  968. }
  969. /**
  970. * Paints the popup menu's border if the <code>borderPainted</code>
  971. * property is <code>true</code>.
  972. * @param g the <code>Graphics</code> object
  973. *
  974. * @see JComponent#paint
  975. * @see JComponent#setBorder
  976. */
  977. protected void paintBorder(Graphics g) {
  978. if (isBorderPainted()) {
  979. super.paintBorder(g);
  980. }
  981. }
  982. /**
  983. * Returns the margin, in pixels, between the popup menu's border and
  984. * its containees.
  985. *
  986. * @return an <code>Insets</code> object containing the margin values.
  987. */
  988. public Insets getMargin() {
  989. if(margin == null) {
  990. return new Insets(0,0,0,0);
  991. } else {
  992. return margin;
  993. }
  994. }
  995. /**
  996. * Examines the list of menu items to determine whether
  997. * <code>popup</code> is a popup menu.
  998. *
  999. * @param popup a <code>JPopupMenu</code>
  1000. * @return true if <code>popup</code>
  1001. */
  1002. boolean isSubPopupMenu(JPopupMenu popup) {
  1003. int ncomponents = this.getComponentCount();
  1004. Component[] component = this.getComponents();
  1005. for (int i = 0 ; i < ncomponents ; i++) {
  1006. Component comp = component[i];
  1007. if (comp instanceof JMenu) {
  1008. JMenu menu = (JMenu)comp;
  1009. JPopupMenu subPopup = menu.getPopupMenu();
  1010. if (subPopup == popup)
  1011. return true;
  1012. if (subPopup.isSubPopupMenu(popup))
  1013. return true;
  1014. }
  1015. }
  1016. return false;
  1017. }
  1018. private static Frame getFrame(Component c) {
  1019. Component w = c;
  1020. while(!(w instanceof Frame) && (w!=null)) {
  1021. w = w.getParent();
  1022. }
  1023. return (Frame)w;
  1024. }
  1025. /**
  1026. * Returns a string representation of this <code>JPopupMenu</code>.
  1027. * This method
  1028. * is intended to be used only for debugging purposes, and the
  1029. * content and format of the returned string may vary between
  1030. * implementations. The returned string may be empty but may not
  1031. * be <code>null</code>.
  1032. *
  1033. * @return a string representation of this <code>JPopupMenu</code>.
  1034. */
  1035. protected String paramString() {
  1036. String labelString = (label != null ?
  1037. label : "");
  1038. String paintBorderString = (paintBorder ?
  1039. "true" : "false");
  1040. String marginString = (margin != null ?
  1041. margin.toString() : "");
  1042. String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ?
  1043. "true" : "false");
  1044. return super.paramString() +
  1045. ",desiredLocationX=" + desiredLocationX +
  1046. ",desiredLocationY=" + desiredLocationY +
  1047. ",label=" + labelString +
  1048. ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
  1049. ",margin=" + marginString +
  1050. ",paintBorder=" + paintBorderString;
  1051. }
  1052. /////////////////
  1053. // Accessibility support
  1054. ////////////////
  1055. /**
  1056. * Gets the AccessibleContext associated with this JPopupMenu.
  1057. * For JPopupMenus, the AccessibleContext takes the form of an
  1058. * AccessibleJPopupMenu.
  1059. * A new AccessibleJPopupMenu instance is created if necessary.
  1060. *
  1061. * @return an AccessibleJPopupMenu that serves as the
  1062. * AccessibleContext of this JPopupMenu
  1063. */
  1064. public AccessibleContext getAccessibleContext() {
  1065. if (accessibleContext == null) {
  1066. accessibleContext = new AccessibleJPopupMenu();
  1067. }
  1068. return accessibleContext;
  1069. }
  1070. /**
  1071. * This class implements accessibility support for the
  1072. * <code>JPopupMenu</code> class. It provides an implementation of the
  1073. * Java Accessibility API appropriate to popup menu user-interface
  1074. * elements.
  1075. */
  1076. protected class AccessibleJPopupMenu extends AccessibleJComponent
  1077. implements PropertyChangeListener {
  1078. /**
  1079. * AccessibleJPopupMenu constructor
  1080. *
  1081. * @since 1.5
  1082. */
  1083. protected AccessibleJPopupMenu() {
  1084. JPopupMenu.this.addPropertyChangeListener(this);
  1085. }
  1086. /**
  1087. * Get the role of this object.
  1088. *
  1089. * @return an instance of AccessibleRole describing the role of
  1090. * the object
  1091. */
  1092. public AccessibleRole getAccessibleRole() {
  1093. return AccessibleRole.POPUP_MENU;
  1094. }
  1095. /**
  1096. * This method gets called when a bound property is changed.
  1097. * @param evt A <code>PropertyChangeEvent</code> object describing
  1098. * the event source and the property that has changed. Must not be null.
  1099. *
  1100. * @throws NullPointerException if the parameter is null.
  1101. * @since 1.5
  1102. */
  1103. public void propertyChange(PropertyChangeEvent e) {
  1104. String propertyName = e.getPropertyName();
  1105. if (propertyName == "visible") {
  1106. if (e.getOldValue() == Boolean.FALSE &&
  1107. e.getNewValue() == Boolean.TRUE) {
  1108. handlePopupIsVisibleEvent(true);
  1109. } else if (e.getOldValue() == Boolean.TRUE &&
  1110. e.getNewValue() == Boolean.FALSE) {
  1111. handlePopupIsVisibleEvent(false);
  1112. }
  1113. }
  1114. }
  1115. /*
  1116. * Handles popup "visible" PropertyChangeEvent
  1117. */
  1118. private void handlePopupIsVisibleEvent(boolean visible) {
  1119. if (visible) {
  1120. // notify listeners that the popup became visible
  1121. firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
  1122. null, AccessibleState.VISIBLE);
  1123. // notify listeners that a popup list item is selected
  1124. fireActiveDescendant();
  1125. } else {
  1126. // notify listeners that the popup became hidden
  1127. firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
  1128. AccessibleState.VISIBLE, null);
  1129. }
  1130. }
  1131. /*
  1132. * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners
  1133. * on the popup menu invoker that a popup list item has been selected
  1134. */
  1135. private void fireActiveDescendant() {
  1136. if (JPopupMenu.this instanceof BasicComboPopup) {
  1137. // get the popup list
  1138. JList popupList = ((BasicComboPopup)JPopupMenu.this).getList();
  1139. if (popupList == null) {
  1140. return;
  1141. }
  1142. // get the first selected item
  1143. AccessibleContext ac = popupList.getAccessibleContext();
  1144. AccessibleSelection selection = ac.getAccessibleSelection();
  1145. if (selection == null) {
  1146. return;
  1147. }
  1148. Accessible a = selection.getAccessibleSelection(0);
  1149. if (a == null) {
  1150. return;
  1151. }
  1152. AccessibleContext selectedItem = a.getAccessibleContext();
  1153. // fire the event with the popup invoker as the source.
  1154. if (selectedItem != null && invoker != null) {
  1155. AccessibleContext invokerContext = invoker.getAccessibleContext();
  1156. if (invokerContext != null) {
  1157. // Check invokerContext because Component.getAccessibleContext
  1158. // returns null. Classes that extend Component are responsible
  1159. // for returning a non-null AccessibleContext.
  1160. invokerContext.firePropertyChange(
  1161. ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
  1162. null, selectedItem);
  1163. }
  1164. }
  1165. }
  1166. }
  1167. } // inner class AccessibleJPopupMenu
  1168. ////////////
  1169. // Serialization support.
  1170. ////////////
  1171. private void writeObject(ObjectOutputStream s) throws IOException {
  1172. Vector values = new Vector();
  1173. s.defaultWriteObject();
  1174. // Save the invoker, if its Serializable.
  1175. if(invoker != null && invoker instanceof Serializable) {
  1176. values.addElement("invoker");
  1177. values.addElement(invoker);
  1178. }
  1179. // Save the popup, if its Serializable.
  1180. if(popup != null && popup instanceof Serializable) {
  1181. values.addElement("popup");
  1182. values.addElement(popup);
  1183. }
  1184. s.writeObject(values);
  1185. if (getUIClassID().equals(uiClassID)) {
  1186. byte count = JComponent.getWriteObjCounter(this);
  1187. JComponent.setWriteObjCounter(this, --count);
  1188. if (count == 0 && ui != null) {
  1189. ui.installUI(this);
  1190. }
  1191. }
  1192. }
  1193. // implements javax.swing.MenuElement
  1194. private void readObject(ObjectInputStream s)
  1195. throws IOException, ClassNotFoundException {
  1196. s.defaultReadObject();
  1197. Vector values = (Vector)s.readObject();
  1198. int indexCounter = 0;
  1199. int maxCounter = values.size();
  1200. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1201. equals("invoker")) {
  1202. invoker = (Component)values.elementAt(++indexCounter);
  1203. indexCounter++;
  1204. }
  1205. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1206. equals("popup")) {
  1207. popup = (Popup)values.elementAt(++indexCounter);
  1208. indexCounter++;
  1209. }
  1210. }
  1211. /**
  1212. * This method is required to conform to the
  1213. * <code>MenuElement</code> interface, but it not implemented.
  1214. * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
  1215. */
  1216. public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
  1217. /**
  1218. * Processes a key event forwarded from the
  1219. * <code>MenuSelectionManager</code> and changes the menu selection,
  1220. * if necessary, by using <code>MenuSelectionManager</code>'s API.
  1221. * <p>
  1222. * Note: you do not have to forward the event to sub-components.
  1223. * This is done automatically by the <code>MenuSelectionManager</code>.
  1224. *
  1225. * @param e a <code>KeyEvent</code>
  1226. * @param path the <code>MenuElement</code> path array
  1227. * @param manager the <code>MenuSelectionManager</code>
  1228. */
  1229. public void processKeyEvent(KeyEvent e, MenuElement path[],
  1230. MenuSelectionManager manager) {
  1231. MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
  1232. e.getWhen(), e.getModifiers(),
  1233. e.getKeyCode(), e.getKeyChar(),
  1234. path, manager);
  1235. processMenuKeyEvent(mke);
  1236. if (mke.isConsumed()) {
  1237. e.consume();
  1238. }
  1239. }
  1240. /**
  1241. * Handles a keystroke in a menu.
  1242. *
  1243. * @param e a <code>MenuKeyEvent</code> object
  1244. * @since 1.5
  1245. */
  1246. private void processMenuKeyEvent(MenuKeyEvent e) {
  1247. switch (e.getID()) {
  1248. case KeyEvent.KEY_PRESSED:
  1249. fireMenuKeyPressed(e); break;
  1250. case KeyEvent.KEY_RELEASED:
  1251. fireMenuKeyReleased(e); break;
  1252. case KeyEvent.KEY_TYPED:
  1253. fireMenuKeyTyped(e); break;
  1254. default:
  1255. break;
  1256. }
  1257. }
  1258. /**
  1259. * Notifies all listeners that have registered interest for
  1260. * notification on this event type.
  1261. *
  1262. * @param event a <code>MenuKeyEvent</code>
  1263. * @see EventListenerList
  1264. */
  1265. private void fireMenuKeyPressed(MenuKeyEvent event) {
  1266. Object[] listeners = listenerList.getListenerList();
  1267. for (int i = listeners.length-2; i>=0; i-=2) {
  1268. if (listeners[i]==MenuKeyListener.class) {
  1269. ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
  1270. }
  1271. }
  1272. }
  1273. /**
  1274. * Notifies all listeners that have registered interest for
  1275. * notification on this event type.
  1276. *
  1277. * @param event a <code>MenuKeyEvent</code>
  1278. * @see EventListenerList
  1279. */
  1280. private void fireMenuKeyReleased(MenuKeyEvent event) {
  1281. Object[] listeners = listenerList.getListenerList();
  1282. for (int i = listeners.length-2; i>=0; i-=2) {
  1283. if (listeners[i]==MenuKeyListener.class) {
  1284. ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
  1285. }
  1286. }
  1287. }
  1288. /**
  1289. * Notifies all listeners that have registered interest for
  1290. * notification on this event type.
  1291. *
  1292. * @param event a <code>MenuKeyEvent</code>
  1293. * @see EventListenerList
  1294. */
  1295. private void fireMenuKeyTyped(MenuKeyEvent event) {
  1296. Object[] listeners = listenerList.getListenerList();
  1297. for (int i = listeners.length-2; i>=0; i-=2) {
  1298. if (listeners[i]==MenuKeyListener.class) {
  1299. ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
  1300. }
  1301. }
  1302. }
  1303. /**
  1304. * Messaged when the menubar selection changes to activate or
  1305. * deactivate this menu. This implements the
  1306. * <code>javax.swing.MenuElement</code> interface.
  1307. * Overrides <code>MenuElement.menuSelectionChanged</code>.
  1308. *
  1309. * @param isIncluded true if this menu is active, false if
  1310. * it is not
  1311. * @see MenuElement#menuSelectionChanged(boolean)
  1312. */
  1313. public void menuSelectionChanged(boolean isIncluded) {
  1314. if (DEBUG) {
  1315. System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded);
  1316. }
  1317. if(invoker instanceof JMenu) {
  1318. JMenu m = (JMenu) invoker;
  1319. if(isIncluded)
  1320. m.setPopupMenuVisible(true);
  1321. else
  1322. m.setPopupMenuVisible(false);
  1323. }
  1324. if (isPopupMenu() && !isIncluded)
  1325. setVisible(false);
  1326. }
  1327. /**
  1328. * Returns an array of <code>MenuElement</code>s containing the submenu
  1329. * for this menu component. It will only return items conforming to
  1330. * the <code>JMenuElement</code> interface.
  1331. * If popup menu is <code>null</code> returns
  1332. * an empty array. This method is required to conform to the
  1333. * <code>MenuElement</code> interface.
  1334. *
  1335. * @return an array of <code>MenuElement</code> objects
  1336. * @see MenuElement#getSubElements
  1337. */
  1338. public MenuElement[] getSubElements() {
  1339. MenuElement result[];
  1340. Vector tmp = new Vector();
  1341. int c = getComponentCount();
  1342. int i;
  1343. Component m;
  1344. for(i=0 ; i < c ; i++) {
  1345. m = getComponent(i);
  1346. if(m instanceof MenuElement)
  1347. tmp.addElement(m);
  1348. }
  1349. result = new MenuElement[tmp.size()];
  1350. for(i=0,c=tmp.size() ; i < c ; i++)
  1351. result[i] = (MenuElement) tmp.elementAt(i);
  1352. return result;
  1353. }
  1354. /**
  1355. * Returns this <code>JPopupMenu</code> component.
  1356. * @return this <code>JPopupMenu</code> object
  1357. * @see MenuElement#getComponent
  1358. */
  1359. public Component getComponent() {
  1360. return this;
  1361. }
  1362. /**
  1363. * A popup menu-specific separator.
  1364. */
  1365. static public class Separator extends JSeparator
  1366. {
  1367. public Separator( )
  1368. {
  1369. super( JSeparator.HORIZONTAL );
  1370. }
  1371. /**
  1372. * Returns the name of the L&F class that renders this component.
  1373. *
  1374. * @return the string "PopupMenuSeparatorUI"
  1375. * @see JComponent#getUIClassID
  1376. * @see UIDefaults#getUI
  1377. */
  1378. public String getUIClassID()
  1379. {
  1380. return "PopupMenuSeparatorUI";
  1381. }
  1382. }
  1383. /**
  1384. * Returns true if the <code>MouseEvent</code> is considered a popup trigger
  1385. * by the <code>JPopupMenu</code>'s currently installed UI.
  1386. *
  1387. * @return true if the mouse event is a popup trigger
  1388. * @since 1.3
  1389. */
  1390. public boolean isPopupTrigger(MouseEvent e) {
  1391. return getUI().isPopupTrigger(e);
  1392. }
  1393. }