1624 lines · 55.4 KB · Java
RawDownload
/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing; import java.awt.AWTEvent;import java.awt.Component;import java.awt.ComponentOrientation;import java.awt.Container;import java.awt.Dimension;import java.awt.Frame;import java.awt.Graphics;import java.awt.GraphicsConfiguration;import java.awt.GraphicsDevice;import java.awt.GraphicsEnvironment;import java.awt.Insets;import java.awt.Point;import java.awt.Polygon;import java.awt.Rectangle;import java.awt.Toolkit;import java.awt.event.*;import java.beans.*; import java.util.*; import java.io.Serializable;import java.io.ObjectOutputStream;import java.io.ObjectInputStream;import java.io.IOException; import javax.swing.event.*;import javax.swing.plaf.*;import javax.swing.plaf.basic.*;import javax.accessibility.*; import java.lang.ref.WeakReference; /** * An implementation of a menu -- a popup window containing * <code>JMenuItem</code>s that * is displayed when the user selects an item on the <code>JMenuBar</code>. * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can * also contain <code>JSeparator</code>s. * <p> * In essence, a menu is a button with an associated <code>JPopupMenu</code>. * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the * "button" is on the <code>JMenuBar</code>, the menu is a top-level window. * If the "button" is another menu item, then the <code>JPopupMenu</code> is * "pull-right" menu. * <p> * Menus can be configured, and to some degree controlled, by * <code><a href="Action.html">Action</a></code>s.  Using an * <code>Action</code> with a menu has many benefits beyond directly * configuring a menu.  Refer to <a href="Action.html#buttonActions"> * Swing Components Supporting <code>Action</code></a> for more * details, and you can find more information in <a * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html">How * to Use Actions</a>, a section in <em>The Java Tutorial</em>. * <p> * For information and examples of using menus see * <a href="http://java.sun.com/doc/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>, * a section in <em>The Java Tutorial.</em> * <p> * <strong>Warning:</strong> Swing is not thread safe. For more * information see <a * href="package-summary.html#threading">Swing's Threading * Policy</a>. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing.  As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @beaninfo *   attribute: isContainer true * description: A popup window containing menu items displayed in a menu bar. * * @author Georges Saab * @author David Karlton * @author Arnaud Weber * @see JMenuItem * @see JSeparator * @see JMenuBar * @see JPopupMenu */public class JMenu extends JMenuItem implements Accessible,MenuElement{    /**     * @see #getUIClassID     * @see #readObject     */    private static final String uiClassID = "MenuUI";     /*     * The popup menu portion of the menu.     */    private JPopupMenu popupMenu;     /*     * The button's model listeners.  Default is <code>null</code>.     */    private ChangeListener menuChangeListener = null;     /*     * Only one <code>MenuEvent</code> is needed for each menu since the     * event's only state is the source property.  The source of events     * generated is always "this".  Default is <code>null</code>.     */    private MenuEvent menuEvent = null;     /* Registry of listeners created for <code>Action-JMenuItem</code>     * linkage.  This is needed so that references can     * be cleaned up at remove time to allow garbage collection     * Default is <code>null</code>.     */    private static Hashtable listenerRegistry = null;     /*     * Used by the look and feel (L&F) code to handle     * implementation specific menu behaviors.     */    private int delay;      /*      * Location of the popup component. Location is <code>null</code>      * if it was not customized by <code>setMenuLocation</code>      */     private Point customMenuLocation = null;     /* Diagnostic aids -- should be false for production builds. */    private static final boolean TRACE =   false; // trace creates and disposes    private static final boolean VERBOSE = false; // show reuse hits/misses    private static final boolean DEBUG =   false;  // show bad params, misc.     /**     * Constructs a new <code>JMenu</code> with no text.     */    public JMenu() {        this("");    }     /**     * Constructs a new <code>JMenu</code> with the supplied string     * as its text.     *     * @param s  the text for the menu label     */    public JMenu(String s) {        super(s);    }     /**     * Constructs a menu whose properties are taken from the     * <code>Action</code> supplied.     * @param a an <code>Action</code>     *     * @since 1.3     */    public JMenu(Action a) {        this();        setAction(a);    }     /**     * Constructs a new <code>JMenu</code> with the supplied string as     * its text and specified as a tear-off menu or not.     *     * @param s the text for the menu label     * @param b can the menu be torn off (not yet implemented)     */    public JMenu(String s, boolean b) {        this(s);    }      /**     * Overriden to do nothing. We want JMenu to be focusable, but     * <code>JMenuItem</code> doesn't want to be, thus we override this     * do nothing. We don't invoke <code>setFocusable(true)</code> after     * super's constructor has completed as this has the side effect that     * <code>JMenu</code> will be considered traversable via the     * keyboard, which we don't want. Making a Component traversable by     * the keyboard after invoking <code>setFocusable(true)</code> is OK,     * as <code>setFocusable</code> is new API     * and is speced as such, but internally we don't want to use it like     * this else we change the keyboard traversability.     */    void initFocusability() {    }     /**     * Resets the UI property with a value from the current look and feel.     *     * @see JComponent#updateUI     */    public void updateUI() {        setUI((MenuItemUI)UIManager.getUI(this));         if ( popupMenu != null )          {            popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));          }     }      /**     * Returns the name of the L&F class that renders this component.     *     * @return the string "MenuUI"     * @see JComponent#getUIClassID     * @see UIDefaults#getUI     */    public String getUIClassID() {        return uiClassID;    }     //    public void repaint(long tm, int x, int y, int width, int height) {    //        Thread.currentThread().dumpStack();    //        super.repaint(tm,x,y,width,height);    //    }     /**     * Sets the data model for the "menu button" -- the label     * that the user clicks to open or close the menu.     *     * @param newModel the <code>ButtonModel</code>     * @see #getModel     * @beaninfo     * description: The menu's model     *       bound: true     *      expert: true     *      hidden: true     */    public void setModel(ButtonModel newModel) {        ButtonModel oldModel = getModel();         super.setModel(newModel);         if (oldModel != null && menuChangeListener != null) {            oldModel.removeChangeListener(menuChangeListener);            menuChangeListener = null;        }         model = newModel;         if (newModel != null) {            menuChangeListener = createMenuChangeListener();            newModel.addChangeListener(menuChangeListener);        }    }     /**     * Returns true if the menu is currently selected (highlighted).     *     * @return true if the menu is selected, else false     */    public boolean isSelected() {        return getModel().isSelected();    }     /**     * Sets the selection status of the menu.     *     * @param b  true to select (highlight) the menu; false to de-select     *          the menu     * @beaninfo     *      description: When the menu is selected, its popup child is shown.     *           expert: true     *           hidden: true     */    public void setSelected(boolean b) {        ButtonModel model = getModel();        boolean oldValue = model.isSelected();         // TIGER - 4840653        // Removed code which fired an AccessibleState.SELECTED        // PropertyChangeEvent since this resulted in two        // identical events being fired since        // AbstractButton.fireItemStateChanged also fires the        // same event. This caused screen readers to speak the        // name of the item twice.         if (b != model.isSelected()) {            getModel().setSelected(b);        }    }     /**     * Returns true if the menu's popup window is visible.     *     * @return true if the menu is visible, else false     */    public boolean isPopupMenuVisible() {        ensurePopupMenuCreated();        return popupMenu.isVisible();    }     /**     * Sets the visibility of the menu's popup.  If the menu is     * not enabled, this method will have no effect.     *     * @param b  a boolean value -- true to make the menu visible,     *           false to hide it     * @beaninfo     *      description: The popup menu's visibility     *           expert: true     *           hidden: true     */    public void setPopupMenuVisible(boolean b) {        if (DEBUG) {            System.out.println("in JMenu.setPopupMenuVisible " + b);            // Thread.dumpStack();        }         boolean isVisible = isPopupMenuVisible();        if (b != isVisible && (isEnabled() || !b)) {            ensurePopupMenuCreated();            if ((b==true) && isShowing()) {                // Set location of popupMenu (pulldown or pullright)                Point p = getCustomMenuLocation();                if (p == null) {                    p = getPopupMenuOrigin();                }                getPopupMenu().show(this, p.x, p.y);            } else {                getPopupMenu().setVisible(false);            }        }    }     /**     * Computes the origin for the <code>JMenu</code>'s popup menu.     * This method uses Look and Feel properties named     * <code>Menu.menuPopupOffsetX</code>,     * <code>Menu.menuPopupOffsetY</code>,     * <code>Menu.submenuPopupOffsetX</code>, and     * <code>Menu.submenuPopupOffsetY</code>     * to adjust the exact location of popup.     *     * @return a <code>Point</code> in the coordinate space of the     *          menu which should be used as the origin     *          of the <code>JMenu</code>'s popup menu     *     * @since 1.3     */    protected Point getPopupMenuOrigin() {        int x;        int y;        JPopupMenu pm = getPopupMenu();        // Figure out the sizes needed to caclulate the menu position        Dimension s = getSize();        Dimension pmSize = pm.getSize();        // For the first time the menu is popped up,        // the size has not yet been initiated        if (pmSize.width==0) {            pmSize = pm.getPreferredSize();        }        Point position = getLocationOnScreen();        Toolkit toolkit = Toolkit.getDefaultToolkit();        GraphicsConfiguration gc = getGraphicsConfiguration();        Rectangle screenBounds = new Rectangle(toolkit.getScreenSize());        GraphicsEnvironment ge =            GraphicsEnvironment.getLocalGraphicsEnvironment();        GraphicsDevice[] gd = ge.getScreenDevices();        for(int i = 0; i < gd.length; i++) {            if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {                GraphicsConfiguration dgc =                    gd[i].getDefaultConfiguration();                if(dgc.getBounds().contains(position)) {                    gc = dgc;                    break;                }            }        }          if (gc != null) {            screenBounds = gc.getBounds();            // take screen insets (e.g. taskbar) into account            Insets screenInsets = toolkit.getScreenInsets(gc);             screenBounds.width -=                        Math.abs(screenInsets.left + screenInsets.right);            screenBounds.height -=                        Math.abs(screenInsets.top + screenInsets.bottom);            position.x -= Math.abs(screenInsets.left);            position.y -= Math.abs(screenInsets.top);        }         Container parent = getParent();        if (parent instanceof JPopupMenu) {            // We are a submenu (pull-right)            int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");            int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");             if( SwingUtilities.isLeftToRight(this) ) {                // First determine x:                x = s.width + xOffset;   // Prefer placement to the right                if (position.x + x + pmSize.width >= screenBounds.width                                                     + screenBounds.x &&                    // popup doesn't fit - place it wherever there's more room                    screenBounds.width - s.width < 2*(position.x                                                    - screenBounds.x)) {                     x = 0 - xOffset - pmSize.width;                }            } else {                // First determine x:                x = 0 - xOffset - pmSize.width; // Prefer placement to the left                if (position.x + x < screenBounds.x &&                    // popup doesn't fit - place it wherever there's more room                    screenBounds.width - s.width > 2*(position.x -                                                    screenBounds.x)) {                     x = s.width + xOffset;                }            }            // Then the y:            y = yOffset;                     // Prefer dropping down            if (position.y + y + pmSize.height >= screenBounds.height                                                  + screenBounds.y &&                // popup doesn't fit - place it wherever there's more room                screenBounds.height - s.height < 2*(position.y                                                  - screenBounds.y)) {                 y = s.height - yOffset - pmSize.height;            }        } else {            // We are a toplevel menu (pull-down)            int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");            int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");             if( SwingUtilities.isLeftToRight(this) ) {                // First determine the x:                x = xOffset;                   // Extend to the right                if (position.x + x + pmSize.width >= screenBounds.width                                                     + screenBounds.x &&                    // popup doesn't fit - place it wherever there's more room                    screenBounds.width - s.width < 2*(position.x                                                    - screenBounds.x)) {                     x = s.width - xOffset - pmSize.width;                }            } else {                // First determine the x:                x = s.width - xOffset - pmSize.width; // Extend to the left                if (position.x + x < screenBounds.x &&                    // popup doesn't fit - place it wherever there's more room                    screenBounds.width - s.width > 2*(position.x                                                    - screenBounds.x)) {                     x = xOffset;                }            }            // Then the y:            y = s.height + yOffset;    // Prefer dropping down            if (position.y + y + pmSize.height >= screenBounds.height &&                // popup doesn't fit - place it wherever there's more room                screenBounds.height - s.height < 2*(position.y                                                  - screenBounds.y)) {                 y = 0 - yOffset - pmSize.height;   // Otherwise drop 'up'            }        }        return new Point(x,y);    }      /**     * Returns the suggested delay, in milliseconds, before submenus     * are popped up or down.     * Each look and feel (L&F) may determine its own policy for     * observing the <code>delay</code> property.

Lines 1–500 of 1624.