ch.randelshofer.quaqua.QuaquaPopupFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Quaqua Show documentation
Show all versions of Quaqua Show documentation
A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library)
Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer.
Mavenisation by Matt Gumbley, DevZendo.org - for problems with
Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page.
For full license details, see http://randelshofer.ch/quaqua/license.html
The newest version!
/**
* @(#)QuaquaPopupFactory.java
*
* Copyright (c) 2004-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* The copyright of this software is owned by Werner Randelshofer.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Werner Randelshofer. For details see accompanying license terms.
*/
package ch.randelshofer.quaqua;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.*;
import javax.swing.border.LineBorder;
/**
* QuaquaPopupFactory to work around a bug with heavy weight popups
* on Java 1.4 in full screen mode.
*
* @author Werner Randelshofer
* @version $Id: QuaquaPopupFactory.java 361 2010-11-21 11:19:20Z wrandelshofer $
*/
public class QuaquaPopupFactory extends PopupFactory {
/**
* 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 = HEAVY_WEIGHT_POPUP;
/**
* Returns the preferred type of Popup to create.
*/
int getPopupType(Component owner) {
if (owner instanceof JComponent) {
JComponent c = (JComponent) owner;
Float alpha = (Float) c.getClientProperty("Quaqua.PopupMenu.alpha");
if (alpha == null) {
alpha = new Float(0.75f);
}
if (alpha.floatValue() == 1f) {
return HEAVY_WEIGHT_POPUP;
}
}
return UIManager.getBoolean("PopupMenu.enableHeavyWeightPopup") ? HEAVY_WEIGHT_POPUP : MEDIUM_WEIGHT_POPUP;
}
/**
* Returns the popup type to use for the specified parameters.
*/
private int getPopupType(Component owner, Component contents,
int ownerX, int ownerY) {
return getPopupType(owner);
}
@Override
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 pType = getPopupType(owner, contents, x, y);
Popup popup = getPopup(owner, contents, x, y, pType);
if (popup == null) {
// Didn't fit, force to heavy.
popup = getPopup(owner, contents, x, y, HEAVY_WEIGHT_POPUP);
}
return popup;
}
/**
* Obtains the appropriate Popup
based on
* popupType
.
*/
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 getLightWeightPopup(owner, contents, ownerX, ownerY);
// return null;
}
/**
* 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) {
return HeavyWeightPopup.getHeavyWeightPopup(owner, contents, ownerX,
ownerY);
}
/**
* Max number of items to store in any one particular cache.
*/
private static final int MAX_CACHE_SIZE = 5;
/**
* ContainerPopup consolidates the common code used in the light/medium
* weight implementations of Popup
.
*/
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;
// the component which represents the popup
Component component;
// the component which represents the contents
Component contents;
/**
* Returns the Component
returned from
* createComponent
that will hold the Popup
.
*/
Component getComponent() {
if (component == null) {
component = createComponent(owner);
}
return component;
}
Component createComponent(Component owner) {
return null;
}
@Override
public void hide() {
Component c = getComponent();
if (c != null) {
Container parent = c.getParent();
if (parent != null) {
Rectangle bounds = c.getBounds();
parent.remove(c);
parent.repaint(bounds.x, bounds.y, bounds.width,
bounds.height);
}
}
owner = null;
}
public void pack() {
Component c = getComponent();
if (c != null) {
c.setSize(c.getPreferredSize());
}
}
void reset(Component owner, Component contents, int ownerX,
int ownerY) {
if ((owner instanceof JFrame) || (owner instanceof JDialog)
|| (owner instanceof JWindow)) {
// Force the content to be added to the layered pane, otherwise
// we'll get an exception when adding to the RootPaneContainer.
owner = ((RootPaneContainer) owner).getLayeredPane();
}
//// super.reset(owner, contents, ownerX, ownerY);
x = ownerX;
y = ownerY;
this.owner = owner;
this.contents = contents;
}
boolean overlappedByOwnedWindow() {
Component c = getComponent();
if (owner != null && c != null) {
Window w = SwingUtilities.getWindowAncestor(owner);
if (w == null) {
return false;
}
Window[] ownedWindows = w.getOwnedWindows();
if (ownedWindows != null) {
Rectangle bnd = c.getBounds();
for (int i = 0; i < ownedWindows.length; i++) {
Window owned = ownedWindows[i];
if (owned.isVisible()
&& bnd.intersects(owned.getBounds())) {
return true;
}
}
}
}
return false;
}
/**
* Returns true if the Popup can fit on the screen.
*/
boolean fitsOnScreen() {
Component c = getComponent();
if (owner != null && c != null) {
Container parent;
int width = c.getWidth();
int height = c.getHeight();
for (parent = owner.getParent(); parent != null;
parent = parent.getParent()) {
if (parent instanceof JFrame
|| parent instanceof JDialog
|| parent instanceof JWindow) {
Rectangle r = parent.getBounds();
Insets i = parent.getInsets();
r.x += i.left;
r.y += i.top;
r.width -= (i.left + i.right);
r.height -= (i.top + i.bottom);
return SwingUtilities.isRectangleContainingRectangle(
r, new Rectangle(x, y, width, height));
} else if (parent instanceof JApplet) {
Rectangle r = parent.getBounds();
Point p = parent.getLocationOnScreen();
r.x = p.x;
r.y = p.y;
return SwingUtilities.isRectangleContainingRectangle(
r, new Rectangle(x, y, width, height));
} else if (parent instanceof Window
|| parent instanceof Applet) {
// No suitable swing component found
break;
}
}
}
return false;
}
}
/**
* Converts the location x
y
to the
* parents coordinate system, returning the location.
*/
static Point convertScreenLocationToParent(Container parent, int x, int y) {
for (Container p = parent; p != null; p = p.getParent()) {
if (p instanceof Window) {
Point point = new Point(x, y);
SwingUtilities.convertPointFromScreen(point, parent);
return point;
}
}
throw new Error("convertScreenLocationToParent: no window ancestor");
}
/**
* Popup implementation that uses a JPanel as the popup.
*/
private static class LightWeightPopup extends ContainerPopup {
/**
* Returns a light weight Popup
implementation. If
* the Popup
needs more space that in available in
* owner
, this will return null.
*/
static Popup getLightWeightPopup(Component owner, Component contents,
int ownerX, int ownerY) {
LightWeightPopup popup = new LightWeightPopup();
popup.reset(owner, contents, ownerX, ownerY);
/*
if (!popup.fitsOnScreen() ||
popup.overlappedByOwnedWindow()) {
popup.hide();
return null;
}*/
return popup;
}
//
// Popup methods
//
@Override
public void hide() {
super.hide();
Container c = (Container) getComponent();
c.removeAll();
}
@Override
public void show() {
Container parent = null;
if (owner != null) {
parent = (owner instanceof Container ? (Container) owner : owner.getParent());
}
// Try to find a JLayeredPane and Window to add
for (Container p = parent; p != null; p = p.getParent()) {
if (p instanceof JRootPane) {
if (p.getParent() instanceof JInternalFrame) {
continue;
}
parent = ((JRootPane) p).getLayeredPane();
// Continue, so that if there is a higher JRootPane, we'll
// pick it up.
} else if (p instanceof Window) {
if (parent == null) {
parent = p;
}
break;
} else if (p instanceof JApplet) {
// Painting code stops at Applets, we don't want
// to add to a Component above an Applet otherwise
// you'll never see it painted.
break;
}
}
Point p = /*SwingUtilities.*/ convertScreenLocationToParent(parent, x,
y);
Component c = getComponent();
c.setLocation(p.x, p.y);
if (parent instanceof JLayeredPane) {
((JLayeredPane) parent).add(c,
JLayeredPane.POPUP_LAYER, 0);
} else {
parent.add(c);
}
}
@Override
Component createComponent(Component owner) {
JComponent c = new JPanel(new BorderLayout(), true);
c.setBorder(new LineBorder(new Color(0xb2b2b2)));
c.setOpaque(true);
return c;
}
//
// Local methods
//
/**
* Resets the Popup
to an initial state.
*/
@Override
void reset(Component owner, Component contents, int ownerX,
int ownerY) {
super.reset(owner, contents, ownerX, ownerY);
JComponent c = (JComponent) getComponent();
c.setLocation(ownerX, ownerY);
c.add(contents, BorderLayout.CENTER);
contents.invalidate();
pack();
}
}
/**
* Popup implementation that uses a Panel as the popup.
*/
private static class MediumWeightPopup extends ContainerPopup {
private static final Object mediumWeightPopupCacheKey =
new StringBuffer("PopupFactory.mediumPopupCache");
/** Child of the panel. The contents are added to this. */
private JRootPane rootPane;
/**
* Returns a medium weight Popup
implementation. If
* the Popup
needs more space that in available in
* owner
, this will return null.
*/
static Popup getMediumWeightPopup(Component owner, Component contents,
int ownerX, int ownerY) {
MediumWeightPopup popup = new MediumWeightPopup();
if (popup == null) {
popup = new MediumWeightPopup();
}
popup.reset(owner, contents, ownerX, ownerY);
/*
if (!popup.fitsOnScreen() ||
popup.overlappedByOwnedWindow()) {
popup.hide();
return null;
}*/
return popup;
}
//
// Popup
//
@Override
public void hide() {
super.hide();
rootPane.getContentPane().removeAll();
}
@Override
public void show() {
Component c = getComponent();
Container parent = null;
if (owner != null) {
parent = owner.getParent();
}
/*
Find the top level window,
if it has a layered pane,
add to that, otherwise
add to the window. */
while (!(parent instanceof Window || parent instanceof Applet)
&& (parent != null)) {
parent = parent.getParent();
}
// Set the visibility to false before adding to workaround a
// bug in Solaris in which the Popup gets added at the wrong
// location, which will result in a mouseExit, which will then
// result in the ToolTip being removed.
if (parent instanceof RootPaneContainer) {
parent = ((RootPaneContainer) parent).getLayeredPane();
Point p = convertScreenLocationToParent(parent,
x, y);
c.setVisible(false);
c.setLocation(p.x, p.y);
((JLayeredPane) parent).add(c, JLayeredPane.POPUP_LAYER,
0);
} else {
Point p = convertScreenLocationToParent(parent,
x, y);
c.setLocation(p.x, p.y);
c.setVisible(false);
if (parent != null) {
parent.add(c);
}
}
c.setVisible(true);
}
@Override
Component createComponent(Component owner) {
Panel c = new Panel(new BorderLayout());
rootPane = new JRootPane();
// NOTE: this uses setOpaque vs LookAndFeel.installProperty as
// there is NO reason for the RootPane not to be opaque. For
// painting to work the contentPane must be opaque, therefor the
// RootPane can also be opaque.
// MacOSX back this change out because it causes problems
// rootPane.setOpaque(true);
c.add(rootPane, BorderLayout.CENTER);
return c;
}
/**
* Resets the Popup
to an initial state.
*/
@Override
void reset(Component owner, Component contents, int ownerX,
int ownerY) {
super.reset(owner, contents, ownerX, ownerY);
Component c = getComponent();
c.setLocation(ownerX, ownerY);
rootPane.getContentPane().add(contents, BorderLayout.CENTER);
contents.invalidate();
c.validate();
pack();
}
}
/**
* Popup implementation that uses a Window as the popup.
*/
private static class HeavyWeightPopup extends ContainerPopup {
/**
* Returns either a new or recycled Popup
containing
* the specified children.
*/
static Popup getHeavyWeightPopup(Component owner, Component contents,
int ownerX, int ownerY) {
HeavyWeightPopup popup = new HeavyWeightPopup();
/*
if (!popup.fitsOnScreen() ||
popup.overlappedByOwnedWindow()) {
popup.hide();
return null;
}*/
boolean focusPopup = false;
if (contents.isFocusable()) {
if (contents instanceof JPopupMenu) {
JPopupMenu jpm = (JPopupMenu) contents;
Component popComps[] = jpm.getComponents();
for (int i = 0; i < popComps.length; i++) {
if (!(popComps[i] instanceof MenuElement)
&& !(popComps[i] instanceof JSeparator)) {
focusPopup = true;
break;
}
}
}
}
popup.reset(owner, contents, ownerX, ownerY);
if (focusPopup) {
JWindow wnd = (JWindow) ((HeavyWeightPopup) popup).getComponent();
wnd.setFocusableWindowState(true);
// Set window name. We need this in BasicPopupMenuUI
// to identify focusable popup window.
wnd.setName("###focusableSwingPopup###");
}
return popup;
}
@Override
Component createComponent(Component owner) {
final JWindow wnd;
Component c = wnd = new JWindow(SwingUtilities.getWindowAncestor(owner));
// Set window name. We need this in BasicPopupMenuUI
// to identify focusable popup window.
wnd.setName("###focusableSwingPopup###");
wnd.getRootPane().putClientProperty("Window.shadow", Boolean.TRUE);
wnd.setAlwaysOnTop(true);
Float windowAlpha = new Float(0.948);
if (contents instanceof JComponent) {
Object value = ((JComponent) contents).getClientProperty(QuaquaPopupMenuUI.WINDOW_ALPHA_PROPERTY);
if (value instanceof Float) {
windowAlpha = (Float) value;
}
}
wnd.getRootPane().putClientProperty("Window.alpha", windowAlpha);
wnd.setBackground(new Color(0xffffff, true));
wnd.addComponentListener(new ComponentListener() {
private void updateShadow() {
Object oldValue = wnd.getRootPane().getClientProperty("apple.awt.windowShadow.revalidateNow");
wnd.getRootPane().putClientProperty("apple.awt.windowShadow.revalidateNow", (oldValue instanceof Integer) ? ((Integer) oldValue) + 1 : 1);
}
public void componentResized(ComponentEvent e) {
updateShadow();
}
public void componentMoved(ComponentEvent e) {
}
public void componentShown(ComponentEvent e) {
updateShadow();
}
public void componentHidden(ComponentEvent e) {
}
});
return c;
}
@Override
void reset(Component owner, Component contents, int ownerX,
int ownerY) {
super.reset(owner, contents, ownerX, ownerY);
JWindow window = (JWindow) getComponent();
window.setLocation(ownerX, ownerY);
window.getContentPane().add(contents, BorderLayout.CENTER);
contents.invalidate();
pack();
}
/**
* Makes the Popup
visible. If the Popup
is
* currently visible, this has no effect.
*/
@Override
public void show() {
Component c = getComponent();
if (c != null) {
c.show();
}
}
/**
* Hides and disposes of the Popup
. Once a Popup
* has been disposed you should no longer invoke methods on it. A
* dispose
d Popup
may be reclaimed and later used
* based on the PopupFactory
. As such, if you invoke methods
* on a disposed
Popup
, indeterminate
* behavior will result.
*/
@Override
public void hide() {
Component c = getComponent();
if (c instanceof JWindow) {
c.hide();
((JWindow) c).getContentPane().removeAll();
}
dispose();
}
/**
* Frees any resources the Popup
may be holding onto.
*/
void dispose() {
Component c = getComponent();
if (c instanceof JWindow) {
((Window) c).dispose();
c = null;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy