0N/A/*
2362N/A * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.swing;
0N/A
0N/Aimport java.applet.Applet;
0N/Aimport java.awt.*;
0N/Aimport java.awt.event.WindowAdapter;
0N/Aimport java.awt.event.WindowEvent;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.List;
0N/Aimport java.util.Map;
0N/Aimport static javax.swing.ClientPropertyKey.PopupFactory_FORCE_HEAVYWEIGHT_POPUP;
0N/A
0N/A/**
0N/A * <code>PopupFactory</code>, as the name implies, is used to obtain
0N/A * instances of <code>Popup</code>s. <code>Popup</code>s are used to
0N/A * display a <code>Component</code> above all other <code>Component</code>s
0N/A * in a particular containment hierarchy. The general contract is that
0N/A * once you have obtained a <code>Popup</code> from a
0N/A * <code>PopupFactory</code>, you must invoke <code>hide</code> on the
0N/A * <code>Popup</code>. The typical usage is:
0N/A * <pre>
0N/A * PopupFactory factory = PopupFactory.getSharedInstance();
0N/A * Popup popup = factory.getPopup(owner, contents, x, y);
0N/A * popup.show();
0N/A * ...
0N/A * popup.hide();
0N/A * </pre>
0N/A *
0N/A * @see Popup
0N/A *
0N/A * @since 1.4
0N/A */
0N/Apublic class PopupFactory {
0N/A /**
0N/A * The shared instanceof <code>PopupFactory</code> is per
0N/A * <code>AppContext</code>. This is the key used in the
0N/A * <code>AppContext</code> to locate the <code>PopupFactory</code>.
0N/A */
0N/A private static final Object SharedInstanceKey =
0N/A new StringBuffer("PopupFactory.SharedInstanceKey");
0N/A
0N/A /**
0N/A * Max number of items to store in any one particular cache.
0N/A */
0N/A private static final int MAX_CACHE_SIZE = 5;
0N/A
0N/A /**
0N/A * Key used to indicate a light weight popup should be used.
0N/A */
0N/A static final int LIGHT_WEIGHT_POPUP = 0;
0N/A
0N/A /**
0N/A * Key used to indicate a medium weight Popup should be used.
0N/A */
0N/A static final int MEDIUM_WEIGHT_POPUP = 1;
0N/A
0N/A /*
0N/A * Key used to indicate a heavy weight Popup should be used.
0N/A */
0N/A static final int HEAVY_WEIGHT_POPUP = 2;
0N/A
0N/A /**
0N/A * Default type of Popup to create.
0N/A */
0N/A private int popupType = LIGHT_WEIGHT_POPUP;
0N/A
0N/A
0N/A /**
0N/A * Sets the <code>PopupFactory</code> that will be used to obtain
0N/A * <code>Popup</code>s.
0N/A * This will throw an <code>IllegalArgumentException</code> if
0N/A * <code>factory</code> is null.
0N/A *
0N/A * @param factory Shared PopupFactory
0N/A * @exception IllegalArgumentException if <code>factory</code> is null
0N/A * @see #getPopup
0N/A */
0N/A public static void setSharedInstance(PopupFactory factory) {
0N/A if (factory == null) {
0N/A throw new IllegalArgumentException("PopupFactory can not be null");
0N/A }
0N/A SwingUtilities.appContextPut(SharedInstanceKey, factory);
0N/A }
0N/A
0N/A /**
0N/A * Returns the shared <code>PopupFactory</code> which can be used
0N/A * to obtain <code>Popup</code>s.
0N/A *
0N/A * @return Shared PopupFactory
0N/A */
0N/A public static PopupFactory getSharedInstance() {
0N/A PopupFactory factory = (PopupFactory)SwingUtilities.appContextGet(
0N/A SharedInstanceKey);
0N/A
0N/A if (factory == null) {
0N/A factory = new PopupFactory();
0N/A setSharedInstance(factory);
0N/A }
0N/A return factory;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Provides a hint as to the type of <code>Popup</code> that should
0N/A * be created.
0N/A */
0N/A void setPopupType(int type) {
0N/A popupType = type;
0N/A }
0N/A
0N/A /**
0N/A * Returns the preferred type of Popup to create.
0N/A */
0N/A int getPopupType() {
0N/A return popupType;
0N/A }
0N/A
0N/A /**
0N/A * Creates a <code>Popup</code> for the Component <code>owner</code>
0N/A * containing the Component <code>contents</code>. <code>owner</code>
0N/A * is used to determine which <code>Window</code> the new
0N/A * <code>Popup</code> will parent the <code>Component</code> the
0N/A * <code>Popup</code> creates to. A null <code>owner</code> implies there
0N/A * is no valid parent. <code>x</code> and
0N/A * <code>y</code> specify the preferred initial location to place
0N/A * the <code>Popup</code> at. Based on screen size, or other paramaters,
0N/A * the <code>Popup</code> may not display at <code>x</code> and
0N/A * <code>y</code>.
0N/A *
0N/A * @param owner Component mouse coordinates are relative to, may be null
0N/A * @param contents Contents of the Popup
0N/A * @param x Initial x screen coordinate
0N/A * @param y Initial y screen coordinate
0N/A * @exception IllegalArgumentException if contents is null
0N/A * @return Popup containing Contents
0N/A */
0N/A public Popup getPopup(Component owner, Component contents,
0N/A int x, int y) throws IllegalArgumentException {
0N/A if (contents == null) {
0N/A throw new IllegalArgumentException(
0N/A "Popup.getPopup must be passed non-null contents");
0N/A }
0N/A
0N/A int popupType = getPopupType(owner, contents, x, y);
0N/A Popup popup = getPopup(owner, contents, x, y, popupType);
0N/A
0N/A if (popup == null) {
0N/A // Didn't fit, force to heavy.
0N/A popup = getPopup(owner, contents, x, y, HEAVY_WEIGHT_POPUP);
0N/A }
0N/A return popup;
0N/A }
0N/A
0N/A /**
0N/A * Returns the popup type to use for the specified parameters.
0N/A */
0N/A private int getPopupType(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A int popupType = getPopupType();
0N/A
0N/A if (owner == null || invokerInHeavyWeightPopup(owner)) {
0N/A popupType = HEAVY_WEIGHT_POPUP;
0N/A }
0N/A else if (popupType == LIGHT_WEIGHT_POPUP &&
0N/A !(contents instanceof JToolTip) &&
0N/A !(contents instanceof JPopupMenu)) {
0N/A popupType = MEDIUM_WEIGHT_POPUP;
0N/A }
0N/A
0N/A // Check if the parent component is an option pane. If so we need to
0N/A // force a heavy weight popup in order to have event dispatching work
0N/A // correctly.
0N/A Component c = owner;
0N/A while (c != null) {
0N/A if (c instanceof JComponent) {
0N/A if (((JComponent)c).getClientProperty(
0N/A PopupFactory_FORCE_HEAVYWEIGHT_POPUP) == Boolean.TRUE) {
0N/A popupType = HEAVY_WEIGHT_POPUP;
0N/A break;
0N/A }
0N/A }
0N/A c = c.getParent();
0N/A }
0N/A
0N/A return popupType;
0N/A }
0N/A
0N/A /**
0N/A * Obtains the appropriate <code>Popup</code> based on
0N/A * <code>popupType</code>.
0N/A */
0N/A private Popup getPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY, int popupType) {
0N/A if (GraphicsEnvironment.isHeadless()) {
0N/A return getHeadlessPopup(owner, contents, ownerX, ownerY);
0N/A }
0N/A
0N/A switch(popupType) {
0N/A case LIGHT_WEIGHT_POPUP:
0N/A return getLightWeightPopup(owner, contents, ownerX, ownerY);
0N/A case MEDIUM_WEIGHT_POPUP:
0N/A return getMediumWeightPopup(owner, contents, ownerX, ownerY);
0N/A case HEAVY_WEIGHT_POPUP:
0N/A return getHeavyWeightPopup(owner, contents, ownerX, ownerY);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Creates a headless popup
0N/A */
0N/A private Popup getHeadlessPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A return HeadlessPopup.getHeadlessPopup(owner, contents, ownerX, ownerY);
0N/A }
0N/A
0N/A /**
0N/A * Creates a light weight popup.
0N/A */
0N/A private Popup getLightWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A return LightWeightPopup.getLightWeightPopup(owner, contents, ownerX,
0N/A ownerY);
0N/A }
0N/A
0N/A /**
0N/A * Creates a medium weight popup.
0N/A */
0N/A private Popup getMediumWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A return MediumWeightPopup.getMediumWeightPopup(owner, contents,
0N/A ownerX, ownerY);
0N/A }
0N/A
0N/A /**
0N/A * Creates a heavy weight popup.
0N/A */
0N/A private Popup getHeavyWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A if (GraphicsEnvironment.isHeadless()) {
0N/A return getMediumWeightPopup(owner, contents, ownerX, ownerY);
0N/A }
0N/A return HeavyWeightPopup.getHeavyWeightPopup(owner, contents, ownerX,
0N/A ownerY);
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the Component <code>i</code> inside a heavy weight
0N/A * <code>Popup</code>.
0N/A */
0N/A private boolean invokerInHeavyWeightPopup(Component i) {
0N/A if (i != null) {
0N/A Container parent;
0N/A for(parent = i.getParent() ; parent != null ; parent =
0N/A parent.getParent()) {
0N/A if (parent instanceof Popup.HeavyWeightWindow) {
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Popup implementation that uses a Window as the popup.
0N/A */
0N/A private static class HeavyWeightPopup extends Popup {
0N/A private static final Object heavyWeightPopupCacheKey =
0N/A new StringBuffer("PopupFactory.heavyWeightPopupCache");
0N/A
0N/A /**
0N/A * Returns either a new or recycled <code>Popup</code> containing
0N/A * the specified children.
0N/A */
0N/A static Popup getHeavyWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A Window window = (owner != null) ? SwingUtilities.
0N/A getWindowAncestor(owner) : null;
0N/A HeavyWeightPopup popup = null;
0N/A
0N/A if (window != null) {
0N/A popup = getRecycledHeavyWeightPopup(window);
0N/A }
0N/A
0N/A boolean focusPopup = false;
0N/A if(contents != null && contents.isFocusable()) {
0N/A if(contents instanceof JPopupMenu) {
0N/A JPopupMenu jpm = (JPopupMenu) contents;
0N/A Component popComps[] = jpm.getComponents();
625N/A for (Component popComp : popComps) {
625N/A if (!(popComp instanceof MenuElement) &&
625N/A !(popComp instanceof JSeparator)) {
0N/A focusPopup = true;
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (popup == null ||
0N/A ((JWindow) popup.getComponent())
0N/A .getFocusableWindowState() != focusPopup) {
0N/A
0N/A if(popup != null) {
0N/A // The recycled popup can't serve us well
0N/A // dispose it and create new one
0N/A popup._dispose();
0N/A }
0N/A
0N/A popup = new HeavyWeightPopup();
0N/A }
0N/A
0N/A popup.reset(owner, contents, ownerX, ownerY);
0N/A
0N/A if(focusPopup) {
0N/A JWindow wnd = (JWindow) popup.getComponent();
0N/A wnd.setFocusableWindowState(true);
0N/A // Set window name. We need this in BasicPopupMenuUI
0N/A // to identify focusable popup window.
0N/A wnd.setName("###focusableSwingPopup###");
0N/A }
0N/A
0N/A return popup;
0N/A }
0N/A
0N/A /**
0N/A * Returns a previously disposed heavy weight <code>Popup</code>
0N/A * associated with <code>window</code>. This will return null if
0N/A * there is no <code>HeavyWeightPopup</code> associated with
0N/A * <code>window</code>.
0N/A */
0N/A private static HeavyWeightPopup getRecycledHeavyWeightPopup(Window w) {
0N/A synchronized (HeavyWeightPopup.class) {
625N/A List<HeavyWeightPopup> cache;
625N/A Map<Window, List<HeavyWeightPopup>> heavyPopupCache = getHeavyWeightPopupCache();
0N/A
0N/A if (heavyPopupCache.containsKey(w)) {
625N/A cache = heavyPopupCache.get(w);
0N/A } else {
0N/A return null;
0N/A }
625N/A if (cache.size() > 0) {
625N/A HeavyWeightPopup r = cache.get(0);
0N/A cache.remove(0);
0N/A return r;
0N/A }
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the cache to use for heavy weight popups. Maps from
0N/A * <code>Window</code> to a <code>List</code> of
0N/A * <code>HeavyWeightPopup</code>s.
0N/A */
625N/A private static Map<Window, List<HeavyWeightPopup>> getHeavyWeightPopupCache() {
0N/A synchronized (HeavyWeightPopup.class) {
625N/A Map<Window, List<HeavyWeightPopup>> cache = (Map<Window, List<HeavyWeightPopup>>)SwingUtilities.appContextGet(
0N/A heavyWeightPopupCacheKey);
0N/A
0N/A if (cache == null) {
625N/A cache = new HashMap<Window, List<HeavyWeightPopup>>(2);
0N/A SwingUtilities.appContextPut(heavyWeightPopupCacheKey,
0N/A cache);
0N/A }
0N/A return cache;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Recycles the passed in <code>HeavyWeightPopup</code>.
0N/A */
0N/A private static void recycleHeavyWeightPopup(HeavyWeightPopup popup) {
0N/A synchronized (HeavyWeightPopup.class) {
625N/A List<HeavyWeightPopup> cache;
625N/A Window window = SwingUtilities.getWindowAncestor(
0N/A popup.getComponent());
625N/A Map<Window, List<HeavyWeightPopup>> heavyPopupCache = getHeavyWeightPopupCache();
0N/A
0N/A if (window instanceof Popup.DefaultFrame ||
625N/A !window.isVisible()) {
0N/A // If the Window isn't visible, we don't cache it as we
0N/A // likely won't ever get a windowClosed event to clean up.
0N/A // We also don't cache DefaultFrames as this indicates
0N/A // there wasn't a valid Window parent, and thus we don't
0N/A // know when to clean up.
0N/A popup._dispose();
0N/A return;
0N/A } else if (heavyPopupCache.containsKey(window)) {
625N/A cache = heavyPopupCache.get(window);
0N/A } else {
625N/A cache = new ArrayList<HeavyWeightPopup>();
0N/A heavyPopupCache.put(window, cache);
0N/A // Clean up if the Window is closed
625N/A final Window w = window;
0N/A
0N/A w.addWindowListener(new WindowAdapter() {
0N/A public void windowClosed(WindowEvent e) {
625N/A List<HeavyWeightPopup> popups;
0N/A
0N/A synchronized(HeavyWeightPopup.class) {
625N/A Map<Window, List<HeavyWeightPopup>> heavyPopupCache2 =
0N/A getHeavyWeightPopupCache();
0N/A
625N/A popups = heavyPopupCache2.remove(w);
0N/A }
0N/A if (popups != null) {
0N/A for (int counter = popups.size() - 1;
0N/A counter >= 0; counter--) {
625N/A popups.get(counter)._dispose();
0N/A }
0N/A }
0N/A }
0N/A });
0N/A }
0N/A
0N/A if(cache.size() < MAX_CACHE_SIZE) {
0N/A cache.add(popup);
0N/A } else {
0N/A popup._dispose();
0N/A }
0N/A }
0N/A }
0N/A
0N/A //
0N/A // Popup methods
0N/A //
0N/A public void hide() {
0N/A super.hide();
0N/A recycleHeavyWeightPopup(this);
0N/A }
0N/A
0N/A /**
0N/A * As we recycle the <code>Window</code>, we don't want to dispose it,
0N/A * thus this method does nothing, instead use <code>_dipose</code>
0N/A * which will handle the disposing.
0N/A */
0N/A void dispose() {
0N/A }
0N/A
0N/A void _dispose() {
0N/A super.dispose();
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * ContainerPopup consolidates the common code used in the light/medium
0N/A * weight implementations of <code>Popup</code>.
0N/A */
0N/A private static class ContainerPopup extends Popup {
0N/A /** Component we are to be added to. */
0N/A Component owner;
0N/A /** Desired x location. */
0N/A int x;
0N/A /** Desired y location. */
0N/A int y;
0N/A
0N/A public void hide() {
0N/A Component component = getComponent();
0N/A
0N/A if (component != null) {
0N/A Container parent = component.getParent();
0N/A
0N/A if (parent != null) {
0N/A Rectangle bounds = component.getBounds();
0N/A
0N/A parent.remove(component);
0N/A parent.repaint(bounds.x, bounds.y, bounds.width,
0N/A bounds.height);
0N/A }
0N/A }
0N/A owner = null;
0N/A }
0N/A public void pack() {
0N/A Component component = getComponent();
0N/A
0N/A if (component != null) {
0N/A component.setSize(component.getPreferredSize());
0N/A }
0N/A }
0N/A
0N/A void reset(Component owner, Component contents, int ownerX,
0N/A int ownerY) {
0N/A if ((owner instanceof JFrame) || (owner instanceof JDialog) ||
0N/A (owner instanceof JWindow)) {
0N/A // Force the content to be added to the layered pane, otherwise
0N/A // we'll get an exception when adding to the RootPaneContainer.
0N/A owner = ((RootPaneContainer)owner).getLayeredPane();
0N/A }
0N/A super.reset(owner, contents, ownerX, ownerY);
0N/A
0N/A x = ownerX;
0N/A y = ownerY;
0N/A this.owner = owner;
0N/A }
0N/A
0N/A boolean overlappedByOwnedWindow() {
0N/A Component component = getComponent();
0N/A if(owner != null && component != null) {
0N/A Window w = SwingUtilities.getWindowAncestor(owner);
0N/A if (w == null) {
0N/A return false;
0N/A }
0N/A Window[] ownedWindows = w.getOwnedWindows();
0N/A if(ownedWindows != null) {
0N/A Rectangle bnd = component.getBounds();
625N/A for (Window window : ownedWindows) {
625N/A if (window.isVisible() &&
625N/A bnd.intersects(window.getBounds())) {
0N/A
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
341N/A * Returns true if popup can fit the screen and the owner's top parent.
341N/A * It determines can popup be lightweight or mediumweight.
0N/A */
0N/A boolean fitsOnScreen() {
341N/A boolean result = false;
0N/A Component component = getComponent();
0N/A if (owner != null && component != null) {
341N/A int popupWidth = component.getWidth();
341N/A int popupHeight = component.getHeight();
1090N/A
1090N/A Container parent = (Container) SwingUtilities.getRoot(owner);
341N/A if (parent instanceof JFrame ||
341N/A parent instanceof JDialog ||
341N/A parent instanceof JWindow) {
341N/A
1090N/A Rectangle parentBounds = parent.getBounds();
341N/A Insets i = parent.getInsets();
341N/A parentBounds.x += i.left;
341N/A parentBounds.y += i.top;
341N/A parentBounds.width -= i.left + i.right;
341N/A parentBounds.height -= i.top + i.bottom;
0N/A
341N/A if (JPopupMenu.canPopupOverlapTaskBar()) {
341N/A GraphicsConfiguration gc =
341N/A parent.getGraphicsConfiguration();
0N/A Rectangle popupArea = getContainerPopupArea(gc);
341N/A result = parentBounds.intersection(popupArea)
341N/A .contains(x, y, popupWidth, popupHeight);
341N/A } else {
341N/A result = parentBounds
341N/A .contains(x, y, popupWidth, popupHeight);
0N/A }
341N/A } else if (parent instanceof JApplet) {
1090N/A Rectangle parentBounds = parent.getBounds();
341N/A Point p = parent.getLocationOnScreen();
341N/A parentBounds.x = p.x;
341N/A parentBounds.y = p.y;
1090N/A result = parentBounds.contains(x, y, popupWidth, popupHeight);
0N/A }
0N/A }
341N/A return result;
0N/A }
0N/A
0N/A Rectangle getContainerPopupArea(GraphicsConfiguration gc) {
0N/A Rectangle screenBounds;
0N/A Toolkit toolkit = Toolkit.getDefaultToolkit();
0N/A Insets insets;
0N/A if(gc != null) {
0N/A // If we have GraphicsConfiguration use it
0N/A // to get screen bounds
0N/A screenBounds = gc.getBounds();
0N/A insets = toolkit.getScreenInsets(gc);
0N/A } else {
0N/A // If we don't have GraphicsConfiguration use primary screen
0N/A screenBounds = new Rectangle(toolkit.getScreenSize());
0N/A insets = new Insets(0, 0, 0, 0);
0N/A }
0N/A // Take insets into account
0N/A screenBounds.x += insets.left;
0N/A screenBounds.y += insets.top;
0N/A screenBounds.width -= (insets.left + insets.right);
0N/A screenBounds.height -= (insets.top + insets.bottom);
0N/A return screenBounds;
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Popup implementation that is used in headless environment.
0N/A */
0N/A private static class HeadlessPopup extends ContainerPopup {
0N/A static Popup getHeadlessPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A HeadlessPopup popup = new HeadlessPopup();
0N/A popup.reset(owner, contents, ownerX, ownerY);
0N/A return popup;
0N/A }
0N/A
0N/A Component createComponent(Component owner) {
0N/A return new Panel(new BorderLayout());
0N/A }
0N/A
0N/A public void show() {
0N/A }
0N/A public void hide() {
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Popup implementation that uses a JPanel as the popup.
0N/A */
0N/A private static class LightWeightPopup extends ContainerPopup {
0N/A private static final Object lightWeightPopupCacheKey =
0N/A new StringBuffer("PopupFactory.lightPopupCache");
0N/A
0N/A /**
0N/A * Returns a light weight <code>Popup</code> implementation. If
0N/A * the <code>Popup</code> needs more space that in available in
0N/A * <code>owner</code>, this will return null.
0N/A */
0N/A static Popup getLightWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A LightWeightPopup popup = getRecycledLightWeightPopup();
0N/A
0N/A if (popup == null) {
0N/A popup = new LightWeightPopup();
0N/A }
0N/A popup.reset(owner, contents, ownerX, ownerY);
0N/A if (!popup.fitsOnScreen() ||
0N/A popup.overlappedByOwnedWindow()) {
0N/A popup.hide();
0N/A return null;
0N/A }
0N/A return popup;
0N/A }
0N/A
0N/A /**
0N/A * Returns the cache to use for heavy weight popups.
0N/A */
625N/A private static List<LightWeightPopup> getLightWeightPopupCache() {
625N/A List<LightWeightPopup> cache = (List<LightWeightPopup>)SwingUtilities.appContextGet(
0N/A lightWeightPopupCacheKey);
0N/A if (cache == null) {
625N/A cache = new ArrayList<LightWeightPopup>();
0N/A SwingUtilities.appContextPut(lightWeightPopupCacheKey, cache);
0N/A }
0N/A return cache;
0N/A }
0N/A
0N/A /**
0N/A * Recycles the LightWeightPopup <code>popup</code>.
0N/A */
0N/A private static void recycleLightWeightPopup(LightWeightPopup popup) {
0N/A synchronized (LightWeightPopup.class) {
625N/A List<LightWeightPopup> lightPopupCache = getLightWeightPopupCache();
0N/A if (lightPopupCache.size() < MAX_CACHE_SIZE) {
0N/A lightPopupCache.add(popup);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns a previously used <code>LightWeightPopup</code>, or null
0N/A * if none of the popups have been recycled.
0N/A */
0N/A private static LightWeightPopup getRecycledLightWeightPopup() {
0N/A synchronized (LightWeightPopup.class) {
625N/A List<LightWeightPopup> lightPopupCache = getLightWeightPopupCache();
625N/A if (lightPopupCache.size() > 0) {
625N/A LightWeightPopup r = lightPopupCache.get(0);
0N/A lightPopupCache.remove(0);
0N/A return r;
0N/A }
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A //
0N/A // Popup methods
0N/A //
0N/A public void hide() {
0N/A super.hide();
0N/A
0N/A Container component = (Container)getComponent();
0N/A
0N/A component.removeAll();
0N/A recycleLightWeightPopup(this);
0N/A }
0N/A public void show() {
0N/A Container parent = null;
0N/A
0N/A if (owner != null) {
0N/A parent = (owner instanceof Container? (Container)owner : owner.getParent());
0N/A }
0N/A
0N/A // Try to find a JLayeredPane and Window to add
0N/A for (Container p = parent; p != null; p = p.getParent()) {
0N/A if (p instanceof JRootPane) {
0N/A if (p.getParent() instanceof JInternalFrame) {
0N/A continue;
0N/A }
0N/A parent = ((JRootPane)p).getLayeredPane();
0N/A // Continue, so that if there is a higher JRootPane, we'll
0N/A // pick it up.
0N/A } else if(p instanceof Window) {
0N/A if (parent == null) {
0N/A parent = p;
0N/A }
0N/A break;
0N/A } else if (p instanceof JApplet) {
0N/A // Painting code stops at Applets, we don't want
0N/A // to add to a Component above an Applet otherwise
0N/A // you'll never see it painted.
0N/A break;
0N/A }
0N/A }
0N/A
0N/A Point p = SwingUtilities.convertScreenLocationToParent(parent, x,
0N/A y);
0N/A Component component = getComponent();
0N/A
0N/A component.setLocation(p.x, p.y);
0N/A if (parent instanceof JLayeredPane) {
625N/A parent.add(component, JLayeredPane.POPUP_LAYER, 0);
0N/A } else {
0N/A parent.add(component);
0N/A }
0N/A }
0N/A
0N/A Component createComponent(Component owner) {
0N/A JComponent component = new JPanel(new BorderLayout(), true);
0N/A
0N/A component.setOpaque(true);
0N/A return component;
0N/A }
0N/A
0N/A //
0N/A // Local methods
0N/A //
0N/A
0N/A /**
0N/A * Resets the <code>Popup</code> to an initial state.
0N/A */
0N/A void reset(Component owner, Component contents, int ownerX,
0N/A int ownerY) {
0N/A super.reset(owner, contents, ownerX, ownerY);
0N/A
0N/A JComponent component = (JComponent)getComponent();
0N/A
0N/A component.setOpaque(contents.isOpaque());
0N/A component.setLocation(ownerX, ownerY);
0N/A component.add(contents, BorderLayout.CENTER);
0N/A contents.invalidate();
0N/A pack();
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Popup implementation that uses a Panel as the popup.
0N/A */
0N/A private static class MediumWeightPopup extends ContainerPopup {
0N/A private static final Object mediumWeightPopupCacheKey =
0N/A new StringBuffer("PopupFactory.mediumPopupCache");
0N/A
0N/A /** Child of the panel. The contents are added to this. */
0N/A private JRootPane rootPane;
0N/A
0N/A
0N/A /**
0N/A * Returns a medium weight <code>Popup</code> implementation. If
0N/A * the <code>Popup</code> needs more space that in available in
0N/A * <code>owner</code>, this will return null.
0N/A */
0N/A static Popup getMediumWeightPopup(Component owner, Component contents,
0N/A int ownerX, int ownerY) {
0N/A MediumWeightPopup popup = getRecycledMediumWeightPopup();
0N/A
0N/A if (popup == null) {
0N/A popup = new MediumWeightPopup();
0N/A }
0N/A popup.reset(owner, contents, ownerX, ownerY);
0N/A if (!popup.fitsOnScreen() ||
0N/A popup.overlappedByOwnedWindow()) {
0N/A popup.hide();
0N/A return null;
0N/A }
0N/A return popup;
0N/A }
0N/A
0N/A /**
0N/A * Returns the cache to use for medium weight popups.
0N/A */
625N/A private static List<MediumWeightPopup> getMediumWeightPopupCache() {
625N/A List<MediumWeightPopup> cache = (List<MediumWeightPopup>)SwingUtilities.appContextGet(
0N/A mediumWeightPopupCacheKey);
0N/A
0N/A if (cache == null) {
625N/A cache = new ArrayList<MediumWeightPopup>();
0N/A SwingUtilities.appContextPut(mediumWeightPopupCacheKey, cache);
0N/A }
0N/A return cache;
0N/A }
0N/A
0N/A /**
0N/A * Recycles the MediumWeightPopup <code>popup</code>.
0N/A */
0N/A private static void recycleMediumWeightPopup(MediumWeightPopup popup) {
0N/A synchronized (MediumWeightPopup.class) {
625N/A List<MediumWeightPopup> mediumPopupCache = getMediumWeightPopupCache();
0N/A if (mediumPopupCache.size() < MAX_CACHE_SIZE) {
0N/A mediumPopupCache.add(popup);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns a previously used <code>MediumWeightPopup</code>, or null
0N/A * if none of the popups have been recycled.
0N/A */
0N/A private static MediumWeightPopup getRecycledMediumWeightPopup() {
0N/A synchronized (MediumWeightPopup.class) {
625N/A List<MediumWeightPopup> mediumPopupCache = getMediumWeightPopupCache();
625N/A if (mediumPopupCache.size() > 0) {
625N/A MediumWeightPopup r = mediumPopupCache.get(0);
0N/A mediumPopupCache.remove(0);
0N/A return r;
0N/A }
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A
0N/A //
0N/A // Popup
0N/A //
0N/A
0N/A public void hide() {
0N/A super.hide();
0N/A rootPane.getContentPane().removeAll();
0N/A recycleMediumWeightPopup(this);
0N/A }
0N/A public void show() {
0N/A Component component = getComponent();
0N/A Container parent = null;
0N/A
0N/A if (owner != null) {
0N/A parent = owner.getParent();
0N/A }
0N/A /*
0N/A Find the top level window,
0N/A if it has a layered pane,
0N/A add to that, otherwise
0N/A add to the window. */
0N/A while (!(parent instanceof Window || parent instanceof Applet) &&
0N/A (parent!=null)) {
0N/A parent = parent.getParent();
0N/A }
0N/A // Set the visibility to false before adding to workaround a
0N/A // bug in Solaris in which the Popup gets added at the wrong
0N/A // location, which will result in a mouseExit, which will then
0N/A // result in the ToolTip being removed.
0N/A if (parent instanceof RootPaneContainer) {
0N/A parent = ((RootPaneContainer)parent).getLayeredPane();
0N/A Point p = SwingUtilities.convertScreenLocationToParent(parent,
0N/A x, y);
0N/A component.setVisible(false);
0N/A component.setLocation(p.x, p.y);
625N/A parent.add(component, JLayeredPane.POPUP_LAYER,
0N/A 0);
0N/A } else {
0N/A Point p = SwingUtilities.convertScreenLocationToParent(parent,
0N/A x, y);
0N/A
0N/A component.setLocation(p.x, p.y);
0N/A component.setVisible(false);
0N/A parent.add(component);
0N/A }
0N/A component.setVisible(true);
0N/A }
0N/A
0N/A Component createComponent(Component owner) {
0N/A Panel component = new MediumWeightComponent();
0N/A
0N/A rootPane = new JRootPane();
0N/A // NOTE: this uses setOpaque vs LookAndFeel.installProperty as
0N/A // there is NO reason for the RootPane not to be opaque. For
0N/A // painting to work the contentPane must be opaque, therefor the
0N/A // RootPane can also be opaque.
0N/A rootPane.setOpaque(true);
0N/A component.add(rootPane, BorderLayout.CENTER);
0N/A return component;
0N/A }
0N/A
0N/A /**
0N/A * Resets the <code>Popup</code> to an initial state.
0N/A */
0N/A void reset(Component owner, Component contents, int ownerX,
0N/A int ownerY) {
0N/A super.reset(owner, contents, ownerX, ownerY);
0N/A
0N/A Component component = getComponent();
0N/A
0N/A component.setLocation(ownerX, ownerY);
0N/A rootPane.getContentPane().add(contents, BorderLayout.CENTER);
0N/A contents.invalidate();
0N/A component.validate();
0N/A pack();
0N/A }
0N/A
0N/A
0N/A // This implements SwingHeavyWeight so that repaints on it
0N/A // are processed by the RepaintManager and SwingPaintEventDispatcher.
0N/A private static class MediumWeightComponent extends Panel implements
0N/A SwingHeavyWeight {
0N/A MediumWeightComponent() {
0N/A super(new BorderLayout());
0N/A }
0N/A }
0N/A }
0N/A}