org.assertj.swing.awt.AWT Maven / Gradle / Ivy
Show all versions of assertj-swing Show documentation
/*
* Created on Jan 27, 2008
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright @2008-2013 the original author or authors.
*/
package org.assertj.swing.awt;
import static java.awt.event.InputEvent.BUTTON3_MASK;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.swing.edt.GuiActionRunner.execute;
import static org.assertj.swing.util.Platform.isWindows;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import org.assertj.swing.annotation.RunsInCurrentThread;
import org.assertj.swing.annotation.RunsInEDT;
import org.assertj.swing.edt.GuiQuery;
import org.assertj.swing.util.ToolkitProvider;
/**
* Utility methods related to AWT.
*
* @author Alex Ruiz
*/
public class AWT {
private static final String ROOT_FRAME_CLASSNAME = SwingUtilities.class.getName() + "$";
private static final ToolkitProvider TOOLKIT_PROVIDER = ToolkitProvider.instance();
/**
* Indicates whether the given point, relative to the given {@code JComponent}, is inside the screen boundaries.
*
* @param c the given {@code JComponent}.
* @param p the point to verify.
* @return {@code true} if the point is inside the screen boundaries; {@code false} otherwise.
*/
public static boolean isPointInScreenBoundaries(@Nonnull JComponent c, @Nonnull Point p) {
Point where = translate(c, p.x, p.y);
Rectangle screen = new Rectangle(TOOLKIT_PROVIDER.defaultToolkit().getScreenSize());
return screen.contains(where);
}
/**
* Indicates whether the given point is inside the screen boundaries.
*
* @param p the point to verify.
* @return {@code true} if the point is inside the screen boundaries; {@code false} otherwise.
*/
public static boolean isPointInScreenBoundaries(@Nonnull Point p) {
Rectangle screen = new Rectangle(TOOLKIT_PROVIDER.defaultToolkit().getScreenSize());
return screen.contains(p);
}
/**
*
* Translates the given coordinates to the location on screen of the given AWT or Swing {@code Component}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code Component}.
* @param x X coordinate.
* @param y Y coordinate.
* @return the translated coordinates.
*/
@RunsInCurrentThread
public static @Nullable
Point translate(@Nonnull Component c, int x, int y) {
Point p = locationOnScreenOf(c);
if (p == null) {
return null;
}
p.translate(x, y);
return p;
}
/**
* Returns a point at the center of the visible area of the given AWT or Swing {@code Component}.
*
* @param c the given {@code Component}.
* @return a point at the center of the visible area of the given {@code Component}.
*/
@RunsInEDT
public static @Nonnull
Point visibleCenterOf(@Nonnull final Component c) {
Point center = execute(new GuiQuery() {
@Override
protected Point executeInEDT() {
if (c instanceof JComponent) {
return centerOfVisibleRect((JComponent) c);
}
return centerOf(c);
}
});
return checkNotNull(center);
}
/**
*
* Returns a point at the center of the given AWT or Swing {@code Component}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code Component}.
* @return a point at the center of the given {@code Component}.
*/
@RunsInCurrentThread
public static @Nonnull
Point centerOf(@Nonnull Component c) {
Dimension size = c.getSize();
return new Point(size.width / 2, size.height / 2);
}
/**
*
* Returns a point at the center of the visible rectangle of the given {@code JComponent}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code JComponent}.
* @return a point at the center of the visible rectangle of the given {@code JComponent}.
*/
@RunsInCurrentThread
public static @Nonnull
Point centerOfVisibleRect(@Nonnull JComponent c) {
Rectangle r = c.getVisibleRect();
return centerOf(checkNotNull(r));
}
/**
*
* Returns a point at the center of the given {@code Rectangle}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param r the given {@code Rectangle}.
* @return a point at the center of the given {@code Rectangle}.
*/
@RunsInCurrentThread
public static @Nonnull
Point centerOf(@Nonnull Rectangle r) {
return new Point((r.x + (r.width / 2)), (r.y + (r.height / 2)));
}
/**
*
* Returns the insets of the given AWT or Swing {@code Container}, or an empty one if no insets can be found.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code Container}.
* @return the insets of the given {@code Container}, or an empty one if no insets can be found.
*/
@RunsInCurrentThread
public static @Nonnull
Insets insetsFrom(@Nonnull Container c) {
try {
Insets insets = c.getInsets();
if (insets != null) {
return insets;
}
} catch (Exception e) {
}
return new Insets(0, 0, 0, 0);
}
/**
* Returns {@code true} if the given component is an Applet viewer.
*
* @param c the component to check.
* @return {@code true} if the given component is an Applet viewer, {@code false} otherwise.
*/
public static boolean isAppletViewer(@Nullable Component c) {
return c != null && "sun.applet.AppletViewer".equals(c.getClass().getName());
}
/**
* Returns whether the given component is the default Swing hidden frame.
*
* @param c the component to check.
* @return {@code true} if the given component is the default hidden frame, {@code false} otherwise.
*/
public static boolean isSharedInvisibleFrame(@Nullable Component c) {
if (c == null) {
return false;
}
// Must perform an additional check, since applets may have their own version in their AppContext
return c instanceof Frame
&& (c == JOptionPane.getRootFrame() || c.getClass().getName().startsWith(ROOT_FRAME_CLASSNAME));
}
/**
*
* Returns whether the given AWT or Swing {@code Component} is a heavy-weight pop-up, that is, a container for a
* {@code JPopupMenu} that is implemented with a heavy-weight component (usually an AWT or Swing {@code Window}).
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code Component}.
* @return {@code true} if the given {@code Component} is a heavy-weight pop-up; {@code false} otherwise.
*/
@RunsInCurrentThread
public static boolean isHeavyWeightPopup(@Nonnull Component c) {
if (!(c instanceof Window) || c instanceof Dialog || c instanceof Frame) {
return false;
}
String name = obtainNameSafely(c);
if ("###overrideRedirect###".equals(name) || "###focusableSwingPopup###".equals(name)) {
return true;
}
String typeName = c.getClass().getName();
return typeName.contains("PopupFactory$WindowPopup") || typeName.contains("HeavyWeightWindow");
}
@RunsInCurrentThread
private static @Nullable
String obtainNameSafely(@Nonnull Component c) {
// Work around some components throwing exceptions if getName is called prematurely
try {
return c.getName();
} catch (Throwable e) {
return null;
}
}
/**
*
* Returns the invoker, if any, of the given AWT or Swing {@code Component}; or {@code null}, if the {@code Component}
* is not on a pop-up of any sort.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given {@code Component}.
* @return the invoker, if any, of the given {@code Component}; or {@code null}, if the {@code Component} is not on a
* pop-up of any sort.
*/
@RunsInCurrentThread
public static @Nullable
Component invokerOf(final @Nonnull Component c) {
if (c instanceof JPopupMenu) {
return ((JPopupMenu) c).getInvoker();
}
Container parent = c.getParent();
return parent != null ? invokerOf(parent) : null;
}
/**
*
* Safe version of {@code Component.getLocationOnScreen}, which avoids lockup if an AWT pop-up menu is showing. The
* AWT pop-up holds the AWT tree lock when showing, which lock is required by {@code getLocationOnScreen}.
*
*
*
* Note: This method is accessed in the current executing thread. Such thread may or may not be the event
* dispatch thread (EDT.) Client code must call this method from the EDT.
*
*
* @param c the given AWT or Swing {@code Component}.
* @return the a point specifying the {@code Component}'s top-left corner in the screen's coordinate space, or
* {@code null}, if the {@code Component} is not showing on the screen.
*/
@RunsInCurrentThread
public static @Nullable
Point locationOnScreenOf(@Nonnull Component c) {
if (!isAWTTreeLockHeld()) {
return new Point(c.getLocationOnScreen());
}
if (!c.isShowing()) {
return null;
}
Point location = new Point(c.getLocation());
if (c instanceof Window) {
return location;
}
Container parent = c.getParent();
if (parent == null) {
return null;
}
Point parentLocation = locationOnScreenOf(parent);
if (parentLocation != null) {
location.translate(parentLocation.x, parentLocation.y);
}
return null;
}
/**
* Returns whether the platform registers a pop-up on mouse press.
*
* @return {@code true} if the platform registers a pop-up on mouse press, {@code false} otherwise.
*/
public static boolean popupOnPress() {
// Only w32 is pop-up on release
return !isWindows();
}
/**
* @return the {@code InputEvent} mask for the pop-up trigger button.
*/
public static int popupMask() {
return BUTTON3_MASK;
}
/**
* Indicates whether the AWT Tree Lock is currently held.
*
* @return {@code true} if the AWT Tree Lock is currently held, {@code false} otherwise.
*/
public static boolean isAWTTreeLockHeld() {
Frame[] frames = Frame.getFrames();
if (frames.length == 0) {
return false;
}
// From Abbot: Hack based on 1.4.2 java.awt.PopupMenu implementation, which blocks the event dispatch thread while
// the pop-up is visible, while holding the AWT tree lock.
// Start another thread which attempts to get the tree lock.
// If it can't get the tree lock, then there is a pop-up active in the current tree.
// Any component can provide the tree lock.
Object treeLock = checkNotNull(frames[0].getTreeLock());
ThreadStateChecker checker = new ThreadStateChecker(treeLock);
try {
checker.start();
// wait a little bit for the checker to finish
if (checker.isAlive()) {
checker.join(100);
}
return checker.isAlive();
} catch (InterruptedException e) {
return false;
}
}
// Try to lock the AWT tree lock; returns immediately if it can
private static class ThreadStateChecker extends Thread {
private final Object lock;
public ThreadStateChecker(@Nonnull Object lock) {
super("Thread state checker");
setDaemon(true);
this.lock = lock;
}
@Override
public synchronized void start() {
super.start();
try {
wait(30000);
} catch (InterruptedException e) {
}
}
@Override
public void run() {
synchronized (this) {
notifyAll();
}
synchronized (lock) {
setName(super.getName()); // dummy operation
}
}
}
private AWT() {
}
}