0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing;
0027
0028 import java.awt.*;
0029 import java.awt.event.*;
0030 import java.io.IOException;
0031 import java.io.ObjectInputStream;
0032 import java.io.ObjectOutputStream;
0033 import java.io.Serializable;
0034 import java.beans.*;
0035
0036 import java.util.Locale;
0037 import java.util.Vector;
0038 import java.util.Hashtable;
0039 import javax.accessibility.*;
0040 import javax.swing.plaf.PopupMenuUI;
0041 import javax.swing.plaf.ComponentUI;
0042 import javax.swing.plaf.basic.BasicComboPopup;
0043 import javax.swing.event.*;
0044
0045 import java.applet.Applet;
0046
0047 /**
0048 * An implementation of a popup menu -- a small window that pops up
0049 * and displays a series of choices. A <code>JPopupMenu</code> is used for the
0050 * menu that appears when the user selects an item on the menu bar.
0051 * It is also used for "pull-right" menu that appears when the
0052 * selects a menu item that activates it. Finally, a <code>JPopupMenu</code>
0053 * can also be used anywhere else you want a menu to appear. For
0054 * example, when the user right-clicks in a specified area.
0055 * <p>
0056 * For information and examples of using popup menus, see
0057 * <a
0058 href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
0059 * in <em>The Java Tutorial.</em>
0060 * <p>
0061 * <strong>Warning:</strong> Swing is not thread safe. For more
0062 * information see <a
0063 * href="package-summary.html#threading">Swing's Threading
0064 * Policy</a>.
0065 * <p>
0066 * <strong>Warning:</strong>
0067 * Serialized objects of this class will not be compatible with
0068 * future Swing releases. The current serialization support is
0069 * appropriate for short term storage or RMI between applications running
0070 * the same version of Swing. As of 1.4, support for long term storage
0071 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0072 * has been added to the <code>java.beans</code> package.
0073 * Please see {@link java.beans.XMLEncoder}.
0074 *
0075 * @beaninfo
0076 * attribute: isContainer false
0077 * description: A small window that pops up and displays a series of choices.
0078 *
0079 * @version 1.205 @(#)JPopupMenu.java 1.205
0080 * @author Georges Saab
0081 * @author David Karlton
0082 * @author Arnaud Weber
0083 */
0084 public class JPopupMenu extends JComponent implements Accessible,
0085 MenuElement {
0086
0087 /**
0088 * @see #getUIClassID
0089 * @see #readObject
0090 */
0091 private static final String uiClassID = "PopupMenuUI";
0092
0093 /**
0094 * Key used in AppContext to determine if light way popups are the default.
0095 */
0096 private static final Object defaultLWPopupEnabledKey = new StringBuffer(
0097 "JPopupMenu.defaultLWPopupEnabledKey");
0098
0099 /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */
0100 static boolean popupPostionFixDisabled = false;
0101
0102 static {
0103 popupPostionFixDisabled = java.security.AccessController
0104 .doPrivileged(
0105 new sun.security.action.GetPropertyAction(
0106 "javax.swing.adjustPopupLocationToFit",
0107 "")).equals("false");
0108
0109 }
0110
0111 transient Component invoker;
0112 transient Popup popup;
0113 transient Frame frame;
0114 private int desiredLocationX, desiredLocationY;
0115
0116 private String label = null;
0117 private boolean paintBorder = true;
0118 private Insets margin = null;
0119
0120 /**
0121 * Used to indicate if lightweight popups should be used.
0122 */
0123 private boolean lightWeightPopup = true;
0124
0125 /*
0126 * Model for the selected subcontrol.
0127 */
0128 private SingleSelectionModel selectionModel;
0129
0130 /* Lock object used in place of class object for synchronization.
0131 * (4187686)
0132 */
0133 private static final Object classLock = new Object();
0134
0135 /* diagnostic aids -- should be false for production builds. */
0136 private static final boolean TRACE = false; // trace creates and disposes
0137 private static final boolean VERBOSE = false; // show reuse hits/misses
0138 private static final boolean DEBUG = false; // show bad params, misc.
0139
0140 /**
0141 * Sets the default value of the <code>lightWeightPopupEnabled</code>
0142 * property.
0143 *
0144 * @param aFlag <code>true</code> if popups can be lightweight,
0145 * otherwise <code>false</code>
0146 * @see #getDefaultLightWeightPopupEnabled
0147 * @see #setLightWeightPopupEnabled
0148 */
0149 public static void setDefaultLightWeightPopupEnabled(boolean aFlag) {
0150 SwingUtilities.appContextPut(defaultLWPopupEnabledKey, Boolean
0151 .valueOf(aFlag));
0152 }
0153
0154 /**
0155 * Gets the <code>defaultLightWeightPopupEnabled</code> property,
0156 * which by default is <code>true</code>.
0157 *
0158 * @return the value of the <code>defaultLightWeightPopupEnabled</code>
0159 * property
0160 *
0161 * @see #setDefaultLightWeightPopupEnabled
0162 */
0163 public static boolean getDefaultLightWeightPopupEnabled() {
0164 Boolean b = (Boolean) SwingUtilities
0165 .appContextGet(defaultLWPopupEnabledKey);
0166 if (b == null) {
0167 SwingUtilities.appContextPut(defaultLWPopupEnabledKey,
0168 Boolean.TRUE);
0169 return true;
0170 }
0171 return b.booleanValue();
0172 }
0173
0174 /**
0175 * Constructs a <code>JPopupMenu</code> without an "invoker".
0176 */
0177 public JPopupMenu() {
0178 this (null);
0179 }
0180
0181 /**
0182 * Constructs a <code>JPopupMenu</code> with the specified title.
0183 *
0184 * @param label the string that a UI may use to display as a title
0185 * for the popup menu.
0186 */
0187 public JPopupMenu(String label) {
0188 this .label = label;
0189 lightWeightPopup = getDefaultLightWeightPopupEnabled();
0190 setSelectionModel(new DefaultSingleSelectionModel());
0191 enableEvents(AWTEvent.MOUSE_EVENT_MASK);
0192 setFocusTraversalKeysEnabled(false);
0193 updateUI();
0194 }
0195
0196 /**
0197 * Returns the look and feel (L&F) object that renders this component.
0198 *
0199 * @return the <code>PopupMenuUI</code> object that renders this component
0200 */
0201 public PopupMenuUI getUI() {
0202 return (PopupMenuUI) ui;
0203 }
0204
0205 /**
0206 * Sets the L&F object that renders this component.
0207 *
0208 * @param ui the new <code>PopupMenuUI</code> L&F object
0209 * @see UIDefaults#getUI
0210 * @beaninfo
0211 * bound: true
0212 * hidden: true
0213 * attribute: visualUpdate true
0214 * description: The UI object that implements the Component's LookAndFeel.
0215 */
0216 public void setUI(PopupMenuUI ui) {
0217 super .setUI(ui);
0218 }
0219
0220 /**
0221 * Resets the UI property to a value from the current look and feel.
0222 *
0223 * @see JComponent#updateUI
0224 */
0225 public void updateUI() {
0226 setUI((PopupMenuUI) UIManager.getUI(this ));
0227 }
0228
0229 /**
0230 * Returns the name of the L&F class that renders this component.
0231 *
0232 * @return the string "PopupMenuUI"
0233 * @see JComponent#getUIClassID
0234 * @see UIDefaults#getUI
0235 */
0236 public String getUIClassID() {
0237 return uiClassID;
0238 }
0239
0240 protected void processFocusEvent(FocusEvent evt) {
0241 super .processFocusEvent(evt);
0242 }
0243
0244 /**
0245 * Processes key stroke events such as mnemonics and accelerators.
0246 *
0247 * @param evt the key event to be processed
0248 */
0249 protected void processKeyEvent(KeyEvent evt) {
0250 MenuSelectionManager.defaultManager().processKeyEvent(evt);
0251 if (evt.isConsumed()) {
0252 return;
0253 }
0254 super .processKeyEvent(evt);
0255 }
0256
0257 /**
0258 * Returns the model object that handles single selections.
0259 *
0260 * @return the <code>selectionModel</code> property
0261 * @see SingleSelectionModel
0262 */
0263 public SingleSelectionModel getSelectionModel() {
0264 return selectionModel;
0265 }
0266
0267 /**
0268 * Sets the model object to handle single selections.
0269 *
0270 * @param model the new <code>SingleSelectionModel</code>
0271 * @see SingleSelectionModel
0272 * @beaninfo
0273 * description: The selection model for the popup menu
0274 * expert: true
0275 */
0276 public void setSelectionModel(SingleSelectionModel model) {
0277 selectionModel = model;
0278 }
0279
0280 /**
0281 * Appends the specified menu item to the end of this menu.
0282 *
0283 * @param menuItem the <code>JMenuItem</code> to add
0284 * @return the <code>JMenuItem</code> added
0285 */
0286 public JMenuItem add(JMenuItem menuItem) {
0287 super .add(menuItem);
0288 return menuItem;
0289 }
0290
0291 /**
0292 * Creates a new menu item with the specified text and appends
0293 * it to the end of this menu.
0294 *
0295 * @param s the string for the menu item to be added
0296 */
0297 public JMenuItem add(String s) {
0298 return add(new JMenuItem(s));
0299 }
0300
0301 /**
0302 * Appends a new menu item to the end of the menu which
0303 * dispatches the specified <code>Action</code> object.
0304 *
0305 * @param a the <code>Action</code> to add to the menu
0306 * @return the new menu item
0307 * @see Action
0308 */
0309 public JMenuItem add(Action a) {
0310 JMenuItem mi = createActionComponent(a);
0311 mi.setAction(a);
0312 add(mi);
0313 return mi;
0314 }
0315
0316 /**
0317 * Returns an point which has been adjusted to take into account of the
0318 * desktop bounds, taskbar and multi-monitor configuration.
0319 * <p>
0320 * This adustment may be cancelled by invoking the application with
0321 * -Djavax.swing.adjustPopupLocationToFit=false
0322 */
0323 Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
0324 Point p = new Point(xposition, yposition);
0325
0326 if (popupPostionFixDisabled == true
0327 || GraphicsEnvironment.isHeadless())
0328 return p;
0329
0330 Toolkit toolkit = Toolkit.getDefaultToolkit();
0331 Rectangle screenBounds;
0332 Insets screenInsets;
0333 GraphicsConfiguration gc = null;
0334 // Try to find GraphicsConfiguration, that includes mouse
0335 // pointer position
0336 GraphicsEnvironment ge = GraphicsEnvironment
0337 .getLocalGraphicsEnvironment();
0338 GraphicsDevice[] gd = ge.getScreenDevices();
0339 for (int i = 0; i < gd.length; i++) {
0340 if (gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
0341 GraphicsConfiguration dgc = gd[i]
0342 .getDefaultConfiguration();
0343 if (dgc.getBounds().contains(p)) {
0344 gc = dgc;
0345 break;
0346 }
0347 }
0348 }
0349
0350 // If not found and we have invoker, ask invoker about his gc
0351 if (gc == null && getInvoker() != null) {
0352 gc = getInvoker().getGraphicsConfiguration();
0353 }
0354
0355 if (gc != null) {
0356 // If we have GraphicsConfiguration use it to get
0357 // screen bounds and insets
0358 screenInsets = toolkit.getScreenInsets(gc);
0359 screenBounds = gc.getBounds();
0360 } else {
0361 // If we don't have GraphicsConfiguration use primary screen
0362 // and empty insets
0363 screenInsets = new Insets(0, 0, 0, 0);
0364 screenBounds = new Rectangle(toolkit.getScreenSize());
0365 }
0366
0367 int scrWidth = screenBounds.width
0368 - Math.abs(screenInsets.left + screenInsets.right);
0369 int scrHeight = screenBounds.height
0370 - Math.abs(screenInsets.top + screenInsets.bottom);
0371
0372 Dimension size;
0373
0374 size = JPopupMenu.this .getPreferredSize();
0375
0376 // Use long variables to prevent overflow
0377 long pw = (long) p.x + (long) size.width;
0378 long ph = (long) p.y + (long) size.height;
0379
0380 if (pw > screenBounds.x + scrWidth)
0381 p.x = screenBounds.x + scrWidth - size.width;
0382
0383 if (ph > screenBounds.y + scrHeight)
0384 p.y = screenBounds.y + scrHeight - size.height;
0385
0386 /* Change is made to the desired (X,Y) values, when the
0387 PopupMenu is too tall OR too wide for the screen
0388 */
0389 if (p.x < screenBounds.x)
0390 p.x = screenBounds.x;
0391 if (p.y < screenBounds.y)
0392 p.y = screenBounds.y;
0393
0394 return p;
0395 }
0396
0397 /**
0398 * Factory method which creates the <code>JMenuItem</code> for
0399 * <code>Actions</code> added to the <code>JPopupMenu</code>.
0400 *
0401 * @param a the <code>Action</code> for the menu item to be added
0402 * @return the new menu item
0403 * @see Action
0404 *
0405 * @since 1.3
0406 */
0407 protected JMenuItem createActionComponent(Action a) {
0408 JMenuItem mi = new JMenuItem() {
0409 protected PropertyChangeListener createActionPropertyChangeListener(
0410 Action a) {
0411 PropertyChangeListener pcl = createActionChangeListener(this );
0412 if (pcl == null) {
0413 pcl = super .createActionPropertyChangeListener(a);
0414 }
0415 return pcl;
0416 }
0417 };
0418 mi.setHorizontalTextPosition(JButton.TRAILING);
0419 mi.setVerticalTextPosition(JButton.CENTER);
0420 return mi;
0421 }
0422
0423 /**
0424 * Returns a properly configured <code>PropertyChangeListener</code>
0425 * which updates the control as changes to the <code>Action</code> occur.
0426 */
0427 protected PropertyChangeListener createActionChangeListener(
0428 JMenuItem b) {
0429 return b.createActionPropertyChangeListener0(b.getAction());
0430 }
0431
0432 /**
0433 * Removes the component at the specified index from this popup menu.
0434 *
0435 * @param pos the position of the item to be removed
0436 * @exception IllegalArgumentException if the value of
0437 * <code>pos</code> < 0, or if the value of
0438 * <code>pos</code> is greater than the
0439 * number of items
0440 */
0441 public void remove(int pos) {
0442 if (pos < 0) {
0443 throw new IllegalArgumentException("index less than zero.");
0444 }
0445 if (pos > getComponentCount() - 1) {
0446 throw new IllegalArgumentException(
0447 "index greater than the number of items.");
0448 }
0449 super .remove(pos);
0450 }
0451
0452 /**
0453 * Sets the value of the <code>lightWeightPopupEnabled</code> property,
0454 * which by default is <code>true</code>.
0455 * By default, when a look and feel displays a popup,
0456 * it can choose to
0457 * use a lightweight (all-Java) popup.
0458 * Lightweight popup windows are more efficient than heavyweight
0459 * (native peer) windows,
0460 * but lightweight and heavyweight components do not mix well in a GUI.
0461 * If your application mixes lightweight and heavyweight components,
0462 * you should disable lightweight popups.
0463 * Some look and feels might always use heavyweight popups,
0464 * no matter what the value of this property.
0465 *
0466 * @param aFlag <code>false</code> to disable lightweight popups
0467 * @beaninfo
0468 * description: Determines whether lightweight popups are used when possible
0469 * expert: true
0470 *
0471 * @see #isLightWeightPopupEnabled
0472 */
0473 public void setLightWeightPopupEnabled(boolean aFlag) {
0474 // NOTE: this use to set the flag on a shared JPopupMenu, which meant
0475 // this effected ALL JPopupMenus.
0476 lightWeightPopup = aFlag;
0477 }
0478
0479 /**
0480 * Gets the <code>lightWeightPopupEnabled</code> property.
0481 *
0482 * @return the value of the <code>lightWeightPopupEnabled</code> property
0483 * @see #setLightWeightPopupEnabled
0484 */
0485 public boolean isLightWeightPopupEnabled() {
0486 return lightWeightPopup;
0487 }
0488
0489 /**
0490 * Returns the popup menu's label
0491 *
0492 * @return a string containing the popup menu's label
0493 * @see #setLabel
0494 */
0495 public String getLabel() {
0496 return label;
0497 }
0498
0499 /**
0500 * Sets the popup menu's label. Different look and feels may choose
0501 * to display or not display this.
0502 *
0503 * @param label a string specifying the label for the popup menu
0504 *
0505 * @see #setLabel
0506 * @beaninfo
0507 * description: The label for the popup menu.
0508 * bound: true
0509 */
0510 public void setLabel(String label) {
0511 String oldValue = this .label;
0512 this .label = label;
0513 firePropertyChange("label", oldValue, label);
0514 if (accessibleContext != null) {
0515 accessibleContext.firePropertyChange(
0516 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
0517 oldValue, label);
0518 }
0519 invalidate();
0520 repaint();
0521 }
0522
0523 /**
0524 * Appends a new separator at the end of the menu.
0525 */
0526 public void addSeparator() {
0527 add(new JPopupMenu.Separator());
0528 }
0529
0530 /**
0531 * Inserts a menu item for the specified <code>Action</code> object at
0532 * a given position.
0533 *
0534 * @param a the <code>Action</code> object to insert
0535 * @param index specifies the position at which to insert the
0536 * <code>Action</code>, where 0 is the first
0537 * @exception IllegalArgumentException if <code>index</code> < 0
0538 * @see Action
0539 */
0540 public void insert(Action a, int index) {
0541 JMenuItem mi = createActionComponent(a);
0542 mi.setAction(a);
0543 insert(mi, index);
0544 }
0545
0546 /**
0547 * Inserts the specified component into the menu at a given
0548 * position.
0549 *
0550 * @param component the <code>Component</code> to insert
0551 * @param index specifies the position at which
0552 * to insert the component, where 0 is the first
0553 * @exception IllegalArgumentException if <code>index</code> < 0
0554 */
0555 public void insert(Component component, int index) {
0556 if (index < 0) {
0557 throw new IllegalArgumentException("index less than zero.");
0558 }
0559
0560 int nitems = getComponentCount();
0561 // PENDING(ges): Why not use an array?
0562 Vector tempItems = new Vector();
0563
0564 /* Remove the item at index, nitems-index times
0565 storing them in a temporary vector in the
0566 order they appear on the menu.
0567 */
0568 for (int i = index; i < nitems; i++) {
0569 tempItems.addElement(getComponent(index));
0570 remove(index);
0571 }
0572
0573 add(component);
0574
0575 /* Add the removed items back to the menu, they are
0576 already in the correct order in the temp vector.
0577 */
0578 for (int i = 0; i < tempItems.size(); i++) {
0579 add((Component) tempItems.elementAt(i));
0580 }
0581 }
0582
0583 /**
0584 * Adds a <code>PopupMenu</code> listener.
0585 *
0586 * @param l the <code>PopupMenuListener</code> to add
0587 */
0588 public void addPopupMenuListener(PopupMenuListener l) {
0589 listenerList.add(PopupMenuListener.class, l);
0590 }
0591
0592 /**
0593 * Removes a <code>PopupMenu</code> listener.
0594 *
0595 * @param l the <code>PopupMenuListener</code> to remove
0596 */
0597 public void removePopupMenuListener(PopupMenuListener l) {
0598 listenerList.remove(PopupMenuListener.class, l);
0599 }
0600
0601 /**
0602 * Returns an array of all the <code>PopupMenuListener</code>s added
0603 * to this JMenuItem with addPopupMenuListener().
0604 *
0605 * @return all of the <code>PopupMenuListener</code>s added or an empty
0606 * array if no listeners have been added
0607 * @since 1.4
0608 */
0609 public PopupMenuListener[] getPopupMenuListeners() {
0610 return (PopupMenuListener[]) listenerList
0611 .getListeners(PopupMenuListener.class);
0612 }
0613
0614 /**
0615 * Adds a <code>MenuKeyListener</code> to the popup menu.
0616 *
0617 * @param l the <code>MenuKeyListener</code> to be added
0618 * @since 1.5
0619 */
0620 public void addMenuKeyListener(MenuKeyListener l) {
0621 listenerList.add(MenuKeyListener.class, l);
0622 }
0623
0624 /**
0625 * Removes a <code>MenuKeyListener</code> from the popup menu.
0626 *
0627 * @param l the <code>MenuKeyListener</code> to be removed
0628 * @since 1.5
0629 */
0630 public void removeMenuKeyListener(MenuKeyListener l) {
0631 listenerList.remove(MenuKeyListener.class, l);
0632 }
0633
0634 /**
0635 * Returns an array of all the <code>MenuKeyListener</code>s added
0636 * to this JPopupMenu with addMenuKeyListener().
0637 *
0638 * @return all of the <code>MenuKeyListener</code>s added or an empty
0639 * array if no listeners have been added
0640 * @since 1.5
0641 */
0642 public MenuKeyListener[] getMenuKeyListeners() {
0643 return (MenuKeyListener[]) listenerList
0644 .getListeners(MenuKeyListener.class);
0645 }
0646
0647 /**
0648 * Notifies <code>PopupMenuListener</code>s that this popup menu will
0649 * become visible.
0650 */
0651 protected void firePopupMenuWillBecomeVisible() {
0652 Object[] listeners = listenerList.getListenerList();
0653 PopupMenuEvent e = null;
0654 for (int i = listeners.length - 2; i >= 0; i -= 2) {
0655 if (listeners[i] == PopupMenuListener.class) {
0656 if (e == null)
0657 e = new PopupMenuEvent(this );
0658 ((PopupMenuListener) listeners[i + 1])
0659 .popupMenuWillBecomeVisible(e);
0660 }
0661 }
0662 }
0663
0664 /**
0665 * Notifies <code>PopupMenuListener</code>s that this popup menu will
0666 * become invisible.
0667 */
0668 protected void firePopupMenuWillBecomeInvisible() {
0669 Object[] listeners = listenerList.getListenerList();
0670 PopupMenuEvent e = null;
0671 for (int i = listeners.length - 2; i >= 0; i -= 2) {
0672 if (listeners[i] == PopupMenuListener.class) {
0673 if (e == null)
0674 e = new PopupMenuEvent(this );
0675 ((PopupMenuListener) listeners[i + 1])
0676 .popupMenuWillBecomeInvisible(e);
0677 }
0678 }
0679 }
0680
0681 /**
0682 * Notifies <code>PopupMenuListeners</code> that this popup menu is
0683 * cancelled.
0684 */
0685 protected void firePopupMenuCanceled() {
0686 Object[] listeners = listenerList.getListenerList();
0687 PopupMenuEvent e = null;
0688 for (int i = listeners.length - 2; i >= 0; i -= 2) {
0689 if (listeners[i] == PopupMenuListener.class) {
0690 if (e == null)
0691 e = new PopupMenuEvent(this );
0692 ((PopupMenuListener) listeners[i + 1])
0693 .popupMenuCanceled(e);
0694 }
0695 }
0696 }
0697
0698 /**
0699 * Always returns true since popups, by definition, should always
0700 * be on top of all other windows.
0701 * @return true
0702 */
0703 // package private
0704 boolean alwaysOnTop() {
0705 return true;
0706 }
0707
0708 /**
0709 * Lays out the container so that it uses the minimum space
0710 * needed to display its contents.
0711 */
0712 public void pack() {
0713 if (popup != null) {
0714 Dimension pref = getPreferredSize();
0715
0716 if (pref == null || pref.width != getWidth()
0717 || pref.height != getHeight()) {
0718 popup = getPopup();
0719 } else {
0720 validate();
0721 }
0722 }
0723 }
0724
0725 /**
0726 * Sets the visibility of the popup menu.
0727 *
0728 * @param b true to make the popup visible, or false to
0729 * hide it
0730 * @beaninfo
0731 * bound: true
0732 * description: Makes the popup visible
0733 */
0734 public void setVisible(boolean b) {
0735 if (DEBUG) {
0736 System.out.println("JPopupMenu.setVisible " + b);
0737 }
0738
0739 // Is it a no-op?
0740 if (b == isVisible())
0741 return;
0742
0743 // if closing, first close all Submenus
0744 if (b == false) {
0745
0746 // 4234793: This is a workaround because JPopupMenu.firePopupMenuCanceled is
0747 // a protected method and cannot be called from BasicPopupMenuUI directly
0748 // The real solution could be to make
0749 // firePopupMenuCanceled public and call it directly.
0750 Boolean doCanceled = (Boolean) getClientProperty("JPopupMenu.firePopupMenuCanceled");
0751 if (doCanceled != null && doCanceled == Boolean.TRUE) {
0752 putClientProperty("JPopupMenu.firePopupMenuCanceled",
0753 Boolean.FALSE);
0754 firePopupMenuCanceled();
0755 }
0756 getSelectionModel().clearSelection();
0757
0758 } else {
0759 // This is a popup menu with MenuElement children,
0760 // set selection path before popping up!
0761 if (isPopupMenu()) {
0762 if (getSubElements().length > 0) {
0763 MenuElement me[] = new MenuElement[2];
0764 me[0] = (MenuElement) this ;
0765 me[1] = getSubElements()[0];
0766 MenuSelectionManager.defaultManager()
0767 .setSelectedPath(me);
0768 } else {
0769 MenuElement me[] = new MenuElement[1];
0770 me[0] = (MenuElement) this ;
0771 MenuSelectionManager.defaultManager()
0772 .setSelectedPath(me);
0773 }
0774 }
0775 }
0776
0777 if (b) {
0778 firePopupMenuWillBecomeVisible();
0779 popup = getPopup();
0780 firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
0781
0782 } else if (popup != null) {
0783 firePopupMenuWillBecomeInvisible();
0784 popup.hide();
0785 popup = null;
0786 firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
0787 // 4694797: When popup menu is made invisible, selected path
0788 // should be cleared
0789 if (isPopupMenu()) {
0790 MenuSelectionManager.defaultManager()
0791 .clearSelectedPath();
0792 }
0793 }
0794 }
0795
0796 /**
0797 * Returns a <code>Popup</code> instance from the
0798 * <code>PopupMenuUI</code> that has had <code>show</code> invoked on
0799 * it. If the current <code>popup</code> is non-null,
0800 * this will invoke <code>dispose</code> of it, and then
0801 * <code>show</code> the new one.
0802 * <p>
0803 * This does NOT fire any events, it is up the caller to dispatch
0804 * the necessary events.
0805 */
0806 private Popup getPopup() {
0807 Popup oldPopup = popup;
0808
0809 if (oldPopup != null) {
0810 oldPopup.hide();
0811 }
0812 PopupFactory popupFactory = PopupFactory.getSharedInstance();
0813
0814 if (isLightWeightPopupEnabled()) {
0815 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
0816 } else {
0817 popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
0818 }
0819
0820 // adjust the location of the popup
0821 Point p = adjustPopupLocationToFitScreen(desiredLocationX,
0822 desiredLocationY);
0823 desiredLocationX = p.x;
0824 desiredLocationY = p.y;
0825
0826 Popup newPopup = getUI().getPopup(this , desiredLocationX,
0827 desiredLocationY);
0828
0829 popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
0830 newPopup.show();
0831 return newPopup;
0832 }
0833
0834 /**
0835 * Returns true if the popup menu is visible (currently
0836 * being displayed).
0837 */
0838 public boolean isVisible() {
0839 if (popup != null)
0840 return true;
0841 else
0842 return false;
0843 }
0844
0845 /**
0846 * Sets the location of the upper left corner of the
0847 * popup menu using x, y coordinates.
0848 *
0849 * @param x the x coordinate of the popup's new position
0850 * in the screen's coordinate space
0851 * @param y the y coordinate of the popup's new position
0852 * in the screen's coordinate space
0853 * @beaninfo
0854 * description: The location of the popup menu.
0855 */
0856 public void setLocation(int x, int y) {
0857 int oldX = desiredLocationX;
0858 int oldY = desiredLocationY;
0859
0860 desiredLocationX = x;
0861 desiredLocationY = y;
0862 if (popup != null && (x != oldX || y != oldY)) {
0863 popup = getPopup();
0864 }
0865 }
0866
0867 /**
0868 * Returns true if the popup menu is a standalone popup menu
0869 * rather than the submenu of a <code>JMenu</code>.
0870 *
0871 * @return true if this menu is a standalone popup menu, otherwise false
0872 */
0873 private boolean isPopupMenu() {
0874 return ((invoker != null) && !(invoker instanceof JMenu));
0875 }
0876
0877 /**
0878 * Returns the component which is the 'invoker' of this
0879 * popup menu.
0880 *
0881 * @return the <code>Component</code> in which the popup menu is displayed
0882 */
0883 public Component getInvoker() {
0884 return this .invoker;
0885 }
0886
0887 /**
0888 * Sets the invoker of this popup menu -- the component in which
0889 * the popup menu menu is to be displayed.
0890 *
0891 * @param invoker the <code>Component</code> in which the popup
0892 * menu is displayed
0893 * @beaninfo
0894 * description: The invoking component for the popup menu
0895 * expert: true
0896 */
0897 public void setInvoker(Component invoker) {
0898 Component oldInvoker = this .invoker;
0899 this .invoker = invoker;
0900 if ((oldInvoker != this .invoker) && (ui != null)) {
0901 ui.uninstallUI(this );
0902 ui.installUI(this );
0903 }
0904 invalidate();
0905 }
0906
0907 /**
0908 * Displays the popup menu at the position x,y in the coordinate
0909 * space of the component invoker.
0910 *
0911 * @param invoker the component in whose space the popup menu is to appear
0912 * @param x the x coordinate in invoker's coordinate space at which
0913 * the popup menu is to be displayed
0914 * @param y the y coordinate in invoker's coordinate space at which
0915 * the popup menu is to be displayed
0916 */
0917 public void show(Component invoker, int x, int y) {
0918 if (DEBUG) {
0919 System.out.println("in JPopupMenu.show ");
0920 }
0921 setInvoker(invoker);
0922 Frame newFrame = getFrame(invoker);
0923 if (newFrame != frame) {
0924 // Use the invoker's frame so that events
0925 // are propagated properly
0926 if (newFrame != null) {
0927 this .frame = newFrame;
0928 if (popup != null) {
0929 setVisible(false);
0930 }
0931 }
0932 }
0933 Point invokerOrigin;
0934 if (invoker != null) {
0935 invokerOrigin = invoker.getLocationOnScreen();
0936
0937 // To avoid integer overflow
0938 long lx, ly;
0939 lx = ((long) invokerOrigin.x) + ((long) x);
0940 ly = ((long) invokerOrigin.y) + ((long) y);
0941 if (lx > Integer.MAX_VALUE)
0942 lx = Integer.MAX_VALUE;
0943 if (lx < Integer.MIN_VALUE)
0944 lx = Integer.MIN_VALUE;
0945 if (ly > Integer.MAX_VALUE)
0946 ly = Integer.MAX_VALUE;
0947 if (ly < Integer.MIN_VALUE)
0948 ly = Integer.MIN_VALUE;
0949
0950 setLocation((int) lx, (int) ly);
0951 } else {
0952 setLocation(x, y);
0953 }
0954 setVisible(true);
0955 }
0956
0957 /**
0958 * Returns the popup menu which is at the root of the menu system
0959 * for this popup menu.
0960 *
0961 * @return the topmost grandparent <code>JPopupMenu</code>
0962 */
0963 JPopupMenu getRootPopupMenu() {
0964 JPopupMenu mp = this ;
0965 while ((mp != null) && (mp.isPopupMenu() != true)
0966 && (mp.getInvoker() != null)
0967 && (mp.getInvoker().getParent() != null)
0968 && (mp.getInvoker().getParent() instanceof JPopupMenu)) {
0969 mp = (JPopupMenu) mp.getInvoker().getParent();
0970 }
0971 return mp;
0972 }
0973
0974 /**
0975 * Returns the component at the specified index.
0976 *
0977 * @param i the index of the component, where 0 is the first
0978 * @return the <code>Component</code> at that index
0979 * @deprecated replaced by {@link java.awt.Container#getComponent(int)}
0980 */
0981 @Deprecated
0982 public Component getComponentAtIndex(int i) {
0983 return getComponent(i);
0984 }
0985
0986 /**
0987 * Returns the index of the specified component.
0988 *
0989 * @param c the <code>Component</code> to find
0990 * @return the index of the component, where 0 is the first;
0991 * or -1 if the component is not found
0992 */
0993 public int getComponentIndex(Component c) {
0994 int ncomponents = this .getComponentCount();
0995 Component[] component = this .getComponents();
0996 for (int i = 0; i < ncomponents; i++) {
0997 Component comp = component[i];
0998 if (comp == c)
0999 return i;
1000 }
1001 return -1;
1002 }
1003
1004 /**
1005 * Sets the size of the Popup window using a <code>Dimension</code> object.
1006 * This is equivalent to <code>setPreferredSize(d)</code>.
1007 *
1008 * @param d the <code>Dimension</code> specifying the new size
1009 * of this component.
1010 * @beaninfo
1011 * description: The size of the popup menu
1012 */
1013 public void setPopupSize(Dimension d) {
1014 Dimension oldSize = getPreferredSize();
1015
1016 setPreferredSize(d);
1017 if (popup != null) {
1018 Dimension newSize = getPreferredSize();
1019
1020 if (!oldSize.equals(newSize)) {
1021 popup = getPopup();
1022 }
1023 }
1024 }
1025
1026 /**
1027 * Sets the size of the Popup window to the specified width and
1028 * height. This is equivalent to
1029 * <code>setPreferredSize(new Dimension(width, height))</code>.
1030 *
1031 * @param width the new width of the Popup in pixels
1032 * @param height the new height of the Popup in pixels
1033 * @beaninfo
1034 * description: The size of the popup menu
1035 */
1036 public void setPopupSize(int width, int height) {
1037 setPopupSize(new Dimension(width, height));
1038 }
1039
1040 /**
1041 * Sets the currently selected component, This will result
1042 * in a change to the selection model.
1043 *
1044 * @param sel the <code>Component</code> to select
1045 * @beaninfo
1046 * description: The selected component on the popup menu
1047 * expert: true
1048 * hidden: true
1049 */
1050 public void setSelected(Component sel) {
1051 SingleSelectionModel model = getSelectionModel();
1052 int index = getComponentIndex(sel);
1053 model.setSelectedIndex(index);
1054 }
1055
1056 /**
1057 * Checks whether the border should be painted.
1058 *
1059 * @return true if the border is painted, false otherwise
1060 * @see #setBorderPainted
1061 */
1062 public boolean isBorderPainted() {
1063 return paintBorder;
1064 }
1065
1066 /**
1067 * Sets whether the border should be painted.
1068 *
1069 * @param b if true, the border is painted.
1070 * @see #isBorderPainted
1071 * @beaninfo
1072 * description: Is the border of the popup menu painted
1073 */
1074 public void setBorderPainted(boolean b) {
1075 paintBorder = b;
1076 repaint();
1077 }
1078
1079 /**
1080 * Paints the popup menu's border if the <code>borderPainted</code>
1081 * property is <code>true</code>.
1082 * @param g the <code>Graphics</code> object
1083 *
1084 * @see JComponent#paint
1085 * @see JComponent#setBorder
1086 */
1087 protected void paintBorder(Graphics g) {
1088 if (isBorderPainted()) {
1089 super .paintBorder(g);
1090 }
1091 }
1092
1093 /**
1094 * Returns the margin, in pixels, between the popup menu's border and
1095 * its containees.
1096 *
1097 * @return an <code>Insets</code> object containing the margin values.
1098 */
1099 public Insets getMargin() {
1100 if (margin == null) {
1101 return new Insets(0, 0, 0, 0);
1102 } else {
1103 return margin;
1104 }
1105 }
1106
1107 /**
1108 * Examines the list of menu items to determine whether
1109 * <code>popup</code> is a popup menu.
1110 *
1111 * @param popup a <code>JPopupMenu</code>
1112 * @return true if <code>popup</code>
1113 */
1114 boolean isSubPopupMenu(JPopupMenu popup) {
1115 int ncomponents = this .getComponentCount();
1116 Component[] component = this .getComponents();
1117 for (int i = 0; i < ncomponents; i++) {
1118 Component comp = component[i];
1119 if (comp instanceof JMenu) {
1120 JMenu menu = (JMenu) comp;
1121 JPopupMenu subPopup = menu.getPopupMenu();
1122 if (subPopup == popup)
1123 return true;
1124 if (subPopup.isSubPopupMenu(popup))
1125 return true;
1126 }
1127 }
1128 return false;
1129 }
1130
1131 private static Frame getFrame(Component c) {
1132 Component w = c;
1133
1134 while (!(w instanceof Frame) && (w != null)) {
1135 w = w.getParent();
1136 }
1137 return (Frame) w;
1138 }
1139
1140 /**
1141 * Returns a string representation of this <code>JPopupMenu</code>.
1142 * This method
1143 * is intended to be used only for debugging purposes, and the
1144 * content and format of the returned string may vary between
1145 * implementations. The returned string may be empty but may not
1146 * be <code>null</code>.
1147 *
1148 * @return a string representation of this <code>JPopupMenu</code>.
1149 */
1150 protected String paramString() {
1151 String labelString = (label != null ? label : "");
1152 String paintBorderString = (paintBorder ? "true" : "false");
1153 String marginString = (margin != null ? margin.toString() : "");
1154 String lightWeightPopupEnabledString = (isLightWeightPopupEnabled() ? "true"
1155 : "false");
1156 return super .paramString() + ",desiredLocationX="
1157 + desiredLocationX + ",desiredLocationY="
1158 + desiredLocationY + ",label=" + labelString
1159 + ",lightWeightPopupEnabled="
1160 + lightWeightPopupEnabledString + ",margin="
1161 + marginString + ",paintBorder=" + paintBorderString;
1162 }
1163
1164 /////////////////
1165 // Accessibility support
1166 ////////////////
1167
1168 /**
1169 * Gets the AccessibleContext associated with this JPopupMenu.
1170 * For JPopupMenus, the AccessibleContext takes the form of an
1171 * AccessibleJPopupMenu.
1172 * A new AccessibleJPopupMenu instance is created if necessary.
1173 *
1174 * @return an AccessibleJPopupMenu that serves as the
1175 * AccessibleContext of this JPopupMenu
1176 */
1177 public AccessibleContext getAccessibleContext() {
1178 if (accessibleContext == null) {
1179 accessibleContext = new AccessibleJPopupMenu();
1180 }
1181 return accessibleContext;
1182 }
1183
1184 /**
1185 * This class implements accessibility support for the
1186 * <code>JPopupMenu</code> class. It provides an implementation of the
1187 * Java Accessibility API appropriate to popup menu user-interface
1188 * elements.
1189 */
1190 protected class AccessibleJPopupMenu extends AccessibleJComponent
1191 implements PropertyChangeListener {
1192
1193 /**
1194 * AccessibleJPopupMenu constructor
1195 *
1196 * @since 1.5
1197 */
1198 protected AccessibleJPopupMenu() {
1199 JPopupMenu.this .addPropertyChangeListener(this );
1200 }
1201
1202 /**
1203 * Get the role of this object.
1204 *
1205 * @return an instance of AccessibleRole describing the role of
1206 * the object
1207 */
1208 public AccessibleRole getAccessibleRole() {
1209 return AccessibleRole.POPUP_MENU;
1210 }
1211
1212 /**
1213 * This method gets called when a bound property is changed.
1214 * @param e A <code>PropertyChangeEvent</code> object describing
1215 * the event source and the property that has changed. Must not be null.
1216 *
1217 * @throws NullPointerException if the parameter is null.
1218 * @since 1.5
1219 */
1220 public void propertyChange(PropertyChangeEvent e) {
1221 String propertyName = e.getPropertyName();
1222 if (propertyName == "visible") {
1223 if (e.getOldValue() == Boolean.FALSE
1224 && e.getNewValue() == Boolean.TRUE) {
1225 handlePopupIsVisibleEvent(true);
1226
1227 } else if (e.getOldValue() == Boolean.TRUE
1228 && e.getNewValue() == Boolean.FALSE) {
1229 handlePopupIsVisibleEvent(false);
1230 }
1231 }
1232 }
1233
1234 /*
1235 * Handles popup "visible" PropertyChangeEvent
1236 */
1237 private void handlePopupIsVisibleEvent(boolean visible) {
1238 if (visible) {
1239 // notify listeners that the popup became visible
1240 firePropertyChange(ACCESSIBLE_STATE_PROPERTY, null,
1241 AccessibleState.VISIBLE);
1242 // notify listeners that a popup list item is selected
1243 fireActiveDescendant();
1244 } else {
1245 // notify listeners that the popup became hidden
1246 firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
1247 AccessibleState.VISIBLE, null);
1248 }
1249 }
1250
1251 /*
1252 * Fires AccessibleActiveDescendant PropertyChangeEvent to notify listeners
1253 * on the popup menu invoker that a popup list item has been selected
1254 */
1255 private void fireActiveDescendant() {
1256 if (JPopupMenu.this instanceof BasicComboPopup) {
1257 // get the popup list
1258 JList popupList = ((BasicComboPopup) JPopupMenu.this )
1259 .getList();
1260 if (popupList == null) {
1261 return;
1262 }
1263
1264 // get the first selected item
1265 AccessibleContext ac = popupList.getAccessibleContext();
1266 AccessibleSelection selection = ac
1267 .getAccessibleSelection();
1268 if (selection == null) {
1269 return;
1270 }
1271 Accessible a = selection.getAccessibleSelection(0);
1272 if (a == null) {
1273 return;
1274 }
1275 AccessibleContext selectedItem = a
1276 .getAccessibleContext();
1277
1278 // fire the event with the popup invoker as the source.
1279 if (selectedItem != null && invoker != null) {
1280 AccessibleContext invokerContext = invoker
1281 .getAccessibleContext();
1282 if (invokerContext != null) {
1283 // Check invokerContext because Component.getAccessibleContext
1284 // returns null. Classes that extend Component are responsible
1285 // for returning a non-null AccessibleContext.
1286 invokerContext.firePropertyChange(
1287 ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
1288 null, selectedItem);
1289 }
1290 }
1291 }
1292 }
1293 } // inner class AccessibleJPopupMenu
1294
1295 ////////////
1296 // Serialization support.
1297 ////////////
1298 private void writeObject(ObjectOutputStream s) throws IOException {
1299 Vector values = new Vector();
1300
1301 s.defaultWriteObject();
1302 // Save the invoker, if its Serializable.
1303 if (invoker != null && invoker instanceof Serializable) {
1304 values.addElement("invoker");
1305 values.addElement(invoker);
1306 }
1307 // Save the popup, if its Serializable.
1308 if (popup != null && popup instanceof Serializable) {
1309 values.addElement("popup");
1310 values.addElement(popup);
1311 }
1312 s.writeObject(values);
1313
1314 if (getUIClassID().equals(uiClassID)) {
1315 byte count = JComponent.getWriteObjCounter(this );
1316 JComponent.setWriteObjCounter(this , --count);
1317 if (count == 0 && ui != null) {
1318 ui.installUI(this );
1319 }
1320 }
1321 }
1322
1323 // implements javax.swing.MenuElement
1324 private void readObject(ObjectInputStream s) throws IOException,
1325 ClassNotFoundException {
1326 s.defaultReadObject();
1327
1328 Vector values = (Vector) s.readObject();
1329 int indexCounter = 0;
1330 int maxCounter = values.size();
1331
1332 if (indexCounter < maxCounter
1333 && values.elementAt(indexCounter).equals("invoker")) {
1334 invoker = (Component) values.elementAt(++indexCounter);
1335 indexCounter++;
1336 }
1337 if (indexCounter < maxCounter
1338 && values.elementAt(indexCounter).equals("popup")) {
1339 popup = (Popup) values.elementAt(++indexCounter);
1340 indexCounter++;
1341 }
1342 }
1343
1344 /**
1345 * This method is required to conform to the
1346 * <code>MenuElement</code> interface, but it not implemented.
1347 * @see MenuElement#processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)
1348 */
1349 public void processMouseEvent(MouseEvent event, MenuElement path[],
1350 MenuSelectionManager manager) {
1351 }
1352
1353 /**
1354 * Processes a key event forwarded from the
1355 * <code>MenuSelectionManager</code> and changes the menu selection,
1356 * if necessary, by using <code>MenuSelectionManager</code>'s API.
1357 * <p>
1358 * Note: you do not have to forward the event to sub-components.
1359 * This is done automatically by the <code>MenuSelectionManager</code>.
1360 *
1361 * @param e a <code>KeyEvent</code>
1362 * @param path the <code>MenuElement</code> path array
1363 * @param manager the <code>MenuSelectionManager</code>
1364 */
1365 public void processKeyEvent(KeyEvent e, MenuElement path[],
1366 MenuSelectionManager manager) {
1367 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(),
1368 e.getID(), e.getWhen(), e.getModifiers(), e
1369 .getKeyCode(), e.getKeyChar(), path, manager);
1370 processMenuKeyEvent(mke);
1371
1372 if (mke.isConsumed()) {
1373 e.consume();
1374 }
1375 }
1376
1377 /**
1378 * Handles a keystroke in a menu.
1379 *
1380 * @param e a <code>MenuKeyEvent</code> object
1381 * @since 1.5
1382 */
1383 private void processMenuKeyEvent(MenuKeyEvent e) {
1384 switch (e.getID()) {
1385 case KeyEvent.KEY_PRESSED:
1386 fireMenuKeyPressed(e);
1387 break;
1388 case KeyEvent.KEY_RELEASED:
1389 fireMenuKeyReleased(e);
1390 break;
1391 case KeyEvent.KEY_TYPED:
1392 fireMenuKeyTyped(e);
1393 break;
1394 default:
1395 break;
1396 }
1397 }
1398
1399 /**
1400 * Notifies all listeners that have registered interest for
1401 * notification on this event type.
1402 *
1403 * @param event a <code>MenuKeyEvent</code>
1404 * @see EventListenerList
1405 */
1406 private void fireMenuKeyPressed(MenuKeyEvent event) {
1407 Object[] listeners = listenerList.getListenerList();
1408 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1409 if (listeners[i] == MenuKeyListener.class) {
1410 ((MenuKeyListener) listeners[i + 1])
1411 .menuKeyPressed(event);
1412 }
1413 }
1414 }
1415
1416 /**
1417 * Notifies all listeners that have registered interest for
1418 * notification on this event type.
1419 *
1420 * @param event a <code>MenuKeyEvent</code>
1421 * @see EventListenerList
1422 */
1423 private void fireMenuKeyReleased(MenuKeyEvent event) {
1424 Object[] listeners = listenerList.getListenerList();
1425 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1426 if (listeners[i] == MenuKeyListener.class) {
1427 ((MenuKeyListener) listeners[i + 1])
1428 .menuKeyReleased(event);
1429 }
1430 }
1431 }
1432
1433 /**
1434 * Notifies all listeners that have registered interest for
1435 * notification on this event type.
1436 *
1437 * @param event a <code>MenuKeyEvent</code>
1438 * @see EventListenerList
1439 */
1440 private void fireMenuKeyTyped(MenuKeyEvent event) {
1441 Object[] listeners = listenerList.getListenerList();
1442 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1443 if (listeners[i] == MenuKeyListener.class) {
1444 ((MenuKeyListener) listeners[i + 1])
1445 .menuKeyTyped(event);
1446 }
1447 }
1448 }
1449
1450 /**
1451 * Messaged when the menubar selection changes to activate or
1452 * deactivate this menu. This implements the
1453 * <code>javax.swing.MenuElement</code> interface.
1454 * Overrides <code>MenuElement.menuSelectionChanged</code>.
1455 *
1456 * @param isIncluded true if this menu is active, false if
1457 * it is not
1458 * @see MenuElement#menuSelectionChanged(boolean)
1459 */
1460 public void menuSelectionChanged(boolean isIncluded) {
1461 if (DEBUG) {
1462 System.out.println("In JPopupMenu.menuSelectionChanged "
1463 + isIncluded);
1464 }
1465 if (invoker instanceof JMenu) {
1466 JMenu m = (JMenu) invoker;
1467 if (isIncluded)
1468 m.setPopupMenuVisible(true);
1469 else
1470 m.setPopupMenuVisible(false);
1471 }
1472 if (isPopupMenu() && !isIncluded)
1473 setVisible(false);
1474 }
1475
1476 /**
1477 * Returns an array of <code>MenuElement</code>s containing the submenu
1478 * for this menu component. It will only return items conforming to
1479 * the <code>JMenuElement</code> interface.
1480 * If popup menu is <code>null</code> returns
1481 * an empty array. This method is required to conform to the
1482 * <code>MenuElement</code> interface.
1483 *
1484 * @return an array of <code>MenuElement</code> objects
1485 * @see MenuElement#getSubElements
1486 */
1487 public MenuElement[] getSubElements() {
1488 MenuElement result[];
1489 Vector tmp = new Vector();
1490 int c = getComponentCount();
1491 int i;
1492 Component m;
1493
1494 for (i = 0; i < c; i++) {
1495 m = getComponent(i);
1496 if (m instanceof MenuElement)
1497 tmp.addElement(m);
1498 }
1499
1500 result = new MenuElement[tmp.size()];
1501 for (i = 0, c = tmp.size(); i < c; i++)
1502 result[i] = (MenuElement) tmp.elementAt(i);
1503 return result;
1504 }
1505
1506 /**
1507 * Returns this <code>JPopupMenu</code> component.
1508 * @return this <code>JPopupMenu</code> object
1509 * @see MenuElement#getComponent
1510 */
1511 public Component getComponent() {
1512 return this ;
1513 }
1514
1515 /**
1516 * A popup menu-specific separator.
1517 */
1518 static public class Separator extends JSeparator {
1519 public Separator() {
1520 super (JSeparator.HORIZONTAL);
1521 }
1522
1523 /**
1524 * Returns the name of the L&F class that renders this component.
1525 *
1526 * @return the string "PopupMenuSeparatorUI"
1527 * @see JComponent#getUIClassID
1528 * @see UIDefaults#getUI
1529 */
1530 public String getUIClassID() {
1531 return "PopupMenuSeparatorUI";
1532
1533 }
1534 }
1535
1536 /**
1537 * Returns true if the <code>MouseEvent</code> is considered a popup trigger
1538 * by the <code>JPopupMenu</code>'s currently installed UI.
1539 *
1540 * @return true if the mouse event is a popup trigger
1541 * @since 1.3
1542 */
1543 public boolean isPopupTrigger(MouseEvent e) {
1544 return getUI().isPopupTrigger(e);
1545 }
1546 }
|