1. /*
  2. * @(#)JPopupMenu.java 1.149 00/04/06
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.*;
  12. import java.awt.event.*;
  13. import java.io.IOException;
  14. import java.io.ObjectInputStream;
  15. import java.io.ObjectOutputStream;
  16. import java.io.Serializable;
  17. import java.beans.*;
  18. import java.util.Locale;
  19. import java.util.Vector;
  20. import java.util.Hashtable;
  21. import javax.accessibility.*;
  22. import javax.swing.plaf.PopupMenuUI;
  23. import javax.swing.plaf.ComponentUI;
  24. import javax.swing.event.*;
  25. import java.applet.Applet;
  26. /**
  27. * An implementation of a popup menu -- a small window that pops up
  28. * and displays a series of choices. A <code>JPopupMenu</code> is used for the
  29. * menu that appears when the user selects an item on the menu bar.
  30. * It is also used for "pull-right" menu that appears when the
  31. * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
  32. * can also be used anywhere else you want a menu to appear. For
  33. * example, when the user right-clicks in a specified area.
  34. * <p>
  35. * For information and examples of using popup menus, see
  36. * <a
  37. href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
  38. * in <em>The Java Tutorial.</em>
  39. * For the keyboard keys used by this component in the standard Look and
  40. * Feel (L&F) renditions, see the
  41. * <a href="doc-files/Key-Index.html#JPopupMenu">JPopupMenu</a> key assignments.
  42. * <p>
  43. * <strong>Warning:</strong>
  44. * Serialized objects of this class will not be compatible with
  45. * future Swing releases. The current serialization support is appropriate
  46. * for short term storage or RMI between applications running the same
  47. * version of Swing. A future release of Swing will provide support for
  48. * long term persistence.
  49. *
  50. * @beaninfo
  51. * attribute: isContainer false
  52. * description: A small window that pops up and displays a series of choices.
  53. *
  54. * @version 1.149 04/06/00
  55. * @author Georges Saab
  56. * @author David Karlton
  57. * @author Arnaud Weber
  58. */
  59. public class JPopupMenu extends JComponent implements Accessible,MenuElement {
  60. /**
  61. * @see #getUIClassID
  62. * @see #readObject
  63. */
  64. private static final String uiClassID = "PopupMenuUI";
  65. transient Component invoker;
  66. transient Popup popup;
  67. transient Frame frame;
  68. private int desiredLocationX,desiredLocationY;
  69. private static PopupFactory popupFactory = new DefaultPopupFactory();
  70. private String label = null;
  71. private boolean paintBorder = true;
  72. private Insets margin = null;
  73. private static final Object defaultLWPopupEnabledKey =
  74. new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
  75. private boolean lightWeightPopupEnabled = true;
  76. /*
  77. * Model for the selected subcontrol.
  78. */
  79. private SingleSelectionModel selectionModel;
  80. /* Lock object used in place of class object for synchronization.
  81. * (4187686)
  82. */
  83. private static final Object classLock = new Object();
  84. /* diagnostic aids -- should be false for production builds. */
  85. private static final boolean TRACE = false; // trace creates and disposes
  86. private static final boolean VERBOSE = false; // show reuse hits/misses
  87. private static final boolean DEBUG = false; // show bad params, misc.
  88. /**
  89. * Sets the default value for the <code>lightWeightPopupEnabled</code>
  90. * property.
  91. * Lightweight popup windows are more efficient than heavy weight windows,
  92. * but light weight and heavy weight components do not mix well in a GUI,
  93. * and in that situation a heavy weight may be required.
  94. *
  95. * @param aFlag true if the popup is to be light weight, otherwise false
  96. * @see #getDefaultLightWeightPopupEnabled
  97. */
  98. public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
  99. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  100. new Boolean(aFlag));
  101. }
  102. /**
  103. * Returns true if this is a light weight popup component, false
  104. * otherwise.
  105. *
  106. * @return the <code>lightWeightPopupEnabled</code> property
  107. * @see #setDefaultLightWeightPopupEnabled
  108. */
  109. public static boolean getDefaultLightWeightPopupEnabled() {
  110. Boolean b = (Boolean)
  111. SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
  112. if (b == null) {
  113. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  114. Boolean.TRUE);
  115. return true;
  116. }
  117. return b.booleanValue();
  118. }
  119. /**
  120. * Constructs a <code>JPopupMenu</code> without an "invoker".
  121. */
  122. public JPopupMenu() {
  123. this(null);
  124. }
  125. /**
  126. * Constructs a <code>JPopupMenu</code> with the specified title.
  127. *
  128. * @param label the string that a UI may use to display as a title
  129. * for the popup menu.
  130. */
  131. public JPopupMenu(String label) {
  132. this.label = label;
  133. // PENDING(ges)
  134. this.lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled();
  135. setSelectionModel(new DefaultSingleSelectionModel());
  136. addMouseListener(new MouseAdapter() {});
  137. updateUI();
  138. }
  139. /**
  140. * Returns the look and feel (L&F) object that renders this component.
  141. *
  142. * @return the <code>PopupMenuUI</code> object that renders this component
  143. */
  144. public PopupMenuUI getUI() {
  145. return (PopupMenuUI)ui;
  146. }
  147. /**
  148. * Sets the L&F object that renders this component.
  149. *
  150. * @param ui the new <code>PopupMenuUI</code> L&F object
  151. * @see UIDefaults#getUI
  152. * @beaninfo
  153. * description: The popup menu UI delegate
  154. * bound: true
  155. * expert: true
  156. * hidden: true
  157. */
  158. public void setUI(PopupMenuUI ui) {
  159. super.setUI(ui);
  160. }
  161. /**
  162. * Notification from the <code>UIFactory</code> that the L&F has changed.
  163. * Called to replace the UI with the latest version from the
  164. * <code>UIFactory</code>.
  165. *
  166. * @see JComponent#updateUI
  167. */
  168. public void updateUI() {
  169. setUI((PopupMenuUI)UIManager.getUI(this));
  170. }
  171. /**
  172. * Returns the name of the L&F class that renders this component.
  173. *
  174. * @return the string "PopupMenuUI"
  175. * @see JComponent#getUIClassID
  176. * @see UIDefaults#getUI
  177. */
  178. public String getUIClassID() {
  179. return uiClassID;
  180. }
  181. /**
  182. * Returns the model object that handles single selections.
  183. *
  184. * @return the <code>selectionModel</code> property
  185. * @see SingleSelectionModel
  186. */
  187. public SingleSelectionModel getSelectionModel() {
  188. return selectionModel;
  189. }
  190. /**
  191. * Sets the model object to handle single selections.
  192. *
  193. * @param model the new <code>SingleSelectionModel</code>
  194. * @see SingleSelectionModel
  195. * @beaninfo
  196. * description: The selection model for the popup menu
  197. * expert: true
  198. */
  199. public void setSelectionModel(SingleSelectionModel model) {
  200. selectionModel = model;
  201. }
  202. /**
  203. * Appends the specified menu item to the end of this menu.
  204. *
  205. * @param c the <code>JMenuItem</code> to add
  206. * @return the <code>JMenuItem</code> added
  207. */
  208. public JMenuItem add(JMenuItem menuItem) {
  209. super.add(menuItem);
  210. return menuItem;
  211. }
  212. /**
  213. * Creates a new menu item with the specified text and appends
  214. * it to the end of this menu.
  215. *
  216. * @param s the string for the menu item to be added
  217. */
  218. public JMenuItem add(String s) {
  219. return add(new JMenuItem(s));
  220. }
  221. /**
  222. * Appends a new menu item to the end of the menu which
  223. * dispatches the specified <code>Action</code> object.
  224. *
  225. * As of JDK 1.3, this is no longer the preferred method for adding
  226. * <code>Actions</code> to
  227. * a container. Instead it is recommended to configure a control with
  228. * an action using <code>setAction</code>, and then add that control
  229. * directly to the <code>Container</code>.
  230. *
  231. * @param a the <code>Action</code> to add to the menu
  232. * @return the new menu item
  233. * @see Action
  234. */
  235. public JMenuItem add(Action a) {
  236. JMenuItem mi = createActionComponent(a);
  237. mi.setAction(a);
  238. add(mi);
  239. return mi;
  240. }
  241. /**
  242. * Factory method which creates the <code>JMenuItem</code> for
  243. * <code>Actions</code> added to the <code>JPopupMenu</code>.
  244. * As of JDK 1.3, this is no
  245. * longer the preferred method, instead it is recommended to configure
  246. * a control with an action using <code>setAction</code>,
  247. * and then adding that
  248. * control directly to the <code>Container</code>.
  249. *
  250. * @param a the <code>Action</code> for the menu item to be added
  251. * @return the new menu item
  252. * @see Action
  253. */
  254. protected JMenuItem createActionComponent(Action a) {
  255. JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  256. (Icon)a.getValue(Action.SMALL_ICON)){
  257. protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
  258. PropertyChangeListener pcl = createActionChangeListener(this);
  259. if (pcl == null) {
  260. pcl = super.createActionPropertyChangeListener(a);
  261. }
  262. return pcl;
  263. }
  264. };
  265. mi.setHorizontalTextPosition(JButton.RIGHT);
  266. mi.setVerticalTextPosition(JButton.CENTER);
  267. mi.setEnabled(a.isEnabled());
  268. return mi;
  269. }
  270. /**
  271. * Returns a properly configured <code>PropertyChangeListener</code>
  272. * which updates the control as changes to the <code>Action</code> occur.
  273. * As of JDK 1.3, this is no longer the preferred method for adding
  274. * <code>Actions</code> to
  275. * a container. Instead it is recommended to configure a control with
  276. * an action using <code>setAction</code>, and then add that control
  277. * directly to the <code>Container</code>.
  278. */
  279. protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
  280. return new ActionChangedListener(b);
  281. }
  282. private class ActionChangedListener implements PropertyChangeListener {
  283. JMenuItem menuItem;
  284. ActionChangedListener(JMenuItem mi) {
  285. super();
  286. setTarget(mi);
  287. }
  288. public void propertyChange(PropertyChangeEvent e) {
  289. String propertyName = e.getPropertyName();
  290. if (e.getPropertyName().equals(Action.NAME)) {
  291. String text = (String) e.getNewValue();
  292. menuItem.setText(text);
  293. } else if (propertyName.equals("enabled")) {
  294. Boolean enabledState = (Boolean) e.getNewValue();
  295. menuItem.setEnabled(enabledState.booleanValue());
  296. } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  297. Icon icon = (Icon) e.getNewValue();
  298. menuItem.setIcon(icon);
  299. menuItem.invalidate();
  300. menuItem.repaint();
  301. }
  302. }
  303. public void setTarget(JMenuItem b) {
  304. this.menuItem = b;
  305. }
  306. }
  307. /**
  308. * Removes the component at the specified index from this popup menu.
  309. *
  310. * @param pos the position of the item to be removed
  311. * @exception IllegalArgumentException if the value of
  312. * <code>pos</code> < 0, or if the value of
  313. * <code>pos</code> is greater than the
  314. * number of items
  315. */
  316. public void remove(int pos) {
  317. if (pos < 0) {
  318. throw new IllegalArgumentException("index less than zero.");
  319. }
  320. if (pos > getComponentCount() -1) {
  321. throw new IllegalArgumentException("index greater than the number of items.");
  322. }
  323. super.remove(pos);
  324. }
  325. /**
  326. * When displaying the popup, <code>JPopupMenu</code> chooses to
  327. * use a light weight popup if it fits.
  328. * This method allows you to disable this feature.
  329. * You have to do disable
  330. * it if your application mixes light weight and heavy weights components.
  331. *
  332. * @param aFlag true if the popup is to be light weight, otherwise false
  333. * @beaninfo
  334. * description: Determines whether lightweight popups are used when possible
  335. * expert: true
  336. */
  337. public void setLightWeightPopupEnabled(boolean aFlag) {
  338. // PENDING(ges)
  339. lightWeightPopupEnabled = aFlag;
  340. // popupFactory.setLightWeightPopupEnabled(aFlag);
  341. }
  342. /**
  343. * Returns true if light weight (all-Java) popups are in use,
  344. * or false if heavy weight (native peer) popups are being used.
  345. *
  346. * @return true if light weight popups are in use, false otherwise
  347. */
  348. public boolean isLightWeightPopupEnabled() {
  349. // PENDING(ges)
  350. return lightWeightPopupEnabled;
  351. // return popupFactory.isLightWeightPopupEnabled();
  352. }
  353. /**
  354. * Returns the popup menu's label
  355. *
  356. * @return a string containing the popup menu's label
  357. * @see #setLabel
  358. */
  359. public String getLabel() {
  360. return label;
  361. }
  362. /**
  363. * Sets the popup menu's label. Different look and feels may choose
  364. * to display or not display this.
  365. *
  366. * @param label a string specifying the label for the popup menu
  367. *
  368. * @see #setLabel
  369. * @beaninfo
  370. * description: The label for the popup menu.
  371. * bound: true
  372. */
  373. public void setLabel(String label) {
  374. String oldValue = this.label;
  375. this.label = label;
  376. firePropertyChange("label", oldValue, label);
  377. if (accessibleContext != null) {
  378. accessibleContext.firePropertyChange(
  379. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  380. oldValue, label);
  381. }
  382. invalidate();
  383. repaint();
  384. }
  385. /**
  386. * Appends a new separator at the end of the menu.
  387. */
  388. public void addSeparator() {
  389. add( new JPopupMenu.Separator() );
  390. }
  391. /**
  392. * Inserts a menu item for the specified <code>Action</code> object at
  393. * a given position.
  394. *
  395. * @param a the <code>Action</code> object to insert
  396. * @param index specifies the position at which to insert the
  397. * <code>Action</code>, where 0 is the first
  398. * @see Action
  399. */
  400. public void insert(Action a, int index) {
  401. JMenuItem mi = createActionComponent(a);
  402. mi.setAction(a);
  403. add(mi, index);
  404. }
  405. /**
  406. * Inserts the specified component into the menu at a given
  407. * position.
  408. *
  409. * @param component the <code>Component</code> to insert
  410. * @param index specifies the position at which
  411. * to insert the component, where 0 is the first
  412. * @exception IllegalArgumentException if <code>index</code> < 0
  413. */
  414. public void insert(Component component, int index) {
  415. if (index < 0) {
  416. throw new IllegalArgumentException("index less than zero.");
  417. }
  418. int nitems = getComponentCount();
  419. // PENDING(ges): Why not use an array?
  420. Vector tempItems = new Vector();
  421. /* Remove the item at index, nitems-index times
  422. storing them in a temporary vector in the
  423. order they appear on the menu.
  424. */
  425. for (int i = index ; i < nitems; i++) {
  426. tempItems.addElement(getComponent(index));
  427. remove(index);
  428. }
  429. add(component);
  430. /* Add the removed items back to the menu, they are
  431. already in the correct order in the temp vector.
  432. */
  433. for (int i = 0; i < tempItems.size() ; i++) {
  434. add((Component)tempItems.elementAt(i));
  435. }
  436. }
  437. /**
  438. * Adds a <code>PopupMenu</code> listener.
  439. *
  440. * @param l the <code>PopupMenuListener</code> to add
  441. */
  442. public void addPopupMenuListener(PopupMenuListener l) {
  443. listenerList.add(PopupMenuListener.class,l);
  444. }
  445. /**
  446. * Removes a <code>PopupMenu</code> listener.
  447. *
  448. * @param l the <code>PopupMenuListener</code> to remove
  449. */
  450. public void removePopupMenuListener(PopupMenuListener l) {
  451. listenerList.remove(PopupMenuListener.class,l);
  452. }
  453. /**
  454. * Notifies <code>PopupMenuListener</code>s that this popup menu will
  455. * become visible.
  456. */
  457. protected void firePopupMenuWillBecomeVisible() {
  458. Object[] listeners = listenerList.getListenerList();
  459. PopupMenuEvent e=null;
  460. for (int i = listeners.length-2; i>=0; i-=2) {
  461. if (listeners[i]==PopupMenuListener.class) {
  462. if (e == null)
  463. e = new PopupMenuEvent(this);
  464. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
  465. }
  466. }
  467. }
  468. /**
  469. * Notifies <code>PopupMenuListener</code>s that this popup menu will
  470. * become invisible.
  471. */
  472. protected void firePopupMenuWillBecomeInvisible() {
  473. Object[] listeners = listenerList.getListenerList();
  474. PopupMenuEvent e=null;
  475. for (int i = listeners.length-2; i>=0; i-=2) {
  476. if (listeners[i]==PopupMenuListener.class) {
  477. if (e == null)
  478. e = new PopupMenuEvent(this);
  479. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
  480. }
  481. }
  482. }
  483. /**
  484. * Notifies <code>PopupMenuListeners</code> that this popup menu is
  485. * cancelled.
  486. */
  487. protected void firePopupMenuCanceled() {
  488. Object[] listeners = listenerList.getListenerList();
  489. PopupMenuEvent e=null;
  490. for (int i = listeners.length-2; i>=0; i-=2) {
  491. if (listeners[i]==PopupMenuListener.class) {
  492. if (e == null)
  493. e = new PopupMenuEvent(this);
  494. ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
  495. }
  496. }
  497. }
  498. /**
  499. * Always returns true since popups, by definition, should always
  500. * be on top of all other windows.
  501. * @return true
  502. */
  503. // package private
  504. boolean alwaysOnTop() {
  505. return true;
  506. }
  507. /**
  508. * Lays out the container so that it uses the minimum space
  509. * needed to display its contents.
  510. */
  511. public void pack() {
  512. if(popup != null)
  513. popup.pack();
  514. }
  515. /**
  516. * Sets the visibility of the popup menu.
  517. *
  518. * @param b true to make the popup visible, or false to
  519. * hide it
  520. * @beaninfo
  521. * description: Makes the popup visible
  522. */
  523. public void setVisible(boolean b) {
  524. if (DEBUG) {
  525. System.out.println("JPopupMenu.setVisible " + b);
  526. }
  527. // Is it a no-op?
  528. if (b == isVisible())
  529. return;
  530. // if closing, first close all Submenus
  531. if (b == false) {
  532. getSelectionModel().clearSelection();
  533. } else {
  534. // This is a popup menu with MenuElement children,
  535. // set selection path before popping up!
  536. if (isPopupMenu()) {
  537. if (getSubElements().length > 0) {
  538. MenuElement me[] = new MenuElement[2];
  539. me[0]=(MenuElement)this;
  540. me[1]=getSubElements()[0];
  541. MenuSelectionManager.defaultManager().setSelectedPath(me);
  542. } else {
  543. MenuElement me[] = new MenuElement[1];
  544. me[0]=(MenuElement)this;
  545. MenuSelectionManager.defaultManager().setSelectedPath(me);
  546. }
  547. }
  548. }
  549. if(b) {
  550. firePopupMenuWillBecomeVisible();
  551. popup = popupFactory.getPopup(this,
  552. invoker,
  553. desiredLocationX,
  554. desiredLocationY);
  555. popup.show(invoker);
  556. } else if(popup != null) {
  557. firePopupMenuWillBecomeInvisible();
  558. popup.hide();
  559. popup.removeComponent(this);
  560. popup = null;
  561. }
  562. if (accessibleContext != null) {
  563. if (b) {
  564. accessibleContext.firePropertyChange(
  565. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  566. null, AccessibleState.VISIBLE);
  567. } else {
  568. accessibleContext.firePropertyChange(
  569. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  570. AccessibleState.VISIBLE, null);
  571. }
  572. }
  573. }
  574. /**
  575. * Returns true if the popup menu is visible (currently
  576. * being displayed).
  577. */
  578. public boolean isVisible() {
  579. if(popup != null)
  580. return popup.isShowing();
  581. else
  582. return false;
  583. }
  584. /**
  585. * Sets the location of the upper left corner of the
  586. * popup menu using x, y coordinates.
  587. *
  588. * @param x the x coordinate of the popup's new position
  589. * @param y the y coordinate of the popup's new position
  590. * @beaninfo
  591. * description: The location of the popup menu.
  592. */
  593. public void setLocation(int x, int y) {
  594. if(popup != null)
  595. popup.setLocationOnScreen(x, y);
  596. else {
  597. desiredLocationX = x;
  598. desiredLocationY = y;
  599. }
  600. }
  601. /**
  602. * Returns true if the popup menu is a standalone popup menu
  603. * rather than the submenu of a <code>JMenu</code>.
  604. *
  605. * @return true if this menu is a standalone popup menu, otherwise false
  606. */
  607. private boolean isPopupMenu() {
  608. return ((invoker != null) && !(invoker instanceof JMenu));
  609. }
  610. /**
  611. * Returns the component which is the 'invoker' of this
  612. * popup menu.
  613. *
  614. * @return the <code>Component</code> in which the popup menu is displayed
  615. */
  616. public Component getInvoker() {
  617. return this.invoker;
  618. }
  619. /**
  620. * Sets the invoker of this popup menu -- the component in which
  621. * the popup menu menu is to be displayed.
  622. *
  623. * @param invoker the <code>Component</code> in which the popup
  624. * menu is displayed
  625. * @beaninfo
  626. * description: The invoking component for the popup menu
  627. * expert: true
  628. */
  629. public void setInvoker(Component invoker) {
  630. Component oldInvoker = this.invoker;
  631. this.invoker = invoker;
  632. if ((oldInvoker != this.invoker) && (ui != null)) {
  633. ui.uninstallUI(this);
  634. ui.installUI(this);
  635. }
  636. invalidate();
  637. }
  638. /**
  639. * Displays the popup menu at the position x,y in the coordinate
  640. * space of the component invoker.
  641. *
  642. * @param invoker the component in whose space the popup menu is to appear
  643. * @param x the x coordinate in invoker's coordinate space at which
  644. * the popup menu is to be displayed
  645. * @param y the y coordinate in invoker's coordinate space at which
  646. * the popup menu is to be displayed
  647. */
  648. public void show(Component invoker, int x, int y) {
  649. if (DEBUG) {
  650. System.out.println("in JPopupMenu.show " );
  651. }
  652. setInvoker(invoker);
  653. Frame newFrame = getFrame(invoker);
  654. if (newFrame != frame) {
  655. // Use the invoker's frame so that events
  656. // are propogated properly
  657. if (newFrame!=null) {
  658. this.frame = newFrame;
  659. if(popup != null) {
  660. setVisible(false);
  661. }
  662. }
  663. }
  664. Point invokerOrigin;
  665. if (invoker != null) {
  666. invokerOrigin = invoker.getLocationOnScreen();
  667. setLocation(invokerOrigin.x + x,
  668. invokerOrigin.y + y);
  669. } else {
  670. setLocation(x, y);
  671. }
  672. setVisible(true);
  673. }
  674. /**
  675. * Returns the popup menu which is at the root of the menu system
  676. * for this popup menu.
  677. *
  678. * @return the topmost grandparent <code>JPopupMenu</code>
  679. */
  680. JPopupMenu getRootPopupMenu() {
  681. JPopupMenu mp = this;
  682. while((mp!=null) && (mp.isPopupMenu()!=true) &&
  683. (mp.getInvoker() != null) &&
  684. (mp.getInvoker().getParent() != null) &&
  685. (mp.getInvoker().getParent() instanceof JPopupMenu)
  686. ) {
  687. mp = (JPopupMenu) mp.getInvoker().getParent();
  688. }
  689. return mp;
  690. }
  691. /**
  692. * Returns the component at the specified index.
  693. *
  694. * @param i the index of the component, where 0 is the first
  695. * @return the <code>Component</code> at that index
  696. * @deprecated replaced by <code>getComponent(int i)</code>
  697. */
  698. public Component getComponentAtIndex(int i) {
  699. return getComponent(i);
  700. }
  701. /**
  702. * Returns the index of the specified component.
  703. *
  704. * @param the <code>Component</code> to find
  705. * @return the index of the component, where 0 is the first;
  706. * or -1 if the component is not found
  707. */
  708. public int getComponentIndex(Component c) {
  709. int ncomponents = this.getComponentCount();
  710. Component[] component = this.getComponents();
  711. for (int i = 0 ; i < ncomponents ; i++) {
  712. Component comp = component[i];
  713. if (comp == c)
  714. return i;
  715. }
  716. return -1;
  717. }
  718. /**
  719. * Sets the size of the Popup window using a <code>Dimension</code> object.
  720. *
  721. * @param d the <code>Dimension</code> specifying the new size
  722. * of this component.
  723. * @beaninfo
  724. * description: The size of the popup menu
  725. */
  726. public void setPopupSize(Dimension d) {
  727. if(popup != null)
  728. popup.setSize(d.width,d.height);
  729. }
  730. /**
  731. * Sets the size of the Popup window to the specified width and
  732. * height.
  733. *
  734. * @param <code>width</code> the new width of the Popup in pixels
  735. * @param <code>height</code> the new height of the Popup in pixels
  736. * @beaninfo
  737. * description: The size of the popup menu
  738. */
  739. public void setPopupSize(int width, int height) {
  740. if(popup != null)
  741. popup.setSize(width, height);
  742. }
  743. /**
  744. * Sets the currently selected component, This will result
  745. * in a change to the selection model.
  746. *
  747. * @param sel the <code>Component</code> to select
  748. * @beaninfo
  749. * description: The selected component on the popup menu
  750. * expert: true
  751. * hidden: true
  752. */
  753. public void setSelected(Component sel) {
  754. SingleSelectionModel model = getSelectionModel();
  755. int index = getComponentIndex(sel);
  756. model.setSelectedIndex(index);
  757. }
  758. /**
  759. * Checks whether the border should be painted.
  760. *
  761. * @return true if the border is painted, false otherwise
  762. * @see #setBorderPainted
  763. */
  764. public boolean isBorderPainted() {
  765. return paintBorder;
  766. }
  767. /**
  768. * Sets whether the border should be painted.
  769. *
  770. * @param b if true, the border is painted.
  771. * @see #isBorderPainted
  772. * @beaninfo
  773. * description: Is the border of the popup menu painted
  774. */
  775. public void setBorderPainted(boolean b) {
  776. paintBorder = b;
  777. repaint();
  778. }
  779. /**
  780. * Paints the popup menu's border if <code>BorderPainted</code>
  781. * property is true.
  782. * @param g the <code>Graphics</code> object
  783. *
  784. * @see JComponent#paint
  785. * @see JComponent#setBorder
  786. */
  787. protected void paintBorder(Graphics g) {
  788. if (isBorderPainted()) {
  789. super.paintBorder(g);
  790. }
  791. }
  792. /**
  793. * Returns the margin, in pixels, between the popup menu's border and
  794. * its containees.
  795. *
  796. * @return an <code>Insets</code> object containing the margin values.
  797. */
  798. public Insets getMargin() {
  799. if(margin == null) {
  800. return new Insets(0,0,0,0);
  801. } else {
  802. return margin;
  803. }
  804. }
  805. /**
  806. * Examines the list of menu items to determine whether
  807. * <code>popup</code> is a popup menu.
  808. *
  809. * @param popup a <code>JPopupMenu</code>
  810. * @return true if <code>popup</code>
  811. */
  812. boolean isSubPopupMenu(JPopupMenu popup) {
  813. int ncomponents = this.getComponentCount();
  814. Component[] component = this.getComponents();
  815. for (int i = 0 ; i < ncomponents ; i++) {
  816. Component comp = component[i];
  817. if (comp instanceof JMenu) {
  818. JMenu menu = (JMenu)comp;
  819. JPopupMenu subPopup = menu.getPopupMenu();
  820. if (subPopup == popup)
  821. return true;
  822. if (subPopup.isSubPopupMenu(popup))
  823. return true;
  824. }
  825. }
  826. return false;
  827. }
  828. private static Frame getFrame(Component c) {
  829. Component w = c;
  830. while(!(w instanceof Frame) && (w!=null)) {
  831. w = w.getParent();
  832. }
  833. return (Frame)w;
  834. }
  835. /**
  836. * Returns a string representation of this <code>JPopupMenu</code>.
  837. * This method
  838. * is intended to be used only for debugging purposes, and the
  839. * content and format of the returned string may vary between
  840. * implementations. The returned string may be empty but may not
  841. * be <code>null</code>.
  842. *
  843. * @return a string representation of this <code>JPopupMenu</code>.
  844. */
  845. protected String paramString() {
  846. String labelString = (label != null ?
  847. label : "");
  848. String paintBorderString = (paintBorder ?
  849. "true" : "false");
  850. String marginString = (margin != null ?
  851. margin.toString() : "");
  852. String lastPopupTypeString;
  853. /* PENDING(ges)
  854. if (lastPopupType == LIGHT_WEIGHT_POPUP) {
  855. lastPopupTypeString = "LIGHT_WEIGHT_POPUP";
  856. } else if (lastPopupType == MEDIUM_WEIGHT_POPUP) {
  857. lastPopupTypeString = "MEDIUM_WEIGHT_POPUP";
  858. } else if (lastPopupType == HEAVY_WEIGHT_POPUP) {
  859. lastPopupTypeString = "HEAVY_WEIGHT_POPUP";
  860. } else
  861. */
  862. lastPopupTypeString = "";
  863. String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
  864. "true" : "false");
  865. return super.paramString() +
  866. ",desiredLocationX=" + desiredLocationX +
  867. ",desiredLocationY=" + desiredLocationY +
  868. ",label=" + labelString +
  869. ",lastPopupType=" + lastPopupTypeString +
  870. ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
  871. ",margin=" + marginString +
  872. ",paintBorder=" + paintBorderString;
  873. }
  874. /////////////////
  875. // Accessibility support
  876. ////////////////
  877. /**
  878. * Gets the AccessibleContext associated with this JPopupMenu.
  879. * For JPopupMenus, the AccessibleContext takes the form of an
  880. * AccessibleJPopupMenu.
  881. * A new AccessibleJPopupMenu instance is created if necessary.
  882. *
  883. * @return an AccessibleJPopupMenu that serves as the
  884. * AccessibleContext of this JPopupMenu
  885. */
  886. public AccessibleContext getAccessibleContext() {
  887. if (accessibleContext == null) {
  888. accessibleContext = new AccessibleJPopupMenu();
  889. }
  890. return accessibleContext;
  891. }
  892. /**
  893. * This class implements accessibility support for the
  894. * <code>JPopupMenu</code> class. It provides an implementation of the
  895. * Java Accessibility API appropriate to popup menu user-interface
  896. * elements.
  897. */
  898. protected class AccessibleJPopupMenu extends AccessibleJComponent {
  899. /**
  900. * Get the role of this object.
  901. *
  902. * @return an instance of AccessibleRole describing the role of
  903. * the object
  904. */
  905. public AccessibleRole getAccessibleRole() {
  906. return AccessibleRole.POPUP_MENU;
  907. }
  908. } // inner class AccessibleJPopupMenu
  909. ////////////
  910. // Serialization support.
  911. ////////////
  912. private void writeObject(ObjectOutputStream s) throws IOException {
  913. Vector values = new Vector();
  914. s.defaultWriteObject();
  915. // Save the invoker, if its Serializable.
  916. if(invoker != null && invoker instanceof Serializable) {
  917. values.addElement("invoker");
  918. values.addElement(invoker);
  919. }
  920. // Save the popup, if its Serializable.
  921. if(popup != null && popup instanceof Serializable) {
  922. values.addElement("popup");
  923. values.addElement(popup);
  924. }
  925. // Save the frame, if its Serializable.
  926. if(frame != null && frame instanceof Serializable) {
  927. values.addElement("frame");
  928. values.addElement(frame);
  929. }
  930. s.writeObject(values);
  931. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  932. ui.installUI(this);
  933. }
  934. }
  935. // implements javax.swing.MenuElement
  936. private void readObject(ObjectInputStream s)
  937. throws IOException, ClassNotFoundException {
  938. s.defaultReadObject();
  939. Vector values = (Vector)s.readObject();
  940. int indexCounter = 0;
  941. int maxCounter = values.size();
  942. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  943. equals("invoker")) {
  944. invoker = (Component)values.elementAt(++indexCounter);
  945. indexCounter++;
  946. }
  947. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  948. equals("popup")) {
  949. popup = (Popup)values.elementAt(++indexCounter);
  950. indexCounter++;
  951. }
  952. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  953. equals("frame")) {
  954. frame = (Frame)values.elementAt(++indexCounter);
  955. indexCounter++;
  956. }
  957. }
  958. /**
  959. * This method is required to conform to the
  960. * <code>MenuElement</code> interface, but it not implemented.
  961. * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
  962. */
  963. public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
  964. /**
  965. * This method is required to conform to the
  966. * <code>MenuElement</code> interface, but it not implemented.
  967. * @see MenuElement#processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager)
  968. */
  969. public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
  970. }
  971. /**
  972. * Messaged when the menubar selection changes to activate or
  973. * deactivate this menu. This implements the
  974. * <code>javax.swing.MenuElement</code> interface.
  975. * Overrides <code>MenuElement.menuSelectionChanged</code>.
  976. *
  977. * @param isIncluded true if this menu is active, false if
  978. * it is not
  979. * @see MenuElement#menuSelectionChanged(boolean)
  980. */
  981. public void menuSelectionChanged(boolean isIncluded) {
  982. if (DEBUG) {
  983. System.out.println("In JPopupMenu.menuSelectionChanged " + isIncluded);
  984. }
  985. if(invoker instanceof JMenu) {
  986. JMenu m = (JMenu) invoker;
  987. if(isIncluded)
  988. m.setPopupMenuVisible(true);
  989. else
  990. m.setPopupMenuVisible(false);
  991. }
  992. if (isPopupMenu() && !isIncluded)
  993. setVisible(false);
  994. }
  995. /**
  996. * Returns an array of <code>MenuElement</code>s containing the submenu
  997. * for this menu component. It will only return items conforming to
  998. * the <code>JMenuElement</code> interface.
  999. * If popup menu is <code>null</code> returns
  1000. * an empty array. This method is required to conform to the
  1001. * <code>MenuElement</code> interface.
  1002. *
  1003. * @return an array of <code>MenuElement</code> objects
  1004. * @see MenuElement#getSubElements
  1005. */
  1006. public MenuElement[] getSubElements() {
  1007. MenuElement result[];
  1008. Vector tmp = new Vector();
  1009. int c = getComponentCount();
  1010. int i;
  1011. Component m;
  1012. for(i=0 ; i < c ; i++) {
  1013. m = getComponent(i);
  1014. if(m instanceof MenuElement)
  1015. tmp.addElement(m);
  1016. }
  1017. result = new MenuElement[tmp.size()];
  1018. for(i=0,c=tmp.size() ; i < c ; i++)
  1019. result[i] = (MenuElement) tmp.elementAt(i);
  1020. return result;
  1021. }
  1022. /**
  1023. * Returns this <code>JPopupMenu</code> component.
  1024. * @return this <code>JPopupMenu</code> object
  1025. * @see MenuElement#getComponent
  1026. */
  1027. public Component getComponent() {
  1028. return this;
  1029. }
  1030. /**
  1031. * A popup menu-specific separator.
  1032. */
  1033. static public class Separator extends JSeparator
  1034. {
  1035. public Separator( )
  1036. {
  1037. super( JSeparator.HORIZONTAL );
  1038. }
  1039. /**
  1040. * Returns the name of the L&F class that renders this component.
  1041. *
  1042. * @return the string "PopupMenuSeparatorUI"
  1043. * @see JComponent#getUIClassID
  1044. * @see UIDefaults#getUI
  1045. */
  1046. public String getUIClassID()
  1047. {
  1048. return "PopupMenuSeparatorUI";
  1049. }
  1050. }
  1051. /**
  1052. * Returns true if the <code>MouseEvent</code> is considered a popup trigger
  1053. * by the <code>JPopupMenu</code>'s currently installed UI.
  1054. *
  1055. * @return true if the mouse event is a popup trigger
  1056. * @since 1.3
  1057. */
  1058. public boolean isPopupTrigger(MouseEvent e) {
  1059. return getUI().isPopupTrigger(e);
  1060. }
  1061. static void setPopupFactory(PopupFactory pf) {
  1062. popupFactory = pf;
  1063. }
  1064. }