/**
* Squidy Interaction Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Squidy Interaction Library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.core;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import org.squidy.util.ImageUtilities;
/**
* <code>SplashWindow</code>.
*
* <pre>
* Date: Nov 19, 2008
* Time: 1:24:15 AM
* </pre>
*
* @author Roman Rädle, <a
* href="mailto:Roman.Raedle@uni-konstanz.de">Roman.Raedle@uni-konstanz.de</a>, University
* of Konstanz
* @version $Id: SplashWindow.java 772 2011-09-16 15:39:44Z raedle $
* @since 1.1.0
*/
public class SplashWindow extends Window {
/**
* Generated serial version UID.
*/
private static final long serialVersionUID = 8170196223649522659L;
public static final double SCREEN_RATIO = 2. / 3.;
/**
* The current instance of the splash window. (Singleton design pattern).
*/
private static SplashWindow instance;
/**
* The splash image which is displayed on the splash window.
*/
private volatile Image image;
/**
* This attribute indicates whether the method paint(Graphics) has been called at least once
* since the construction of this window.<br>
* This attribute is used to notify method splash(Image) that the window has been drawn at least
* once by the AWT event dispatcher thread.<br>
* This attribute acts like a latch. Once set to true, it will never be changed back to false
* again.
*
* @see #paint
* @see #splash
*/
private boolean paintCalled = false;
/**
* Creates a new instance.
*
* @param parent
* the parent of the window.
* @param image
* the splash image.
*/
private SplashWindow(Frame parent, Image image) {
super(parent);
// Load the image
MediaTracker mt = new MediaTracker(this);
mt.addImage(image, 0);
try {
mt.waitForID(0);
}
catch (InterruptedException ie) {
}
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
Dimension splashDimension = calculateSplashDimension(image);
this.image = ImageUtilities.scaleImageTo(image, (int) splashDimension.getWidth(), (int) splashDimension.getHeight());
setSize(splashDimension);
// Center the window on the screen
setLocation((screenDim.width - (int) splashDimension.getWidth()) / 2, (screenDim.height - (int) splashDimension.getHeight()) / 2);
// Users shall be able to close the splash window by
// clicking on its display area. This mouse listener
// listens for mouse clicks and disposes the splash window.
MouseAdapter disposeOnClick = new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
// Note: To avoid that method splash hangs, we
// must set paintCalled to true and call notifyAll.
// This is necessary because the mouse click may
// occur before the contents of the window
// has been painted.
synchronized (SplashWindow.this) {
SplashWindow.this.paintCalled = true;
SplashWindow.this.notifyAll();
}
dispose();
}
};
addMouseListener(disposeOnClick);
// // Sets the splash window on top of everything
// setAlwaysOnTop(true);
}
/**
* Updates the display area of the window.
*/
public void update(Graphics g) {
// Note: Since the paint method is going to draw an
// image that covers the complete area of the component we
// do not fill the component with its background color
// here. This avoids flickering.
paint(g);
}
/**
* Paints the image on the window.
*/
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
Graphics2D g2d = (Graphics2D) g;
Stroke defaultStroke = g2d.getStroke();
g2d.setStroke(new BasicStroke(1f));
Color defaultColor = g.getColor();
g.setColor(Color.LIGHT_GRAY);
g.drawRect(0, 0, getWidth(), getHeight());
g2d.setStroke(defaultStroke);
g.setColor(defaultColor);
// Notify method splash that the window
// has been painted.
// Note: To improve performance we do not enter
// the synchronized block unless we have to.
// if (!paintCalled) {
// paintCalled = true;
// synchronized (this) {
// notifyAll();
// }
// }
}
/**
* Open's a splash window using the specified image.
*
* @param image
* The splash image.
*/
public static void splash(Image image) {
if (instance == null && image != null) {
Frame f = new Frame();
// Create the splash image
instance = new SplashWindow(f, image);
// Show the window.
instance.setVisible(true);
// Note: To make sure the user gets a chance to see the
// splash window we wait until its paint method has been
// called at least once by the AWT event dispatcher thread.
// If more than one processor is available, we don't wait,
// and maximize CPU throughput instead.
if (!EventQueue.isDispatchThread() && Runtime.getRuntime().availableProcessors() == 1) {
synchronized (instance) {
while (!instance.paintCalled) {
try {
instance.wait();
}
catch (InterruptedException e) {
}
}
}
}
}
}
/**
* Open's a splash window using the specified image.
*
* @param imageURL
* The url of the splash image.
*/
public static void splash(URL imageURL) {
if (imageURL != null) {
splash(Toolkit.getDefaultToolkit().createImage(imageURL));
}
}
/**
* Calculates an adequate splash window size.
*
* @param image
* Image used to calculate splash window size.
* @return The calculated splash window size.
*/
private Dimension calculateSplashDimension(Image image) {
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
double screenWidth = screenDimension.getWidth();
double imageWidth = image.getWidth(this);
double splashWidth = (screenWidth * SCREEN_RATIO);
if (splashWidth < imageWidth) {
double ratio = imageWidth / splashWidth;
return new Dimension((int) splashWidth, (int) (image.getHeight(this) / ratio));
}
return new Dimension((int) imageWidth, (int) (image.getHeight(this)));
}
/**
* Closes the splash window.
*/
public static void disposeSplash() {
if (instance != null) {
instance.getOwner().dispose();
instance = null;
}
}
/**
* Invokes the main method of the provided class name.
*
* @param args
* the command line arguments
*/
public static void invokeMain(String className, String[] args) {
try {
Class.forName(className).getMethod("main", new Class[] { String[].class }).invoke(null,
new Object[] { args });
}
catch (Exception e) {
InternalError error = new InternalError("Failed to invoke main method");
error.initCause(e);
throw error;
}
}
}