/*
* Copyright (c) 2009 Kathryn Huxtable and Kenneth Orr.
*
* This file is part of the SeaGlass Pluggable Look and Feel.
*
* 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.
*
* $Id: WindowUtils.java 1595 2011-08-09 20:33:48Z rosstauscher@gmx.de $
*/
package com.seaglasslookandfeel.util;
import java.awt.Component;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
/**
* Various utitilies used to manage window drawing.
*
* @author Ken Orr
* @author Kathryn Huxtable
*/
public class WindowUtils {
/**
* Try's to make the given {@link Window} non-opqaue (transparent) across
* platforms and JREs. This method is not guaranteed to succeed, and will
* fail silently if the given {@code Window} cannot be made non-opaque.
*
* <p>This method is useful, for example, when creating a HUD style window
* that is semi-transparent, and thus doesn't want the window background to
* be drawn.</p>
*
* @param window the {@code Window} to make non-opaque.
*/
public static void makeWindowNonOpaque(Window window) {
// on the mac, simply setting the window's background color to be fully
// transparent makes the window non-opaque.
// window.setBackground(UIManager.getColor("seaGlassTransparent"));
// on non-mac platforms, try to use the facilities of Java 6 update 10.
if (!PlatformUtils.isMac()) {
quietlyTryToMakeWindowNonOqaque(window);
} else {
window.setBackground(UIManager.getColor("seaGlassTransparent"));
}
}
/**
* Sets the shape of a window.
* This will be done via a com.sun API and may be not available on all platforms.
* @param window to change the shape for
* @param s the new shape for the window.
*/
@SuppressWarnings("unchecked")
public static void setWindowShape(Window window, Shape s) {
try {
Class clazz = Class.forName("com.sun.awt.AWTUtilities");
Method method = clazz.getMethod("setWindowShape", java.awt.Window.class, Shape.class);
method.invoke(clazz, window, s);
} catch (Exception e) {
// silently ignore this exception.
}
}
/**
* Trys to invoke
* {@code com.sun.awt.AWTUtilities.setWindowOpaque(window,false)} on the
* given {@link Window}. This will only work when running with JRE 6 update
* 10 or higher. This method will silently fail if the method cannot be
* invoked.
*
* @param window the {@code Window} to try and make non-opaque.
*/
@SuppressWarnings("unchecked")
private static void quietlyTryToMakeWindowNonOqaque(Window window) {
try {
Class clazz = Class.forName("com.sun.awt.AWTUtilities");
Method method = clazz.getMethod("setWindowOpaque", java.awt.Window.class, Boolean.TYPE);
method.invoke(clazz, window, false);
} catch (Exception e) {
// silently ignore this exception.
}
}
/**
* Create and install the repaint window focus listener.
*
* @param window the window.
*
* @return the listener.
*/
public static WindowFocusListener createAndInstallRepaintWindowFocusListener(Window window) {
// create a WindowFocusListener that repaints the window on focus
// changes.
WindowFocusListener windowFocusListener = new WindowFocusListener() {
public void windowGainedFocus(WindowEvent e) {
e.getWindow().repaint();
}
public void windowLostFocus(WindowEvent e) {
e.getWindow().repaint();
}
};
window.addWindowFocusListener(windowFocusListener);
return windowFocusListener;
}
/**
* {@code true} if the given {@link Component}'s parent {@link Window} is
* currently active (focused).
*
* @param component the {@code Component} to check the parent
* {@code Window}'s focus for.
*
* @return {@code true} if the given {@code Component}'s parent
* {@code Window} is currently active.
*/
public static boolean isParentWindowFocused(Component component) {
Window window = SwingUtilities.getWindowAncestor(component);
return window != null && window.isFocused();
}
/**
* Installs a {@link WindowFocusListener} on the given {@link JComponent}'s
* parent {@link Window}. If the {@code JComponent} doesn't yet have a
* parent, then the listener will be installed when the component is added
* to a container.
*
* @param component the component who's parent frame to listen to focus
* changes on.
* @param focusListener the {@code WindowFocusListener} to notify when focus
* changes.
*/
public static void installWeakWindowFocusListener(JComponent component, WindowFocusListener focusListener) {
WindowListener weakFocusListener = createWeakWindowFocusListener(focusListener);
AncestorListener ancestorListener = createAncestorListener(component, weakFocusListener);
component.addAncestorListener(ancestorListener);
}
/**
* Create the weak window focus listener.
*
* @param windowFocusListener the focus listener
*
* @return the weak referenced listener.
*/
private static WindowListener createWeakWindowFocusListener(WindowFocusListener windowFocusListener) {
final WeakReference<WindowFocusListener> weakReference = new WeakReference<WindowFocusListener>(windowFocusListener);
return new WindowAdapter() {
public void windowActivated(WindowEvent e) {
// TODO if the WeakReference's object is null, remove the
// WeakReference as a FocusListener.
if (weakReference.get() != null) {
weakReference.get().windowGainedFocus(e);
}
}
public void windowDeactivated(WindowEvent e) {
if (weakReference.get() != null) {
weakReference.get().windowLostFocus(e);
}
}
};
}
/**
* Installs a listener on the given {@link JComponent}'s parent
* {@link Window} that repaints the given component when the parent window's
* focused state changes. If the given component does not have a parent at
* the time this method is called, then an ancestor listener will be
* installed that installs a window listener when the components parent
* changes.
*
* @param component the {@code JComponent} to add the repaint focus listener
* to.
*/
public static void installJComponentRepainterOnWindowFocusChanged(JComponent component) {
// TODO check to see if the component already has an ancestor.
WindowListener windowListener = createWeakWindowFocusListener(createRepaintWindowListener(component));
AncestorListener ancestorListener = createAncestorListener(component, windowListener);
component.addAncestorListener(ancestorListener);
}
/**
* Create the ancestor listener.
*
* @param component the component.
* @param windowListener the listener.
*
* @return the weak referenced listener.
*/
private static AncestorListener createAncestorListener(JComponent component, final WindowListener windowListener) {
final WeakReference<JComponent> weakReference = new WeakReference<JComponent>(component);
return new AncestorListener() {
public void ancestorAdded(AncestorEvent event) {
// TODO if the WeakReference's object is null, remove the
// WeakReference as an AncestorListener.
Window window = weakReference.get() == null ? null : SwingUtilities.getWindowAncestor(weakReference.get());
if (window != null) {
window.removeWindowListener(windowListener);
window.addWindowListener(windowListener);
}
}
public void ancestorRemoved(AncestorEvent event) {
Window window = weakReference.get() == null ? null : SwingUtilities.getWindowAncestor(weakReference.get());
if (window != null) {
window.removeWindowListener(windowListener);
}
}
public void ancestorMoved(AncestorEvent event) {
// no implementation.
}
};
}
/**
* Create the repaint window listener.
*
* @param component the component.
*
* @return the listener.
*/
private static WindowFocusListener createRepaintWindowListener(final JComponent component) {
return new WindowAdapter() {
public void windowActivated(WindowEvent e) {
component.repaint();
}
public void windowDeactivated(WindowEvent e) {
component.repaint();
}
};
}
}