1. /*
  2. * @(#)JPopupMenu.java 1.132 01/11/29
  3. *
  4. * Copyright 2002 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.event.*;
  22. import java.applet.Applet;
  23. /**
  24. * An implementation of a Popup Menu -- a small window which pops up
  25. * and displays a series of choices. A JPopupMenu is used for the
  26. * menu that appears when the user selects an item on the menu bar.
  27. * It is also used for "pull-right" menu that appears when the
  28. * selects a menu item that activates it. Finally, a JPopupMenu
  29. * can also be used anywhere else you want a menu to appear -- for
  30. * example, when the user right-clicks in a specified area.
  31. * <p>
  32. * For the keyboard keys used by this component in the standard Look and
  33. * Feel (L&F) renditions, see the
  34. * <a href="doc-files/Key-Index.html#JPopupMenu">JPopupMenu</a> key assignments.
  35. * <p>
  36. * <strong>Warning:</strong>
  37. * Serialized objects of this class will not be compatible with
  38. * future Swing releases. The current serialization support is appropriate
  39. * for short term storage or RMI between applications running the same
  40. * version of Swing. A future release of Swing will provide support for
  41. * long term persistence.
  42. *
  43. * @version 1.132 11/29/01
  44. * @author Georges Saab
  45. * @author David Karlton
  46. * @author Arnaud Weber
  47. */
  48. public class JPopupMenu extends JComponent implements Accessible,MenuElement {
  49. /**
  50. * @see #getUIClassID
  51. * @see #readObject
  52. */
  53. private static final String uiClassID = "PopupMenuUI";
  54. transient Component invoker;
  55. transient Popup popup;
  56. transient Frame frame;
  57. private String label = null;
  58. private boolean paintBorder = true;
  59. private Insets margin = null;
  60. private int desiredLocationX,desiredLocationY;
  61. private int lastPopupType = LIGHT_WEIGHT_POPUP;
  62. private static final Object heavyPopupCacheKey =
  63. new StringBuffer("JPopupMenu.heavyPopupCache");
  64. private static final Object lightPopupCacheKey =
  65. new StringBuffer("JPopupMenu.lightPopupCache");
  66. private static final Object mediumPopupCacheKey =
  67. new StringBuffer("JPopupMenu.mediumPopupCache");
  68. private static final Object defaultLWPopupEnabledKey =
  69. new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey");
  70. private static final int MAX_CACHE_SIZE = 5;
  71. private boolean lightWeightPopupEnabled = true;
  72. /** A light weight popup is used when it fits and light weight popups are enabled **/
  73. private static final int LIGHT_WEIGHT_POPUP = 0;
  74. /** A "Medium weight" popup is a panel. We use this when downgrading an heavy weight in
  75. * dialogs
  76. */
  77. private static final int MEDIUM_WEIGHT_POPUP = 1;
  78. /** A popup implemented with a window */
  79. private static final int HEAVY_WEIGHT_POPUP = 2;
  80. /*
  81. * Model for the selected subcontrol
  82. */
  83. private SingleSelectionModel selectionModel;
  84. /* Registry of listeners created for Action-JMenuItem
  85. * linkage. This is needed so that references can
  86. * be cleaned up at remove time to allow GC.
  87. */
  88. private static Hashtable listenerRegistry = null;
  89. /* Lock object used in place of class object for synchronization.
  90. * (4187686)
  91. */
  92. private static final Object classLock = new Object();
  93. /**
  94. * Set the default value for the <b>lightWeightPopupEnabled</b>
  95. * property.
  96. */
  97. /* Pending(arnaud) this property should scope to awt-context */
  98. public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
  99. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  100. new Boolean(aFlag));
  101. }
  102. /**
  103. * Return the default value for the <b>lightWeightPopupEnabled</b>
  104. * property.
  105. */
  106. public static boolean getDefaultLightWeightPopupEnabled() {
  107. Boolean b = (Boolean)
  108. SwingUtilities.appContextGet(defaultLWPopupEnabledKey);
  109. if (b == null) {
  110. SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
  111. Boolean.TRUE);
  112. return true;
  113. }
  114. return b.booleanValue();
  115. }
  116. private static Hashtable getHeavyPopupCache() {
  117. Hashtable cache =
  118. (Hashtable)SwingUtilities.appContextGet(heavyPopupCacheKey);
  119. if (cache == null) {
  120. cache = new Hashtable(2);
  121. SwingUtilities.appContextPut(heavyPopupCacheKey, cache);
  122. }
  123. return cache;
  124. }
  125. private static Vector getLightPopupCache() {
  126. Vector cache =
  127. (Vector)SwingUtilities.appContextGet(lightPopupCacheKey);
  128. if (cache == null) {
  129. cache = new Vector();
  130. SwingUtilities.appContextPut(lightPopupCacheKey, cache);
  131. }
  132. return cache;
  133. }
  134. private static Vector getMediumPopupCache() {
  135. Vector cache =
  136. (Vector)SwingUtilities.appContextGet(mediumPopupCacheKey);
  137. if (cache == null) {
  138. cache = new Vector();
  139. SwingUtilities.appContextPut(mediumPopupCacheKey, cache);
  140. }
  141. return cache;
  142. }
  143. static void recycleHeavyPopup(Popup aPopup) {
  144. synchronized (classLock) {
  145. Vector cache;
  146. final Frame f = getFrame((Component)aPopup);
  147. Hashtable heavyPopupCache = getHeavyPopupCache();
  148. if (heavyPopupCache.containsKey(f)) {
  149. cache = (Vector)heavyPopupCache.get(f);
  150. } else {
  151. cache = new Vector();
  152. heavyPopupCache.put(f, cache);
  153. // Clean up if the Frame is closed
  154. f.addWindowListener(new WindowAdapter() {
  155. public void windowClosed(WindowEvent e) {
  156. Hashtable heavyPopupCache2 = getHeavyPopupCache();
  157. heavyPopupCache2.remove(f);
  158. }
  159. });
  160. }
  161. if(cache.size() < MAX_CACHE_SIZE) {
  162. cache.addElement(aPopup);
  163. }
  164. }
  165. }
  166. static Popup getRecycledHeavyPopup(Frame f) {
  167. synchronized (classLock) {
  168. Vector cache;
  169. Hashtable heavyPopupCache = getHeavyPopupCache();
  170. if (heavyPopupCache.containsKey(f)) {
  171. cache = (Vector)heavyPopupCache.get(f);
  172. } else {
  173. return null;
  174. }
  175. int c;
  176. if((c=cache.size()) > 0) {
  177. Popup r = (Popup)cache.elementAt(0);
  178. cache.removeElementAt(0);
  179. return r;
  180. }
  181. return null;
  182. }
  183. }
  184. static void recycleLightPopup(Popup aPopup) {
  185. synchronized (classLock) {
  186. Vector lightPopupCache = getLightPopupCache();
  187. if (lightPopupCache.size() < MAX_CACHE_SIZE) {
  188. lightPopupCache.addElement(aPopup);
  189. }
  190. }
  191. }
  192. static Popup getRecycledLightPopup() {
  193. synchronized (classLock) {
  194. Vector lightPopupCache = getLightPopupCache();
  195. int c;
  196. if((c=lightPopupCache.size()) > 0) {
  197. Popup r = (Popup)lightPopupCache.elementAt(0);
  198. lightPopupCache.removeElementAt(0);
  199. return r;
  200. }
  201. return null;
  202. }
  203. }
  204. static void recycleMediumPopup(Popup aPopup) {
  205. synchronized (classLock) {
  206. Vector mediumPopupCache = getMediumPopupCache();
  207. if(mediumPopupCache.size() < MAX_CACHE_SIZE) {
  208. mediumPopupCache.addElement(aPopup);
  209. }
  210. }
  211. }
  212. static Popup getRecycledMediumPopup() {
  213. synchronized (classLock) {
  214. Vector mediumPopupCache = getMediumPopupCache();
  215. int c;
  216. if((c=mediumPopupCache.size()) > 0) {
  217. Popup r = (Popup)mediumPopupCache.elementAt(0);
  218. mediumPopupCache.removeElementAt(0);
  219. return r;
  220. }
  221. return null;
  222. }
  223. }
  224. static void recyclePopup(Popup aPopup) {
  225. if(aPopup instanceof JPanelPopup)
  226. recycleLightPopup(aPopup);
  227. else if(aPopup instanceof WindowPopup)
  228. recycleHeavyPopup(aPopup);
  229. else if(aPopup instanceof PanelPopup)
  230. recycleMediumPopup(aPopup);
  231. }
  232. /**
  233. * Create a JPopupMenu without an "invoker".
  234. */
  235. public JPopupMenu() {
  236. this(null);
  237. }
  238. /**
  239. * Create a JPopupMenu with the specified title.
  240. *
  241. * @param label The string that a UI may use to display as a title
  242. * for the popup menu.
  243. */
  244. public JPopupMenu(String label) {
  245. this.label = label;
  246. this.lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled();
  247. setSelectionModel(new DefaultSingleSelectionModel());
  248. addMouseListener(new MouseAdapter() {});
  249. updateUI();
  250. }
  251. /**
  252. * Returns the L&F object that renders this component.
  253. *
  254. * @return the PopupMenuUI object that renders this component
  255. */
  256. public PopupMenuUI getUI() {
  257. return (PopupMenuUI)ui;
  258. }
  259. /**
  260. * Sets the L&F object that renders this component.
  261. *
  262. * @param ui the new PopupMenuUI L&F object
  263. * @see UIDefaults#getUI
  264. * @beaninfo
  265. * description: The popup menu UI delegate
  266. * bound: true
  267. * expert: true
  268. * hidden: true
  269. */
  270. public void setUI(PopupMenuUI ui) {
  271. super.setUI(ui);
  272. }
  273. /**
  274. * Notification from the UIFactory that the L&F has changed.
  275. * Called to replace the UI with the latest version from the
  276. * UIFactory.
  277. *
  278. * @see JComponent#updateUI
  279. */
  280. public void updateUI() {
  281. setUI((PopupMenuUI)UIManager.getUI(this));
  282. }
  283. /**
  284. * Returns the name of the L&F class that renders this component.
  285. *
  286. * @return "PopupMenuUI"
  287. * @see JComponent#getUIClassID
  288. * @see UIDefaults#getUI
  289. */
  290. public String getUIClassID() {
  291. return uiClassID;
  292. }
  293. /**
  294. * Returns the model object that handles single selections.
  295. *
  296. * @return the SingleSelectionModel in use
  297. * @see SingleSelectionModel
  298. */
  299. public SingleSelectionModel getSelectionModel() {
  300. return selectionModel;
  301. }
  302. /**
  303. * Set the model object to handle single selections.
  304. *
  305. * @param model the SingleSelectionModel to use
  306. * @see SingleSelectionModel
  307. * @beaninfo
  308. * description: The selection model for the popup menu
  309. * expert: true
  310. */
  311. public void setSelectionModel(SingleSelectionModel model) {
  312. selectionModel = model;
  313. }
  314. /**
  315. * Appends the specified menu item to the end of this menu.
  316. *
  317. * @param c the JMenuItem to add
  318. * @return the JMenuItem added.
  319. */
  320. public JMenuItem add(JMenuItem menuItem) {
  321. super.add(menuItem);
  322. return menuItem;
  323. }
  324. /**
  325. * Creates a new menuitem with the specified text and appends
  326. * it to the end of this menu.
  327. *
  328. * @param s the string for the menuitem to be added
  329. */
  330. public JMenuItem add(String s) {
  331. return add(new JMenuItem(s));
  332. }
  333. /**
  334. * Append a new menuitem to the end of the menu which
  335. * dispatches the specified Action object.
  336. *
  337. * @param a the Action to add to the menu
  338. * @see Action
  339. */
  340. public JMenuItem add(Action a) {
  341. JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  342. (Icon)a.getValue(Action.SMALL_ICON));
  343. mi.setHorizontalTextPosition(JButton.RIGHT);
  344. mi.setVerticalTextPosition(JButton.CENTER);
  345. mi.setEnabled(a.isEnabled());
  346. mi.addActionListener(a);
  347. add(mi);
  348. registerMenuItemForAction(mi, a);
  349. return mi;
  350. }
  351. /**
  352. * Removes the specified component from this popup menu.
  353. *
  354. * @param item the JMenuItem to be removed from the menu
  355. */
  356. public void remove(Component comp) {
  357. super.remove(comp);
  358. if (comp instanceof JMenuItem) {
  359. JMenuItem item = (JMenuItem)comp;
  360. unregisterMenuItemForAction(item);
  361. }
  362. }
  363. /**
  364. * Removes the component at the specified index from this popup menu.
  365. *
  366. * @param index the position of the item to be removed.
  367. * @exception IllegalArgumentException if the value of
  368. * <code>index</code> is less than 0.
  369. */
  370. public void remove(int pos) {
  371. if (pos < 0) {
  372. throw new IllegalArgumentException("index less than zero.");
  373. }
  374. if (pos > getComponentCount() -1) {
  375. throw new IllegalArgumentException("index greater than the number of items.");
  376. }
  377. Component c = getComponent(pos);
  378. if (c instanceof JMenuItem)
  379. unregisterMenuItemForAction((JMenuItem)c);
  380. super.remove(pos);
  381. }
  382. private void registerMenuItemForAction(JMenuItem mi, Action a) {
  383. PropertyChangeListener actionPropertyChangeListener =
  384. createActionChangeListener(mi);
  385. if (listenerRegistry == null) {
  386. listenerRegistry = new Hashtable();
  387. }
  388. listenerRegistry.put(mi, actionPropertyChangeListener);
  389. listenerRegistry.put(actionPropertyChangeListener, a);
  390. a.addPropertyChangeListener(actionPropertyChangeListener);
  391. }
  392. private void unregisterMenuItemForAction(JMenuItem item) {
  393. if (listenerRegistry != null) {
  394. ActionChangedListener p = (ActionChangedListener)listenerRegistry.remove(item);
  395. if (p!=null) {
  396. Action a = (Action)listenerRegistry.remove(p);
  397. if (a!=null) {
  398. item.removeActionListener(a);
  399. a.removePropertyChangeListener(p);
  400. }
  401. p.setTarget(null);
  402. }
  403. }
  404. }
  405. protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
  406. return new ActionChangedListener(b);
  407. }
  408. private class ActionChangedListener implements PropertyChangeListener {
  409. JMenuItem menuItem;
  410. ActionChangedListener(JMenuItem mi) {
  411. super();
  412. setTarget(mi);
  413. }
  414. public void propertyChange(PropertyChangeEvent e) {
  415. String propertyName = e.getPropertyName();
  416. if (e.getPropertyName().equals(Action.NAME)) {
  417. String text = (String) e.getNewValue();
  418. menuItem.setText(text);
  419. } else if (propertyName.equals("enabled")) {
  420. Boolean enabledState = (Boolean) e.getNewValue();
  421. menuItem.setEnabled(enabledState.booleanValue());
  422. } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  423. Icon icon = (Icon) e.getNewValue();
  424. menuItem.setIcon(icon);
  425. menuItem.invalidate();
  426. menuItem.repaint();
  427. }
  428. }
  429. public void setTarget(JMenuItem b) {
  430. this.menuItem = b;
  431. }
  432. }
  433. /**
  434. * When displaying the popup, JPopupMenu choose to use a light weight popup if
  435. * it fits. This method allows you to disable this feature. You have to do disable
  436. * it if your application mixes light weight and heavy weights components.
  437. * @beaninfo
  438. * description: Determines whether lightweight popups are used when possible
  439. * expert: true
  440. */
  441. public void setLightWeightPopupEnabled(boolean aFlag) {
  442. lightWeightPopupEnabled = aFlag;
  443. }
  444. /**
  445. * Returns true if lightweight (all-Java) popups are in use,
  446. * or false if heavyweight (native peer) popups are being used.
  447. *
  448. * @return true if lightweight popups are in use
  449. */
  450. public boolean isLightWeightPopupEnabled() {
  451. return lightWeightPopupEnabled;
  452. }
  453. /**
  454. * Returns the popup menu's label
  455. * @return a String containing the popup menu's label
  456. * @see #setLabel
  457. */
  458. public String getLabel() {
  459. return label;
  460. }
  461. /**
  462. * Sets the popup menu's label. Different Look and Feels may choose
  463. * to display or not display this.
  464. * @param label a String specifying the label for the popup menu
  465. * @see #setLabel
  466. * @beaninfo
  467. * description: The label for the popup menu.
  468. * bound: true
  469. */
  470. public void setLabel(String label) {
  471. String oldValue = this.label;
  472. this.label = label;
  473. firePropertyChange("label", oldValue, label);
  474. if (accessibleContext != null) {
  475. accessibleContext.firePropertyChange(
  476. AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  477. oldValue, label);
  478. }
  479. invalidate();
  480. repaint();
  481. }
  482. /**
  483. * Appends a new separator at the end of the menu.
  484. */
  485. public void addSeparator() {
  486. add( new JPopupMenu.Separator() );
  487. }
  488. /**
  489. * Inserts a menu item for the specified Action object at a given
  490. * position.
  491. *
  492. * @param component the Action object to insert
  493. * @param index an int specifying the position at which
  494. * to insert the Action, where 0 is the first
  495. * @see Action
  496. */
  497. public void insert(Action a, int index) {
  498. throw new Error("void insert(Action, int) {} not yet implemented");
  499. }
  500. /**
  501. * Inserts the specified component into the menu at a given
  502. * position.
  503. *
  504. * @param component the Component to insert
  505. * @param index an int specifying the position at which
  506. * to insert the component, where 0 is the first
  507. */
  508. public void insert(Component component, int index) {
  509. if (index < 0) {
  510. throw new IllegalArgumentException("index less than zero.");
  511. }
  512. int nitems = getComponentCount();
  513. Vector tempItems = new Vector();
  514. /* Remove the item at index, nitems-index times
  515. storing them in a temporary vector in the
  516. order they appear on the menu.
  517. */
  518. for (int i = index ; i < nitems; i++) {
  519. tempItems.addElement(getComponent(index));
  520. remove(index);
  521. }
  522. add(component);
  523. /* Add the removed items back to the menu, they are
  524. already in the correct order in the temp vector.
  525. */
  526. for (int i = 0; i < tempItems.size() ; i++) {
  527. add((Component)tempItems.elementAt(i));
  528. }
  529. }
  530. /**
  531. * Add a PopupMenu listener
  532. *
  533. * param l the PopupMenuListener to add
  534. */
  535. public void addPopupMenuListener(PopupMenuListener l) {
  536. listenerList.add(PopupMenuListener.class,l);
  537. }
  538. /**
  539. * Remove a PopupMenu listener
  540. *
  541. * param l the PopupMenuListener to remove
  542. */
  543. public void removePopupMenuListener(PopupMenuListener l) {
  544. listenerList.remove(PopupMenuListener.class,l);
  545. }
  546. /**
  547. * Notifies PopupMenuListeners that this popup menu will become
  548. * visible
  549. */
  550. protected void firePopupMenuWillBecomeVisible() {
  551. Object[] listeners = listenerList.getListenerList();
  552. PopupMenuEvent e=null;
  553. for (int i = listeners.length-2; i>=0; i-=2) {
  554. if (listeners[i]==PopupMenuListener.class) {
  555. if (e == null)
  556. e = new PopupMenuEvent(this);
  557. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
  558. }
  559. }
  560. }
  561. /**
  562. * Notifies PopupMenuListeners that this popup menu will become
  563. * invisible
  564. */
  565. protected void firePopupMenuWillBecomeInvisible() {
  566. Object[] listeners = listenerList.getListenerList();
  567. PopupMenuEvent e=null;
  568. for (int i = listeners.length-2; i>=0; i-=2) {
  569. if (listeners[i]==PopupMenuListener.class) {
  570. if (e == null)
  571. e = new PopupMenuEvent(this);
  572. ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
  573. }
  574. }
  575. }
  576. /**
  577. * Notifies PopupMenuListeners that this popup menu is canceled
  578. */
  579. protected void firePopupMenuCanceled() {
  580. Object[] listeners = listenerList.getListenerList();
  581. PopupMenuEvent e=null;
  582. for (int i = listeners.length-2; i>=0; i-=2) {
  583. if (listeners[i]==PopupMenuListener.class) {
  584. if (e == null)
  585. e = new PopupMenuEvent(this);
  586. ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
  587. }
  588. }
  589. }
  590. /**
  591. * Always return true since popups, by definition, should always
  592. * be on top of all other windows.
  593. */
  594. // package private
  595. boolean alwaysOnTop() {
  596. return true;
  597. }
  598. /**
  599. * Layout the container so that it uses the minimum space
  600. * needed to display its contents.
  601. */
  602. public void pack() {
  603. if(popup != null)
  604. popup.pack();
  605. }
  606. private Popup createLightWeightPopup() {
  607. Popup popup;
  608. popup = JPopupMenu.getRecycledLightPopup();
  609. if(popup == null) {
  610. popup = new JPanelPopup();
  611. }
  612. return popup;
  613. }
  614. private Popup createMediumWeightPopup() {
  615. Popup popup;
  616. popup = JPopupMenu.getRecycledMediumPopup();
  617. if(popup == null) {
  618. popup = new PanelPopup();
  619. }
  620. return popup;
  621. }
  622. private Popup createHeavyWeightPopup() {
  623. Frame frame = getFrame(invoker);
  624. if (frame != null) {
  625. popup = JPopupMenu.getRecycledHeavyPopup(frame);
  626. } else {
  627. frame = new Frame();
  628. }
  629. if (popup == null)
  630. popup = new WindowPopup(frame);
  631. return popup;
  632. }
  633. private boolean popupFit(Rectangle popupRectInScreen) {
  634. if(invoker != null) {
  635. Container parent;
  636. for(parent = invoker.getParent(); parent != null ; parent = parent.getParent()) {
  637. if(parent instanceof JFrame || parent instanceof JDialog ||
  638. parent instanceof JWindow) {
  639. return SwingUtilities.isRectangleContainingRectangle(parent.getBounds(),popupRectInScreen);
  640. } else if(parent instanceof JApplet) {
  641. Rectangle r = parent.getBounds();
  642. Point p = parent.getLocationOnScreen();
  643. r.x = p.x;
  644. r.y = p.y;
  645. return SwingUtilities.isRectangleContainingRectangle(r,popupRectInScreen);
  646. } else if(parent instanceof java.awt.Frame) {
  647. return SwingUtilities.isRectangleContainingRectangle(parent.getBounds(),popupRectInScreen);
  648. }
  649. }
  650. }
  651. return false;
  652. }
  653. private boolean ancestorIsModalDialog(Component i) {
  654. Container parent = null;
  655. if (i !=null) {
  656. for(parent = i.getParent() ; parent != null ; parent = parent.getParent())
  657. if ((parent instanceof Dialog) && (((Dialog)parent).isModal() == true))
  658. return true;
  659. }
  660. return false;
  661. }
  662. private void replacePopup(int newType) {
  663. popup.removeComponent(this);
  664. recyclePopup(popup);
  665. popup = null;
  666. switch(newType) {
  667. case LIGHT_WEIGHT_POPUP:
  668. popup = createLightWeightPopup();
  669. break;
  670. case MEDIUM_WEIGHT_POPUP:
  671. popup = createMediumWeightPopup();
  672. break;
  673. case HEAVY_WEIGHT_POPUP:
  674. popup = createHeavyWeightPopup();
  675. break;
  676. }
  677. popup.setLocationOnScreen(desiredLocationX,desiredLocationY);
  678. popup.addComponent(this,"Center");
  679. invalidate();
  680. popup.setBackground(getBackground());
  681. popup.pack();
  682. }
  683. /**
  684. * Set the visibility of the popup menu.
  685. *
  686. * @param b true to make the popup visible, or false to
  687. * hide it
  688. * @beaninfo
  689. * description: Makes the popup visible
  690. */
  691. public void setVisible(boolean b) {
  692. // Is it a no-op?
  693. if (b == isVisible())
  694. return;
  695. // if closing, first close all Submenus
  696. if (b == false) {
  697. getSelectionModel().clearSelection();
  698. } else {
  699. // This is a popupmenu with MenuElement children,
  700. // set selection path before popping up!
  701. if (isPopupMenu()) {
  702. if (getSubElements().length > 0) {
  703. MenuElement me[] = new MenuElement[2];
  704. me[0]=(MenuElement)this;
  705. me[1]=getSubElements()[0];
  706. MenuSelectionManager.defaultManager().setSelectedPath(me);
  707. } else {
  708. MenuElement me[] = new MenuElement[1];
  709. me[0]=(MenuElement)this;
  710. MenuSelectionManager.defaultManager().setSelectedPath(me);
  711. }
  712. }
  713. }
  714. if(b) {
  715. int popupType;
  716. int newPopupType;
  717. boolean shouldDowngradeHeavyWeight = ancestorIsModalDialog(invoker);
  718. firePopupMenuWillBecomeVisible();
  719. switch(lastPopupType) {
  720. case LIGHT_WEIGHT_POPUP:
  721. popup = createLightWeightPopup();
  722. break;
  723. case MEDIUM_WEIGHT_POPUP:
  724. popup = createMediumWeightPopup();
  725. break;
  726. case HEAVY_WEIGHT_POPUP:
  727. popup = createHeavyWeightPopup();
  728. break;
  729. }
  730. popupType = lastPopupType;
  731. popup.setLocationOnScreen(desiredLocationX,desiredLocationY);
  732. popup.addComponent(this,"Center");
  733. popup.setBackground(getBackground());
  734. popup.pack();
  735. Rectangle popupRect = new Rectangle(desiredLocationX,desiredLocationY,
  736. popup.getWidth(),popup.getHeight());
  737. if(popupFit(popupRect)) {
  738. if(lightWeightPopupEnabled)
  739. newPopupType = LIGHT_WEIGHT_POPUP;
  740. else
  741. newPopupType = MEDIUM_WEIGHT_POPUP;
  742. } else {
  743. if(shouldDowngradeHeavyWeight)
  744. newPopupType = MEDIUM_WEIGHT_POPUP;
  745. else
  746. newPopupType = HEAVY_WEIGHT_POPUP;
  747. }
  748. if(invokerInHeavyWeightPopup(invoker))
  749. newPopupType = HEAVY_WEIGHT_POPUP;
  750. if(invoker == null) {
  751. newPopupType = HEAVY_WEIGHT_POPUP;
  752. }
  753. if(newPopupType != popupType) {
  754. replacePopup(newPopupType);
  755. popupType = newPopupType;
  756. }
  757. lastPopupType = popupType;
  758. popup.show(invoker);
  759. } else if(popup != null) {
  760. firePopupMenuWillBecomeInvisible();
  761. popup.hide();
  762. popup.removeComponent(this);
  763. recyclePopup(popup);
  764. popup = null;
  765. }
  766. if (accessibleContext != null) {
  767. if (b) {
  768. accessibleContext.firePropertyChange(
  769. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  770. null, AccessibleState.VISIBLE);
  771. } else {
  772. accessibleContext.firePropertyChange(
  773. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  774. AccessibleState.VISIBLE, null);
  775. }
  776. }
  777. }
  778. /**
  779. * Returns true if the popupmenu is visible (currently
  780. * being displayed).
  781. */
  782. public boolean isVisible() {
  783. if(popup != null)
  784. return popup.isShowing();
  785. else
  786. return false;
  787. }
  788. /**
  789. * Set the location of the upper left corner of the
  790. * popup menu using x, y coordinates.
  791. *
  792. * @param x the x coordinate of the popup's new position
  793. * @param y the y coordinate of the popup's new position
  794. * @beaninfo
  795. * description: The location of the popup menu.
  796. */
  797. public void setLocation(int x, int y) {
  798. if(popup != null)
  799. popup.setLocationOnScreen(x, y);
  800. else {
  801. desiredLocationX = x;
  802. desiredLocationY = y;
  803. }
  804. }
  805. /**
  806. * Returns true if the popupmenu is a stand-alone popup menu
  807. * rather than the submenu of a JMenu.
  808. *
  809. * @return true if this menu is a stand-alone popup menu
  810. */
  811. private boolean isPopupMenu() {
  812. return ((invoker != null) && !(invoker instanceof JMenu));
  813. }
  814. /**
  815. * Returns the component which is the 'invoker' of this
  816. * popup menu.
  817. *
  818. * @return the Component in which the popup menu is displayed
  819. */
  820. public Component getInvoker() {
  821. return this.invoker;
  822. }
  823. /**
  824. * Sets the invoker of this popupmenu -- the component in which
  825. * the popupmenu menu is to be displayed.
  826. *
  827. * @param invoker the Component in which the popup menu is displayed
  828. * @beaninfo
  829. * description: The invoking component for the popup menu
  830. * expert: true
  831. */
  832. public void setInvoker(Component invoker) {
  833. Component oldInvoker = this.invoker;
  834. this.invoker = invoker;
  835. if ((oldInvoker != this.invoker) && (ui != null)) {
  836. ui.uninstallUI(this);
  837. ui.installUI(this);
  838. }
  839. invalidate();
  840. }
  841. /**
  842. * Display the popupmenu at the position x,y in the coordinate
  843. * space of the component invoker.
  844. *
  845. * @param invoker The component in whose space the popupmenu is to appear
  846. * @param x the x coordinate in invoker's coordinate space at which
  847. * the popup menu is to be displayed
  848. * @param y the y coordinate in invoker's coordinate space at which
  849. * the popup menu is to be displayed
  850. */
  851. public void show(Component invoker, int x, int y) {
  852. setInvoker(invoker);
  853. Frame newFrame = getFrame(invoker);
  854. if (newFrame != frame) {
  855. // Use the invoker's frame so that events
  856. // are propogated properly
  857. if (newFrame!=null) {
  858. this.frame = newFrame;
  859. if(popup != null) {
  860. setVisible(false);
  861. }
  862. }
  863. }
  864. Point invokerOrigin;
  865. if (invoker != null) {
  866. invokerOrigin = invoker.getLocationOnScreen();
  867. setLocation(invokerOrigin.x + x,
  868. invokerOrigin.y + y);
  869. } else {
  870. setLocation(x, y);
  871. }
  872. setVisible(true);
  873. }
  874. /**
  875. * Returns the popupmenu which is at the root of the menu system
  876. * for this popupmenu.
  877. *
  878. * @return the topmost grandparent JPopupMenu
  879. */
  880. JPopupMenu getRootPopupMenu() {
  881. JPopupMenu mp = this;
  882. while((mp!=null) && (mp.isPopupMenu()!=true) &&
  883. (mp.getInvoker() != null) &&
  884. (mp.getInvoker().getParent() != null) &&
  885. (mp.getInvoker().getParent() instanceof JPopupMenu)
  886. ) {
  887. mp = (JPopupMenu) mp.getInvoker().getParent();
  888. }
  889. return mp;
  890. }
  891. /**
  892. * Returns the component at the specified index.
  893. * This method is obsolete, please use <code>getComponent(int i)</code> instead.
  894. *
  895. * @param i the index of the component, where 0 is the first
  896. * @return the Component at that index
  897. */
  898. public Component getComponentAtIndex(int i) {
  899. return getComponent(i);
  900. }
  901. /**
  902. * Returns the index of the specified component.
  903. *
  904. * @param the Component to find
  905. * @return the index of the component, where 0 is the first,
  906. * or -1 if the component is not found
  907. */
  908. public int getComponentIndex(Component c) {
  909. int ncomponents = this.getComponentCount();
  910. Component[] component = this.getComponents();
  911. for (int i = 0 ; i < ncomponents ; i++) {
  912. Component comp = component[i];
  913. if (comp == c)
  914. return i;
  915. }
  916. return -1;
  917. }
  918. /**
  919. * Sets the size of the Popup window using a Dimension object.
  920. *
  921. * @param <code>d</code> The dimension specifying the new size
  922. * of this component.
  923. * @beaninfo
  924. * description: The size of the popup menu
  925. */
  926. public void setPopupSize(Dimension d) {
  927. if(popup != null)
  928. popup.setSize(d.width,d.height);
  929. }
  930. /**
  931. * Sets the size of the Popup window to the specified width and
  932. * height.
  933. *
  934. * @param <code>width</code> The new width of the Popup in pixels.
  935. * @param <code>height</code> The new height of the Popup in pixels.
  936. * @beaninfo
  937. * description: The size of the popup menu
  938. */
  939. public void setPopupSize(int width, int height) {
  940. if(popup != null)
  941. popup.setSize(width, height);
  942. }
  943. /**
  944. * Sets the currently selected component, This will result
  945. * in a change to the selection model.
  946. *
  947. * @param sel the Component to select
  948. * @beaninfo
  949. * description: The selected component on the popup menu
  950. * expert: true
  951. * hidden: true
  952. */
  953. public void setSelected(Component sel) {
  954. SingleSelectionModel model = getSelectionModel();
  955. int index = getComponentIndex(sel);
  956. model.setSelectedIndex(index);
  957. }
  958. /**
  959. * Checks whether the border should be painted.
  960. *
  961. * @return true if the border is painted
  962. * @see #setBorderPainted
  963. */
  964. public boolean isBorderPainted() {
  965. return paintBorder;
  966. }
  967. /**
  968. * Sets whether the border should be painted.
  969. *
  970. * @param b if true, the border is painted.
  971. * @see #isBorderPainted
  972. * @beaninfo
  973. * description: Is the border of the popup menu painted
  974. */
  975. public void setBorderPainted(boolean b) {
  976. paintBorder = b;
  977. repaint();
  978. }
  979. /**
  980. * Paint the popup menu's border if BorderPainted property is true.
  981. *
  982. * @see JComponent#paint
  983. * @see JComponent#setBorder
  984. */
  985. protected void paintBorder(Graphics g) {
  986. if (isBorderPainted()) {
  987. super.paintBorder(g);
  988. }
  989. }
  990. /**
  991. * Returns the margin between the popupmenu's border and
  992. * its containees.
  993. *
  994. * return an Insets object containing the margin values.
  995. */
  996. public Insets getMargin() {
  997. if(margin == null) {
  998. return new Insets(0,0,0,0);
  999. } else {
  1000. return margin;
  1001. }
  1002. }
  1003. boolean isSubPopupMenu(JPopupMenu popup) {
  1004. int ncomponents = this.getComponentCount();
  1005. Component[] component = this.getComponents();
  1006. for (int i = 0 ; i < ncomponents ; i++) {
  1007. Component comp = component[i];
  1008. if (comp instanceof JMenu) {
  1009. JMenu menu = (JMenu)comp;
  1010. JPopupMenu subPopup = menu.getPopupMenu();
  1011. if (subPopup == popup)
  1012. return true;
  1013. if (subPopup.isSubPopupMenu(popup))
  1014. return true;
  1015. }
  1016. }
  1017. return false;
  1018. }
  1019. private boolean invokerInHeavyWeightPopup(Component i) {
  1020. if (i !=null) {
  1021. Container parent;
  1022. for(parent = i.getParent() ; parent != null ; parent =
  1023. parent.getParent()) {
  1024. if(parent instanceof WindowPopup)
  1025. return true;
  1026. else if(parent instanceof PanelPopup)
  1027. break;
  1028. else if(parent instanceof JPanelPopup)
  1029. break;
  1030. }
  1031. }
  1032. return false;
  1033. }
  1034. private static Frame getFrame(Component c) {
  1035. Component w = c;
  1036. while(!(w instanceof Frame) && (w!=null)) {
  1037. w = w.getParent();
  1038. }
  1039. return (Frame)w;
  1040. }
  1041. /*
  1042. * The following interface describes what a popup should implement.
  1043. * We do this because JPopupMenu uses popup that can be windows or
  1044. * panels.
  1045. */
  1046. private interface Popup {
  1047. public void setSize(int width,int height);
  1048. public int getWidth();
  1049. public int getHeight();
  1050. public void addComponent(Component aComponent,Object constraints);
  1051. public void removeComponent(Component c);
  1052. public void pack();
  1053. public void setBackground(Color c);
  1054. public void show(Component invoker);
  1055. public void hide();
  1056. public boolean isShowing();
  1057. public Rectangle getBoundsOnScreen();
  1058. public void setLocationOnScreen(int x,int y);
  1059. public Component getComponent();
  1060. }
  1061. /**
  1062. * A class used to popup a window.
  1063. * <p>
  1064. * <strong>Warning:</strong>
  1065. * Serialized objects of this class will not be compatible with
  1066. * future Swing releases. The current serialization support is appropriate
  1067. * for short term storage or RMI between applications running the same
  1068. * version of Swing. A future release of Swing will provide support for
  1069. * long term persistence.
  1070. */
  1071. class WindowPopup extends JWindow implements Popup,Serializable,Accessible {
  1072. int saveX,saveY;
  1073. boolean firstShow = true;
  1074. public WindowPopup(Frame f) {
  1075. super(f);
  1076. }
  1077. public Component getComponent() {
  1078. return this;
  1079. }
  1080. public int getWidth() {
  1081. return getBounds().width;
  1082. }
  1083. public int getHeight() {
  1084. return getBounds().height;
  1085. }
  1086. public void update(Graphics g) {
  1087. paint(g);
  1088. }
  1089. public void show(Component invoker) {
  1090. this.setLocation(saveX,saveY);
  1091. this.setVisible(true);
  1092. /** This hack is to workaround a bug on Solaris where the windows does not really show
  1093. * the first time
  1094. */
  1095. if(firstShow) {
  1096. this.hide();
  1097. this.setVisible(true);
  1098. firstShow = false;
  1099. }
  1100. }
  1101. public void hide() {
  1102. super.hide();
  1103. /** We need to call removeNotify() here because hide() does something only if
  1104. * Component.visible is true. When the app frame is miniaturized, the parent
  1105. * frame of this frame is invisible, causing AWT to believe that this frame
  1106. * is invisible and causing hide() to do nothing
  1107. */
  1108. removeNotify();
  1109. }
  1110. public Rectangle getBoundsOnScreen() {
  1111. return getBounds();
  1112. }
  1113. public void setLocationOnScreen(int x,int y) {
  1114. this.setLocation(x,y);
  1115. saveX = x;
  1116. saveY = y;
  1117. }
  1118. public void addComponent(Component aComponent,Object constraints) {
  1119. this.getContentPane().add(aComponent,constraints);
  1120. }
  1121. public void removeComponent(Component c) {
  1122. this.getContentPane().remove(c);
  1123. }
  1124. /////////////////
  1125. // Accessibility support
  1126. ////////////////
  1127. protected AccessibleContext accessibleContext = null;
  1128. /**
  1129. * Get the AccessibleContext associated with this JWindow
  1130. *
  1131. * @return the AccessibleContext of this JWindow
  1132. */
  1133. public AccessibleContext getAccessibleContext() {
  1134. if (accessibleContext == null) {
  1135. accessibleContext = new AccessibleWindowPopup();
  1136. }
  1137. return accessibleContext;
  1138. }
  1139. /**
  1140. * The class used to obtain the accessible role for this object.
  1141. * <p>
  1142. * <strong>Warning:</strong>
  1143. * Serialized objects of this class will not be compatible with
  1144. * future Swing releases. The current serialization support is appropriate
  1145. * for short term storage or RMI between applications running the same
  1146. * version of Swing. A future release of Swing will provide support for
  1147. * long term persistence.
  1148. */
  1149. protected class AccessibleWindowPopup extends AccessibleContext
  1150. implements Serializable, AccessibleComponent {
  1151. // AccessibleContext methods
  1152. //
  1153. /**
  1154. * Get the role of this object.
  1155. *
  1156. * @return an instance of AccessibleRole describing the role of
  1157. * the object
  1158. * @see AccessibleRole
  1159. */
  1160. public AccessibleRole getAccessibleRole() {
  1161. return AccessibleRole.WINDOW;
  1162. }
  1163. /**
  1164. * Get the state of this object.
  1165. *
  1166. * @return an instance of AccessibleStateSet containing the
  1167. * current state set of the object
  1168. * @see AccessibleState
  1169. */
  1170. public AccessibleStateSet getAccessibleStateSet() {
  1171. AccessibleStateSet states = SwingUtilities.getAccessibleStateSet(WindowPopup.this);
  1172. if (getFocusOwner() != null) {
  1173. states.add(AccessibleState.ACTIVE);
  1174. }
  1175. return states;
  1176. }
  1177. /**
  1178. * Get the Accessible parent of this object. If the parent of this
  1179. * object implements Accessible, this method should simply return
  1180. * getParent().
  1181. *
  1182. * @return the Accessible parent of this object -- can be null if
  1183. * this object does not have an Accessible parent
  1184. */
  1185. public Accessible getAccessibleParent() {
  1186. if (accessibleParent != null) {
  1187. return accessibleParent;
  1188. } else {
  1189. Container parent = getParent();
  1190. if (parent instanceof Accessible) {
  1191. return (Accessible) parent;
  1192. }
  1193. }
  1194. return null;
  1195. }
  1196. /**
  1197. * Get the index of this object in its accessible parent.
  1198. *
  1199. * @return the index of this object in its parent; -1 if this
  1200. * object does not have an accessible parent.
  1201. * @see #getAccessibleParent
  1202. */
  1203. public int getAccessibleIndexInParent() {
  1204. return SwingUtilities.getAccessibleIndexInParent(WindowPopup.this);
  1205. }
  1206. /**
  1207. * Returns the number of accessible children in the object. If all
  1208. * of the children of this object implement Accessible, than this
  1209. * method should return the number of children of this object.
  1210. *
  1211. * @return the number of accessible children in the object.
  1212. */
  1213. public int getAccessibleChildrenCount() {
  1214. return SwingUtilities.getAccessibleChildrenCount(WindowPopup.this);
  1215. }
  1216. /**
  1217. * Return the nth Accessible child of the object.
  1218. *
  1219. * @param i zero-based index of child
  1220. * @return the nth Accessible child of the object
  1221. */
  1222. public Accessible getAccessibleChild(int i) {
  1223. return SwingUtilities.getAccessibleChild(WindowPopup.this,i);
  1224. }
  1225. /**
  1226. * Return the locale of this object.
  1227. *
  1228. * @return the locale of this object
  1229. */
  1230. public Locale getLocale() {
  1231. return WindowPopup.this.getLocale();
  1232. }
  1233. /**
  1234. * Get the AccessibleComponent associated with this object if one
  1235. * exists. Otherwise return null.
  1236. */
  1237. public AccessibleComponent getAccessibleComponent() {
  1238. return this;
  1239. }
  1240. // AccessibleComponent methods
  1241. //
  1242. /**
  1243. * Get the background color of this object.
  1244. *
  1245. * @return the background color, if supported, of the object;
  1246. * otherwise, null
  1247. */
  1248. public Color getBackground() {
  1249. return WindowPopup.this.getBackground();
  1250. }
  1251. /**
  1252. * Set the background color of this object.
  1253. *
  1254. * @param c the new Color for the background
  1255. */
  1256. public void setBackground(Color c) {
  1257. WindowPopup.this.setBackground(c);
  1258. }
  1259. /**
  1260. * Get the foreground color of this object.
  1261. *
  1262. * @return the foreground color, if supported, of the object;
  1263. * otherwise, null
  1264. */
  1265. public Color getForeground() {
  1266. return WindowPopup.this.getForeground();
  1267. }
  1268. /**
  1269. * Set the foreground color of this object.
  1270. *
  1271. * @param c the new Color for the foreground
  1272. */
  1273. public void setForeground(Color c) {
  1274. WindowPopup.this.setForeground(c);
  1275. }
  1276. /**
  1277. * Get the Cursor of this object.
  1278. *
  1279. * @return the Cursor, if supported, of the object; otherwise, null
  1280. */
  1281. public Cursor getCursor() {
  1282. return WindowPopup.this.getCursor();
  1283. }
  1284. /**
  1285. * Set the Cursor of this object.
  1286. *
  1287. * @param c the new Cursor for the object
  1288. */
  1289. public void setCursor(Cursor cursor) {
  1290. WindowPopup.this.setCursor(cursor);
  1291. }
  1292. /**
  1293. * Get the Font of this object.
  1294. *
  1295. * @return the Font,if supported, for the object; otherwise, null
  1296. */
  1297. public Font getFont() {
  1298. return WindowPopup.this.getFont();
  1299. }
  1300. /**
  1301. * Set the Font of this object.
  1302. *
  1303. * @param f the new Font for the object
  1304. */
  1305. public void setFont(Font f) {
  1306. WindowPopup.this.setFont(f);
  1307. }
  1308. /**
  1309. * Get the FontMetrics of this object.
  1310. *
  1311. * @param f the Font
  1312. * @return the FontMetrics, if supported, the object;
  1313. * otherwise, null
  1314. * @see #getFont
  1315. */
  1316. public FontMetrics getFontMetrics(Font f) {
  1317. return WindowPopup.this.getFontMetrics(f);
  1318. }
  1319. /**
  1320. * Determine if the object is enabled.
  1321. *
  1322. * @return true if object is enabled; otherwise, false
  1323. */
  1324. public boolean isEnabled() {
  1325. return WindowPopup.this.isEnabled();
  1326. }
  1327. /**
  1328. * Set the enabled state of the object.
  1329. *
  1330. * @param b if true, enables this object; otherwise, disables it
  1331. */
  1332. public void setEnabled(boolean b) {
  1333. WindowPopup.this.setEnabled(b);
  1334. }
  1335. /**
  1336. * Determine if the object is visible. Note: this means that the
  1337. * object intends to be visible; however, it may not in fact be
  1338. * showing on the screen because one of the objects this object
  1339. * is contained by is not visible. To determine if an object is
  1340. * showing on the screen, use isShowing().
  1341. *
  1342. * @return true if object is visible; otherwise, false
  1343. */
  1344. public boolean isVisible() {
  1345. return WindowPopup.this.isVisible();
  1346. }
  1347. /**
  1348. * Set the visible state of the object.
  1349. *
  1350. * @param b if true, shows this object; otherwise, hides it
  1351. */
  1352. public void setVisible(boolean b) {
  1353. WindowPopup.this.setVisible(b);
  1354. }
  1355. /**
  1356. * Determine if the object is showing. Determined by checking
  1357. * the visibility of the object and ancestors of the object.
  1358. * This will return true even if the object is obscured by another
  1359. * (for example, it is underneath a menu that was pulled
  1360. * down).
  1361. *
  1362. * @return true if object is showing; otherwise, false
  1363. */
  1364. public boolean isShowing() {
  1365. return WindowPopup.this.isShowing();
  1366. }
  1367. /**
  1368. * Checks if the specified point is within this object's bounds,
  1369. * where the point's x and y coordinates are defined to be relative
  1370. * to the coordinate system of the object.
  1371. *
  1372. * @param p the Point relative to the coordinate system of the
  1373. * object
  1374. * @return true if object contains Point; otherwise false
  1375. */
  1376. public boolean contains(Point p) {
  1377. return WindowPopup.this.contains(p);
  1378. }
  1379. /**
  1380. * Returns the location of the object on the screen.
  1381. *
  1382. * @return location of object on screen -- can be null if this
  1383. * object is not on the screen
  1384. */
  1385. public Point getLocationOnScreen() {
  1386. return WindowPopup.this.getLocationOnScreen();
  1387. }
  1388. /**
  1389. * Gets the location of the object relative to the parent in the
  1390. * form of a point specifying the object's top-left corner in the
  1391. * screen's coordinate space.
  1392. *
  1393. * @return An instance of Point representing the top-left corner
  1394. * of the objects's bounds in the coordinate space of the screen;
  1395. * null if this object or its parent are not on the screen
  1396. */
  1397. public Point getLocation() {
  1398. return WindowPopup.this.getLocation();
  1399. }
  1400. /**
  1401. * Sets the location of the object relative to the parent.
  1402. */
  1403. public void setLocation(Point p) {
  1404. WindowPopup.this.setLocation(p);
  1405. }
  1406. /**
  1407. * Gets the bounds of this object in the form of a Rectangle
  1408. * object. The bounds specify this object's width, height,
  1409. * and location relative to its parent.
  1410. *
  1411. * @return A rectangle indicating this component's bounds; null if
  1412. * this object is not on the screen.
  1413. */
  1414. public Rectangle getBounds() {
  1415. return WindowPopup.this.getBounds();
  1416. }
  1417. /**
  1418. * Sets the bounds of this object in the form of a Rectangle
  1419. * object. The bounds specify this object's width, height,
  1420. * and location relative to its parent.
  1421. *
  1422. * @param A rectangle indicating this component's bounds
  1423. */
  1424. public void setBounds(Rectangle r) {
  1425. WindowPopup.this.setBounds(r);
  1426. }
  1427. /**
  1428. * Returns the size of this object in the form of a Dimension
  1429. * object. The height field of the Dimension object contains
  1430. * this objects's height, and the width field of the Dimension
  1431. * object contains this object's width.
  1432. *
  1433. * @return A Dimension object that indicates the size of this
  1434. * component; null if this object is not on the screen
  1435. */
  1436. public Dimension getSize() {
  1437. return WindowPopup.this.getSize();
  1438. }
  1439. /**
  1440. * Resizes this object so that it has width width and height.
  1441. *
  1442. * @param d - The dimension specifying the new size of the object.
  1443. */
  1444. public void setSize(Dimension d) {
  1445. WindowPopup.this.setSize(d);
  1446. }
  1447. /**
  1448. * Returns the Accessible child, if one exists, contained at the
  1449. * local coordinate Point.
  1450. *
  1451. * @param p The point defining the top-left corner of the
  1452. * Accessible, given in the coordinate space of the object's
  1453. * parent.
  1454. * @return the Accessible, if it exists, at the specified
  1455. * location; else null
  1456. */
  1457. public Accessible getAccessibleAt(Point p) {
  1458. return SwingUtilities.getAccessibleAt(WindowPopup.this,p);
  1459. }
  1460. /**
  1461. * Returns whether this object can accept focus or not.
  1462. *
  1463. * @return true if object can accept focus; otherwise false
  1464. */
  1465. public boolean isFocusTraversable() {
  1466. return WindowPopup.this.isFocusTraversable();
  1467. }
  1468. /**
  1469. * Requests focus for this object.
  1470. */
  1471. public void requestFocus() {
  1472. WindowPopup.this.requestFocus();
  1473. }
  1474. /**
  1475. * Adds the specified focus listener to receive focus events from
  1476. * this component.
  1477. *
  1478. * @param l the focus listener
  1479. */
  1480. public void addFocusListener(FocusListener l) {
  1481. WindowPopup.this.addFocusListener(l);
  1482. }
  1483. /**
  1484. * Removes the specified focus listener so it no longer receives
  1485. * focus events from this component.
  1486. *
  1487. * @param l the focus listener
  1488. */
  1489. public void removeFocusListener(FocusListener l) {
  1490. WindowPopup.this.removeFocusListener(l);
  1491. }
  1492. } // inner class AccessibleWindowPopup
  1493. }
  1494. /**
  1495. * A class used to popup a JPanel.
  1496. * <p>
  1497. * <strong>Warning:</strong>
  1498. * Serialized objects of this class will not be compatible with
  1499. * future Swing releases. The current serialization support is appropriate
  1500. * for short term storage or RMI between applications running the same
  1501. * version of Swing. A future release of Swing will provide support for
  1502. * long term persistence.
  1503. */
  1504. class JPanelPopup extends JPanel implements Popup,Serializable {
  1505. int desiredLocationX,desiredLocationY;
  1506. public JPanelPopup() {
  1507. super();
  1508. setLayout(new BorderLayout());
  1509. setDoubleBuffered(true);
  1510. this.setOpaque(true);
  1511. }
  1512. public Component getComponent() {
  1513. return this;
  1514. }
  1515. public void addComponent(Component aComponent,Object constraints) {
  1516. this.add(aComponent,constraints);
  1517. }
  1518. public void removeComponent(Component c) {
  1519. this.remove(c);
  1520. }
  1521. public void update(Graphics g) {
  1522. paint(g);
  1523. }
  1524. public void pack() {
  1525. setSize(getPreferredSize());
  1526. }
  1527. public void show(Component invoker) {
  1528. Container parent = null;
  1529. if (invoker != null)
  1530. parent = invoker.getParent();
  1531. Window parentWindow = null;
  1532. for(Container p = parent; p != null; p = p.getParent()) {
  1533. if(p instanceof JRootPane) {
  1534. if(p.getParent() instanceof JInternalFrame)
  1535. continue;
  1536. parent = ((JRootPane)p).getLayeredPane();
  1537. for(p = parent.getParent(); p != null && (!(p instanceof java.awt.Window));
  1538. p = p.getParent());
  1539. parentWindow = (Window)p;
  1540. break;
  1541. } else if(p instanceof Window) {
  1542. parent = p;
  1543. parentWindow = (Window)p;
  1544. break;
  1545. }
  1546. }
  1547. Point p = convertScreenLocationToParent(parent,desiredLocationX,desiredLocationY);
  1548. this.setLocation(p.x,p.y);
  1549. if(parent instanceof JLayeredPane) {
  1550. ((JLayeredPane)parent).add(this,JLayeredPane.POPUP_LAYER,0);
  1551. } else
  1552. parent.add(this);
  1553. }
  1554. public void hide() {
  1555. Container parent = getParent();
  1556. Rectangle r = this.getBounds();
  1557. if(parent != null)
  1558. parent.remove(this);
  1559. parent.repaint(r.x,r.y,r.width,r.height);
  1560. }
  1561. public Rectangle getBoundsOnScreen() {
  1562. Container parent = getParent();
  1563. if(parent != null) {
  1564. Rectangle r = getBounds();
  1565. Point p;
  1566. p = convertParentLocationToScreen(parent,r.x,r.y);
  1567. r.x = p.x;
  1568. r.y = p.y;
  1569. return r;
  1570. } else
  1571. throw new Error("getBoundsOnScreen called on an invisible popup");
  1572. }
  1573. Point convertParentLocationToScreen(Container parent,int x,int y) {
  1574. Window parentWindow = null;
  1575. Rectangle r;
  1576. Container p;
  1577. Point pt;
  1578. for(p = this; p != null; p = p.getParent()) {
  1579. if(p instanceof Window) {
  1580. parentWindow = (Window)p;
  1581. break;
  1582. }
  1583. }
  1584. if(parentWindow != null) {
  1585. r = parentWindow.getBounds();
  1586. pt = new Point(x,y);
  1587. pt = SwingUtilities.convertPoint(parent,pt,null);
  1588. pt.x += r.x;
  1589. pt.y += r.y;
  1590. return pt;
  1591. } else
  1592. throw new Error("convertParentLocationToScreen: no window ancestor found"); }
  1593. Point convertScreenLocationToParent(Container parent,int x,int y) {
  1594. Window parentWindow = null;
  1595. Rectangle r;
  1596. for(Container p = parent; p != null; p = p.getParent()) {
  1597. if(p instanceof Window) {
  1598. parentWindow = (Window)p;
  1599. break;
  1600. }
  1601. }
  1602. if(parentWindow != null) {
  1603. Point p = new Point(x,y);
  1604. SwingUtilities.convertPointFromScreen(p,parent);
  1605. return p;
  1606. } else
  1607. throw new Error("convertScreenLocationToParent: no window ancestor found");
  1608. }
  1609. public void setLocationOnScreen(int x,int y) {
  1610. Container parent = getParent();
  1611. if(parent != null) {
  1612. Point p = convertScreenLocationToParent(parent,x,y);
  1613. this.setLocation(p.x,p.y);
  1614. } else {
  1615. desiredLocationX = x;
  1616. desiredLocationY = y;
  1617. }
  1618. }
  1619. }
  1620. /**
  1621. * A class used to popup an AWT panel.
  1622. * <p>
  1623. * <strong>Warning:</strong>
  1624. * Serialized objects of this class will not be compatible with
  1625. * future Swing releases. The current serialization support is appropriate
  1626. * for short term storage or RMI between applications running the same
  1627. * version of Swing. A future release of Swing will provide support for
  1628. * long term persistence.
  1629. */
  1630. class PanelPopup extends Panel implements Popup,Serializable {
  1631. int desiredLocationX,desiredLocationY;
  1632. JRootPane rootPane;
  1633. public PanelPopup() {
  1634. super();
  1635. setLayout(new BorderLayout());
  1636. rootPane = new JRootPane();
  1637. this.add(rootPane, BorderLayout.CENTER);
  1638. }
  1639. public int getWidth() {
  1640. return getBounds().width;
  1641. }
  1642. public int getHeight() {
  1643. return getBounds().height;
  1644. }
  1645. public Component getComponent() {
  1646. return this;
  1647. }
  1648. public void addComponent(Component aComponent,Object constraints) {
  1649. rootPane.getContentPane().add(aComponent,constraints);
  1650. }
  1651. public void removeComponent(Component c) {
  1652. rootPane.getContentPane().remove(c);
  1653. }
  1654. public void update(Graphics g) {
  1655. paint(g);
  1656. }
  1657. public void paint(Graphics g) {
  1658. super.paint(g);
  1659. }
  1660. public void pack() {
  1661. setSize(getPreferredSize());
  1662. }
  1663. public void show(Component invoker) {
  1664. Container parent = null;
  1665. if (invoker != null)
  1666. parent = invoker.getParent();
  1667. /*
  1668. Find the top level window,
  1669. if it has a layered pane,
  1670. add to that, otherwise
  1671. add to the window. */
  1672. while(!(parent instanceof Window || parent instanceof Applet) && (parent!=null)) {
  1673. parent = parent.getParent();
  1674. }
  1675. if (parent instanceof RootPaneContainer) {
  1676. parent = ((RootPaneContainer)parent).getLayeredPane();
  1677. Point p = convertScreenLocationToParent(parent,desiredLocationX,desiredLocationY);
  1678. this.setLocation(p.x,p.y);
  1679. ((JLayeredPane)parent).add(this,JLayeredPane.POPUP_LAYER,0);
  1680. } else {
  1681. Point p = convertScreenLocationToParent(parent,desiredLocationX,desiredLocationY);
  1682. this.setLocation(p.x,p.y);
  1683. parent.add(this);
  1684. }
  1685. }
  1686. public void hide() {
  1687. Container parent = getParent();
  1688. Rectangle r = this.getBounds();
  1689. if(parent != null)
  1690. parent.remove(this);
  1691. parent.repaint(r.x,r.y,r.width,r.height);
  1692. }
  1693. public Rectangle getBoundsOnScreen() {
  1694. Container parent = getParent();
  1695. if(parent != null) {
  1696. Rectangle r = getBounds();
  1697. Point p;
  1698. p = convertParentLocationToScreen(parent,r.x,r.y);
  1699. r.x = p.x;
  1700. r.y = p.y;
  1701. return r;
  1702. } else
  1703. throw new Error("getBoundsOnScreen called on an invisible popup");
  1704. }
  1705. Point convertParentLocationToScreen(Container parent,int x,int y) {
  1706. Window parentWindow = null;
  1707. Rectangle r;
  1708. Container p;
  1709. Point pt;
  1710. for(p = this; p != null; p = p.getParent()) {
  1711. if(p instanceof Window) {
  1712. parentWindow = (Window)p;
  1713. break;
  1714. }
  1715. }
  1716. if(parentWindow != null) {
  1717. r = parentWindow.getBounds();
  1718. pt = new Point(x,y);
  1719. pt = SwingUtilities.convertPoint(parent,pt,null);
  1720. pt.x += r.x;
  1721. pt.y += r.y;
  1722. return pt;
  1723. } else
  1724. throw new Error("convertParentLocationToScreen: no window ancestor found"); }
  1725. Point convertScreenLocationToParent(Container parent,int x,int y) {
  1726. Window parentWindow = null;
  1727. Rectangle r;
  1728. for(Container p = parent; p != null; p = p.getParent()) {
  1729. if(p instanceof Window) {
  1730. parentWindow = (Window)p;
  1731. break;
  1732. }
  1733. }
  1734. if(parentWindow != null) {
  1735. Point p = new Point(x,y);
  1736. SwingUtilities.convertPointFromScreen(p,parent);
  1737. return p;
  1738. } else
  1739. throw new Error("convertScreenLocationToParent: no window ancestor found");
  1740. }
  1741. public void setLocationOnScreen(int x,int y) {
  1742. Container parent = getParent();
  1743. if(parent != null) {
  1744. Point p = convertScreenLocationToParent(parent,x,y);
  1745. this.setLocation(p.x,p.y);
  1746. } else {
  1747. desiredLocationX = x;
  1748. desiredLocationY = y;
  1749. }
  1750. }
  1751. }
  1752. /**
  1753. * Returns a string representation of this JPopupMenu. This method
  1754. * is intended to be used only for debugging purposes, and the
  1755. * content and format of the returned string may vary between
  1756. * implementations. The returned string may be empty but may not
  1757. * be <code>null</code>.
  1758. *
  1759. * @return a string representation of this JPopupMenu.
  1760. */
  1761. protected String paramString() {
  1762. String labelString = (label != null ?
  1763. label : "");
  1764. String paintBorderString = (paintBorder ?
  1765. "true" : "false");
  1766. String marginString = (margin != null ?
  1767. margin.toString() : "");
  1768. String lastPopupTypeString;
  1769. if (lastPopupType == LIGHT_WEIGHT_POPUP) {
  1770. lastPopupTypeString = "LIGHT_WEIGHT_POPUP";
  1771. } else if (lastPopupType == MEDIUM_WEIGHT_POPUP) {
  1772. lastPopupTypeString = "MEDIUM_WEIGHT_POPUP";
  1773. } else if (lastPopupType == HEAVY_WEIGHT_POPUP) {
  1774. lastPopupTypeString = "HEAVY_WEIGHT_POPUP";
  1775. } else lastPopupTypeString = "";
  1776. String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
  1777. "true" : "false");
  1778. return super.paramString() +
  1779. ",desiredLocationX=" + desiredLocationX +
  1780. ",desiredLocationY=" + desiredLocationY +
  1781. ",label=" + labelString +
  1782. ",lastPopupType=" + lastPopupTypeString +
  1783. ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
  1784. ",margin=" + marginString +
  1785. ",paintBorder=" + paintBorderString;
  1786. }
  1787. /////////////////
  1788. // Accessibility support
  1789. ////////////////
  1790. /**
  1791. * Get the AccessibleContext associated with this JComponent
  1792. *
  1793. * @return the AccessibleContext of this JComponent
  1794. */
  1795. public AccessibleContext getAccessibleContext() {
  1796. if (accessibleContext == null) {
  1797. accessibleContext = new AccessibleJPopupMenu();
  1798. }
  1799. return accessibleContext;
  1800. }
  1801. protected class AccessibleJPopupMenu extends AccessibleJComponent {
  1802. /**
  1803. * Get the role of this object.
  1804. *
  1805. * @return an instance of AccessibleRole describing the role of
  1806. * the object
  1807. */
  1808. public AccessibleRole getAccessibleRole() {
  1809. return AccessibleRole.POPUP_MENU;
  1810. }
  1811. } // inner class AccessibleJPopupMenu
  1812. ////////////
  1813. // Serialization support.
  1814. ////////////
  1815. private void writeObject(ObjectOutputStream s) throws IOException {
  1816. Vector values = new Vector();
  1817. s.defaultWriteObject();
  1818. // Save the invoker, if its Serializable.
  1819. if(invoker != null && invoker instanceof Serializable) {
  1820. values.addElement("invoker");
  1821. values.addElement(invoker);
  1822. }
  1823. // Save the popup, if its Serializable.
  1824. if(popup != null && popup instanceof Serializable) {
  1825. values.addElement("popup");
  1826. values.addElement(popup);
  1827. }
  1828. // Save the frame, if its Serializable.
  1829. if(frame != null && frame instanceof Serializable) {
  1830. values.addElement("frame");
  1831. values.addElement(frame);
  1832. }
  1833. s.writeObject(values);
  1834. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  1835. ui.installUI(this);
  1836. }
  1837. }
  1838. // implements javax.swing.MenuElement
  1839. private void readObject(ObjectInputStream s)
  1840. throws IOException, ClassNotFoundException {
  1841. s.defaultReadObject();
  1842. Vector values = (Vector)s.readObject();
  1843. int indexCounter = 0;
  1844. int maxCounter = values.size();
  1845. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1846. equals("invoker")) {
  1847. invoker = (Component)values.elementAt(++indexCounter);
  1848. indexCounter++;
  1849. }
  1850. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1851. equals("popup")) {
  1852. popup = (Popup)values.elementAt(++indexCounter);
  1853. indexCounter++;
  1854. }
  1855. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1856. equals("frame")) {
  1857. frame = (Frame)values.elementAt(++indexCounter);
  1858. indexCounter++;
  1859. }
  1860. }
  1861. // implements javax.swing.MenuElement
  1862. public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {}
  1863. // implements javax.swing.MenuElement
  1864. public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
  1865. }
  1866. // implements javax.swing.MenuElement
  1867. public void menuSelectionChanged(boolean isIncluded) {
  1868. if(invoker instanceof JMenu) {
  1869. JMenu m = (JMenu) invoker;
  1870. if(isIncluded)
  1871. m.setPopupMenuVisible(true);
  1872. else
  1873. m.setPopupMenuVisible(false);
  1874. }
  1875. if (isPopupMenu() && !isIncluded)
  1876. setVisible(false);
  1877. }
  1878. // implements javax.swing.MenuElement
  1879. public MenuElement[] getSubElements() {
  1880. MenuElement result[];
  1881. Vector tmp = new Vector();
  1882. int c = getComponentCount();
  1883. int i;
  1884. Component m;
  1885. for(i=0 ; i < c ; i++) {
  1886. m = getComponent(i);
  1887. if(m instanceof MenuElement)
  1888. tmp.addElement(m);
  1889. }
  1890. result = new MenuElement[tmp.size()];
  1891. for(i=0,c=tmp.size() ; i < c ; i++)
  1892. result[i] = (MenuElement) tmp.elementAt(i);
  1893. return result;
  1894. }
  1895. public Component getComponent() {
  1896. return this;
  1897. }
  1898. /**
  1899. * A popupmenu-specific separator.
  1900. */
  1901. static public class Separator extends JSeparator
  1902. {
  1903. public Separator( )
  1904. {
  1905. super( JSeparator.HORIZONTAL );
  1906. }
  1907. /**
  1908. * Returns the name of the L&F class that renders this component.
  1909. *
  1910. * @return "PopupMenuSeparatorUI"
  1911. * @see JComponent#getUIClassID
  1912. * @see UIDefaults#getUI
  1913. */
  1914. public String getUIClassID()
  1915. {
  1916. return "PopupMenuSeparatorUI";
  1917. }
  1918. }
  1919. }