957 lines · 34.4 KB · Java
RawDownload
/* * Copyright (c) 1999, 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.applet.Applet;import java.awt.*;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import static javax.swing.ClientPropertyKey.PopupFactory_FORCE_HEAVYWEIGHT_POPUP; /** * <code>PopupFactory</code>, as the name implies, is used to obtain * instances of <code>Popup</code>s. <code>Popup</code>s are used to * display a <code>Component</code> above all other <code>Component</code>s * in a particular containment hierarchy. The general contract is that * once you have obtained a <code>Popup</code> from a * <code>PopupFactory</code>, you must invoke <code>hide</code> on the * <code>Popup</code>. The typical usage is: * <pre> *   PopupFactory factory = PopupFactory.getSharedInstance(); *   Popup popup = factory.getPopup(owner, contents, x, y); *   popup.show(); *   ... *   popup.hide(); * </pre> * * @see Popup * * @since 1.4 */public class PopupFactory {    /**     * The shared instanceof <code>PopupFactory</code> is per     * <code>AppContext</code>. This is the key used in the     * <code>AppContext</code> to locate the <code>PopupFactory</code>.     */    private static final Object SharedInstanceKey =        new StringBuffer("PopupFactory.SharedInstanceKey");     /**     * Max number of items to store in any one particular cache.     */    private static final int MAX_CACHE_SIZE = 5;     /**     * Key used to indicate a light weight popup should be used.     */    static final int LIGHT_WEIGHT_POPUP   = 0;     /**     * Key used to indicate a medium weight Popup should be used.     */    static final int MEDIUM_WEIGHT_POPUP  = 1;     /*     * Key used to indicate a heavy weight Popup should be used.     */    static final int HEAVY_WEIGHT_POPUP   = 2;     /**     * Default type of Popup to create.     */    private int popupType = LIGHT_WEIGHT_POPUP;      /**     * Sets the <code>PopupFactory</code> that will be used to obtain     * <code>Popup</code>s.     * This will throw an <code>IllegalArgumentException</code> if     * <code>factory</code> is null.     *     * @param factory Shared PopupFactory     * @exception IllegalArgumentException if <code>factory</code> is null     * @see #getPopup     */    public static void setSharedInstance(PopupFactory factory) {        if (factory == null) {            throw new IllegalArgumentException("PopupFactory can not be null");        }        SwingUtilities.appContextPut(SharedInstanceKey, factory);    }     /**     * Returns the shared <code>PopupFactory</code> which can be used     * to obtain <code>Popup</code>s.     *     * @return Shared PopupFactory     */    public static PopupFactory getSharedInstance() {        PopupFactory factory = (PopupFactory)SwingUtilities.appContextGet(                         SharedInstanceKey);         if (factory == null) {            factory = new PopupFactory();            setSharedInstance(factory);        }        return factory;    }      /**     * Provides a hint as to the type of <code>Popup</code> that should     * be created.     */    void setPopupType(int type) {        popupType = type;    }     /**     * Returns the preferred type of Popup to create.     */    int getPopupType() {        return popupType;    }     /**     * Creates a <code>Popup</code> for the Component <code>owner</code>     * containing the Component <code>contents</code>. <code>owner</code>     * is used to determine which <code>Window</code> the new     * <code>Popup</code> will parent the <code>Component</code> the     * <code>Popup</code> creates to. A null <code>owner</code> implies there     * is no valid parent. <code>x</code> and     * <code>y</code> specify the preferred initial location to place     * the <code>Popup</code> at. Based on screen size, or other paramaters,     * the <code>Popup</code> may not display at <code>x</code> and     * <code>y</code>.     *     * @param owner    Component mouse coordinates are relative to, may be null     * @param contents Contents of the Popup     * @param x        Initial x screen coordinate     * @param y        Initial y screen coordinate     * @exception IllegalArgumentException if contents is null     * @return Popup containing Contents     */    public Popup getPopup(Component owner, Component contents,                          int x, int y) throws IllegalArgumentException {        if (contents == null) {            throw new IllegalArgumentException(                          "Popup.getPopup must be passed non-null contents");        }         int popupType = getPopupType(owner, contents, x, y);        Popup popup = getPopup(owner, contents, x, y, popupType);         if (popup == null) {            // Didn't fit, force to heavy.            popup = getPopup(owner, contents, x, y, HEAVY_WEIGHT_POPUP);        }        return popup;    }     /**     * Returns the popup type to use for the specified parameters.     */    private int getPopupType(Component owner, Component contents,                             int ownerX, int ownerY) {        int popupType = getPopupType();         if (owner == null || invokerInHeavyWeightPopup(owner)) {            popupType = HEAVY_WEIGHT_POPUP;        }        else if (popupType == LIGHT_WEIGHT_POPUP &&                 !(contents instanceof JToolTip) &&                 !(contents instanceof JPopupMenu)) {            popupType = MEDIUM_WEIGHT_POPUP;        }         // Check if the parent component is an option pane.  If so we need to        // force a heavy weight popup in order to have event dispatching work        // correctly.        Component c = owner;        while (c != null) {            if (c instanceof JComponent) {                if (((JComponent)c).getClientProperty(                            PopupFactory_FORCE_HEAVYWEIGHT_POPUP) == Boolean.TRUE) {                    popupType = HEAVY_WEIGHT_POPUP;                    break;                }            } else if (c instanceof Window) {                Window w = (Window) c;                if (!w.isOpaque() || w.getOpacity() < 1 || w.getShape() != null) {                    popupType = HEAVY_WEIGHT_POPUP;                    break;                }            }            c = c.getParent();        }         return popupType;    }     /**     * Obtains the appropriate <code>Popup</code> based on     * <code>popupType</code>.     */    private Popup getPopup(Component owner, Component contents,                           int ownerX, int ownerY, int popupType) {        if (GraphicsEnvironment.isHeadless()) {            return getHeadlessPopup(owner, contents, ownerX, ownerY);        }         switch(popupType) {        case LIGHT_WEIGHT_POPUP:            return getLightWeightPopup(owner, contents, ownerX, ownerY);        case MEDIUM_WEIGHT_POPUP:            return getMediumWeightPopup(owner, contents, ownerX, ownerY);        case HEAVY_WEIGHT_POPUP:            return getHeavyWeightPopup(owner, contents, ownerX, ownerY);        }        return null;    }     /**     * Creates a headless popup     */    private Popup getHeadlessPopup(Component owner, Component contents,                                   int ownerX, int ownerY) {        return HeadlessPopup.getHeadlessPopup(owner, contents, ownerX, ownerY);    }     /**     * Creates a light weight popup.     */    private Popup getLightWeightPopup(Component owner, Component contents,                                         int ownerX, int ownerY) {        return LightWeightPopup.getLightWeightPopup(owner, contents, ownerX,                                                    ownerY);    }     /**     * Creates a medium weight popup.     */    private Popup getMediumWeightPopup(Component owner, Component contents,                                          int ownerX, int ownerY) {        return MediumWeightPopup.getMediumWeightPopup(owner, contents,                                                      ownerX, ownerY);    }     /**     * Creates a heavy weight popup.     */    private Popup getHeavyWeightPopup(Component owner, Component contents,                                         int ownerX, int ownerY) {        if (GraphicsEnvironment.isHeadless()) {            return getMediumWeightPopup(owner, contents, ownerX, ownerY);        }        return HeavyWeightPopup.getHeavyWeightPopup(owner, contents, ownerX,                                                    ownerY);    }     /**     * Returns true if the Component <code>i</code> inside a heavy weight     * <code>Popup</code>.     */    private boolean invokerInHeavyWeightPopup(Component i) {        if (i != null) {            Container parent;            for(parent = i.getParent() ; parent != null ; parent =                    parent.getParent()) {                if (parent instanceof Popup.HeavyWeightWindow) {                    return true;                }            }        }        return false;    }      /**     * Popup implementation that uses a Window as the popup.     */    private static class HeavyWeightPopup extends Popup {        private static final Object heavyWeightPopupCacheKey =                 new StringBuffer("PopupFactory.heavyWeightPopupCache");         /**         * Returns either a new or recycled <code>Popup</code> containing         * the specified children.         */        static Popup getHeavyWeightPopup(Component owner, Component contents,                                         int ownerX, int ownerY) {            Window window = (owner != null) ? SwingUtilities.                              getWindowAncestor(owner) : null;            HeavyWeightPopup popup = null;             if (window != null) {                popup = getRecycledHeavyWeightPopup(window);            }             boolean focusPopup = false;            if(contents != null && contents.isFocusable()) {                if(contents instanceof JPopupMenu) {                    JPopupMenu jpm = (JPopupMenu) contents;                    Component popComps[] = jpm.getComponents();                    for (Component popComp : popComps) {                        if (!(popComp instanceof MenuElement) &&                                !(popComp instanceof JSeparator)) {                            focusPopup = true;                            break;                        }                    }                }            }             if (popup == null ||                ((JWindow) popup.getComponent())                 .getFocusableWindowState() != focusPopup) {                 if(popup != null) {                    // The recycled popup can't serve us well                    // dispose it and create new one                    popup._dispose();                }                 popup = new HeavyWeightPopup();            }             popup.reset(owner, contents, ownerX, ownerY);             if(focusPopup) {                JWindow wnd = (JWindow) popup.getComponent();                wnd.setFocusableWindowState(true);                // Set window name. We need this in BasicPopupMenuUI                // to identify focusable popup window.                wnd.setName("###focusableSwingPopup###");            }             return popup;        }         /**         * Returns a previously disposed heavy weight <code>Popup</code>         * associated with <code>window</code>. This will return null if         * there is no <code>HeavyWeightPopup</code> associated with         * <code>window</code>.         */        private static HeavyWeightPopup getRecycledHeavyWeightPopup(Window w) {            synchronized (HeavyWeightPopup.class) {                List<HeavyWeightPopup> cache;                Map<Window, List<HeavyWeightPopup>> heavyPopupCache = getHeavyWeightPopupCache();                 if (heavyPopupCache.containsKey(w)) {                    cache = heavyPopupCache.get(w);                } else {                    return null;                }                if (cache.size() > 0) {                    HeavyWeightPopup r = cache.get(0);                    cache.remove(0);                    return r;                }                return null;            }        }         /**         * Returns the cache to use for heavy weight popups. Maps from         * <code>Window</code> to a <code>List</code> of         * <code>HeavyWeightPopup</code>s.         */        private static Map<Window, List<HeavyWeightPopup>> getHeavyWeightPopupCache() {            synchronized (HeavyWeightPopup.class) {                Map<Window, List<HeavyWeightPopup>> cache = (Map<Window, List<HeavyWeightPopup>>)SwingUtilities.appContextGet(                                  heavyWeightPopupCacheKey);                 if (cache == null) {                    cache = new HashMap<Window, List<HeavyWeightPopup>>(2);                    SwingUtilities.appContextPut(heavyWeightPopupCacheKey,                                                 cache);                }                return cache;            }        }         /**         * Recycles the passed in <code>HeavyWeightPopup</code>.         */        private static void recycleHeavyWeightPopup(HeavyWeightPopup popup) {            synchronized (HeavyWeightPopup.class) {                List<HeavyWeightPopup> cache;                Window window = SwingUtilities.getWindowAncestor(                                     popup.getComponent());                Map<Window, List<HeavyWeightPopup>> heavyPopupCache = getHeavyWeightPopupCache();                 if (window instanceof Popup.DefaultFrame ||                                      !window.isVisible()) {                    // If the Window isn't visible, we don't cache it as we                    // likely won't ever get a windowClosed event to clean up.                    // We also don't cache DefaultFrames as this indicates                    // there wasn't a valid Window parent, and thus we don't                    // know when to clean up.                    popup._dispose();                    return;                } else if (heavyPopupCache.containsKey(window)) {                    cache = heavyPopupCache.get(window);                } else {                    cache = new ArrayList<HeavyWeightPopup>();                    heavyPopupCache.put(window, cache);                    // Clean up if the Window is closed                    final Window w = window;                     w.addWindowListener(new WindowAdapter() {                        public void windowClosed(WindowEvent e) {                            List<HeavyWeightPopup> popups;                             synchronized(HeavyWeightPopup.class) {                                Map<Window, List<HeavyWeightPopup>> heavyPopupCache2 =                                              getHeavyWeightPopupCache();                                 popups = heavyPopupCache2.remove(w);                            }                            if (popups != null) {                                for (int counter = popups.size() - 1;                                                   counter >= 0; counter--) {                                    popups.get(counter)._dispose();                                }                            }                        }                    });                }                 if(cache.size() < MAX_CACHE_SIZE) {                    cache.add(popup);                } else {                    popup._dispose();                }            }        }         //        // Popup methods        //        public void hide() {            super.hide();            recycleHeavyWeightPopup(this);        }         /**         * As we recycle the <code>Window</code>, we don't want to dispose it,         * thus this method does nothing, instead use <code>_dipose</code>         * which will handle the disposing.         */        void dispose() {        }         void _dispose() {            super.dispose();        }    }       /**     * ContainerPopup consolidates the common code used in the light/medium     * weight implementations of <code>Popup</code>.     */    private static class ContainerPopup extends Popup {        /** Component we are to be added to. */        Component owner;        /** Desired x location. */        int x;        /** Desired y location. */        int y;         public void hide() {            Component component = getComponent();             if (component != null) {                Container parent = component.getParent();                 if (parent != null) {                    Rectangle bounds = component.getBounds(); 

Lines 1–500 of 957.