/*
* @(#)SplashWindow.java 2.2.1 2006-05-27
*
* Copyright (c) 2003-2006 Werner Randelshofer
* Staldenmattweg 2, Immensee, CH-6405, Switzerland.
* All rights reserved.
*
* This software is in the public domain.
*/
package org.eclipse.buckminster.jnlp.bootstrap;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.net.URL;
/**
* A Splash window.
* <p>
* Usage: MyApplication is your application class. Create a Splasher class which opens the splash window, invokes the
* main method of your Application class, and disposes the splash window afterwards. Please note that we want to keep
* the Splasher class and the SplashWindow class as small as possible. The less code and the less classes must be loaded
* into the JVM to open the splash screen, the faster it will appear.
*
* <pre>
* class Splasher
* {
* public static void main(String[] args)
* {
* SplashWindow.splash(Startup.class.getResource("splash.gif"));
* MyApplication.main(args);
* SplashWindow.disposeSplash();
* }
* }
* </pre>
*
* @author Werner Randelshofer
* @version 2.2.1 2006-05-27 Abort when splash image can not be loaded. (original version)
* @author Henrik Lindberg
*/
// NOTE: Please keep commented code - it is used for debugging, but should not be used in production
// It is kind of tricky to debug via JWS!
//
@SuppressWarnings("serial")
public class SplashWindow extends Frame
{
private static final int PROGRESS_XMARGIN = 4;
private static final int PROGRESS_YMARGIN = 2;
private static final int PROGRESS_TICK_HEIGHT = 6;
private static final int PROGRESS_TICK_WIDTH = 4;
private static final int PROGRESS_TICK_GAP = 2;
private static final int STOP_ICON_SIZE = 16;
private static final int STOP_ICON_MARGIN = 4;
/**
* The current instance of the splash window. (Singleton design pattern).
*/
private static SplashWindow s_instance;
/**
* The current download listener. (Singleton design pattern).
*/
private static ProgressFacade s_listener;
public static final int SPLASH_IMAGE_BOOT_ID = 0;
public static final int SPLASH_IMAGE_ID = 1;
private static final int WINDOW_ICON_ID = 2;
private static final int STOP_ICON_DB_ID = 3;
private static final int STOP_ICON_BB_ID = 4;
private static final int STOP_ICON_DG_ID = 5;
private static final int STOP_ICON_BG_ID = 6;
private static final String STOP_ICON_DB = "stop.gif"; //$NON-NLS-1$
private static final String STOP_ICON_BB = "stop_border.gif"; //$NON-NLS-1$
private static final String STOP_ICON_DG = "stop_gray.gif"; //$NON-NLS-1$
private static final String STOP_ICON_BG = "stop_border_gray.gif"; //$NON-NLS-1$
/**
* Closes the splash window.
*/
public static void disposeSplash()
{
// s_debugInfo.append("Disposed; ");
if(s_instance != null)
{
// logProgress(0, s_instance.m_progress);
// s_instance.getOwner().dispose();
s_instance.dispose();
s_instance = null;
s_listener = null;
}
}
public static String getDebugString()
{
return ""; //$NON-NLS-1$
// return s_debugInfo.toString();
}
public static ProgressFacade getDownloadServiceListener()
{
if(s_listener == null)
s_listener = new ProgressFacade();
return s_listener;
}
/*
* public static void main(String[] args) throws Exception { byte[] splashImageBootData =
* loadData("http://cs-web1.mainloop.net:8080/cssite/img/splash.cloudpowered.png"); byte[] windowIconData =
* loadData("http://cs-web1.mainloop.net:8080/cssite/img/favicont.png");
*
* Image splashImageBoot = splashImageBootData != null ?
* Toolkit.getDefaultToolkit().createImage(splashImageBootData) : null;
*
* Image windowIconImage = windowIconData != null ? Toolkit.getDefaultToolkit().createImage(windowIconData) : null;
*
* try { SplashWindow.splash(splashImageBoot, splashImageBoot, windowIconImage);
*
* final ProgressFacade monitor = SplashWindow.getDownloadServiceListener();
*
* int startupTime = 200; monitor.setTask("Starting", startupTime); while(--startupTime >= 0 ) { Thread.sleep(100);
* monitor.taskIncrementalProgress(1); }
*
* monitor.taskDone();
*
* } finally { SplashWindow.disposeSplash(); }
*
* }
*
* private static byte[] loadData(String url) throws JNLPException, IOException { byte[] data = null; if(url !=
* null) { InputStream is = null; try { is = new URL(url).openStream(); ByteArrayOutputStream os = new
* ByteArrayOutputStream(); byte[] buf = new byte[0x1000]; int count; while((count = is.read(buf)) > 0)
* os.write(buf, 0, count); data = os.toByteArray();
*
* } catch(IOException e) { throw new JNLPException("Unable to read a splash screen or window icon image",
* "Check your internet connection and try again", ERROR_CODE_REMOTE_IO_EXCEPTION, e); } finally { is.close(); } }
*
* return data; }
*/
// private static void logProgress(int from, int to)
// {
// s_debugInfo.append(s_taskName);
// s_debugInfo.append(": ");
// s_debugInfo.append(from);
// s_debugInfo.append("-");
// s_debugInfo.append(to);
// s_debugInfo.append("; \n");
// }
public static void setProgress(int percentageDone)
{
if(s_instance != null)
{
if(percentageDone > 100)
percentageDone = 100;
if(percentageDone < 0)
{
// s_debugInfo.append("*");
percentageDone = 0;
}
// // if percentageDone is 0, it is considered to start a new "run" - log this
// if(percentageDone == 0)
// {
// logProgress(0,s_instance.m_progress);
// }
if(percentageDone == 0 && s_instance.m_progress > 0 && s_instance.m_progress < 95)
{
// progress did not go to (close to) 100 before it went to 0
// set it to 100 first, and repaint, then wait, and continue.
setProgressChecked(100);
try
{
Thread.sleep(50); // give user the chance to see it
}
catch(InterruptedException e)
{
// just ignore
}
}
setProgressChecked(percentageDone);
}
}
public static void setSplashImage(int imageId)
{
s_instance.setImageId(imageId);
}
/**
* Sets the name of the task for progress monitoring
*
* @param taskName
* The task name
*/
public static void setTaskName(String taskName)
{
s_taskName = taskName;
}
/**
* Open's a splash window using the specified image.
*
* @param splashImage
* The splash image.
*/
public static void splash(Image splashImageBoot, Image splashImage, Image windowIconImage)
{
// s_debugInfo.append("Splash; ");
if(s_instance == null && splashImage != null)
{
// Create the splash image
s_instance = new SplashWindow(splashImageBoot, splashImage, windowIconImage);
// Show the window.
s_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(s_instance)
{
while(!s_instance.m_paintCalled)
{
try
{
s_instance.wait();
}
catch(InterruptedException e)
{
}
}
}
}
}
}
public static boolean splashIsUp()
{
return s_instance != null;
}
private static void setProgressChecked(int percentageDone)
{
// set the progress in splash window
int tickw = PROGRESS_TICK_WIDTH + PROGRESS_TICK_GAP;
int w = s_instance.getWidth() - PROGRESS_XMARGIN * 2;
int n = w / tickw;
// if, by skipping the last gap, there is room for one more...
if(w - n * tickw >= PROGRESS_TICK_WIDTH)
n++;
int n1 = s_instance.m_progress == 0
? 0
: (int)(((s_instance.m_progress / 100.0) * n + 0.5));
int n2 = percentageDone == 0
? 0
: (int)(((percentageDone / 100.0) * n + 0.5));
// in case progress goes in the reverse...
if(s_instance.m_progress > percentageDone)
{
int tmp = n2;
n2 = n1;
n1 = tmp;
}
// set the new progress value
s_instance.m_progress = percentageDone;
// repaint the progressbar area - include only the area m1-m2 to reduce flicker
s_instance.repaint(PROGRESS_XMARGIN + n1 * (PROGRESS_TICK_GAP + PROGRESS_TICK_WIDTH), s_instance.getHeight()
- PROGRESS_TICK_HEIGHT - PROGRESS_YMARGIN, (n2 - n1) * (PROGRESS_TICK_GAP + PROGRESS_TICK_WIDTH),
PROGRESS_TICK_HEIGHT);
}
private Image m_stopIcon;
private Image m_stopIconDB;
private Image m_stopIconBB;
private Image m_stopIconDG;
private Image m_stopIconBG;
private int m_stopXLocation = 0;
private int m_stopYLocation = 0;
private boolean stopped = false;
/**
* The two splash images which is displayed on the splash window.
*/
private final Image[] m_images = new Image[2];
private int m_imageId = SPLASH_IMAGE_BOOT_ID;
/* private static StringBuffer s_debugInfo = new StringBuffer(); */
// Please keep this variable, even if it is not read - future functionality will make use
// of it
@SuppressWarnings("unused")
private static String s_taskName = Messages.getString("run"); //$NON-NLS-1$
/**
* 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 m_paintCalled = false;
/**
* This attribute indicates how much progress that should be reported. Leave it at 0 to only get a splash image.
*/
private int m_progress = 0;
private Color m_progressColor;
/**
* Creates a new instance.
*
* @param parent
* the parent of the window.
* @param splashImage
* the splash image.
* @param windowIconImage
* the taskbar icon image
*/
private SplashWindow(Image splashImageBoot, Image splashImage, Image windowIconImage)
{
m_stopIconDB = getImageFromResources(STOP_ICON_DB);
m_stopIconBB = getImageFromResources(STOP_ICON_BB);
m_stopIconDG = getImageFromResources(STOP_ICON_DG);
m_stopIconBG = getImageFromResources(STOP_ICON_BG);
// Load the images
MediaTracker mt = new MediaTracker(this);
if(m_stopIconDB != null)
mt.addImage(m_stopIconDB, STOP_ICON_DB_ID);
if(m_stopIconBB != null)
mt.addImage(m_stopIconBB, STOP_ICON_BB_ID);
if(m_stopIconDG != null)
mt.addImage(m_stopIconDG, STOP_ICON_DG_ID);
if(m_stopIconBG != null)
mt.addImage(m_stopIconBG, STOP_ICON_BG_ID);
if(splashImageBoot != null)
mt.addImage(splashImageBoot, SPLASH_IMAGE_BOOT_ID);
if(splashImage != null)
mt.addImage(splashImage, SPLASH_IMAGE_ID);
if(windowIconImage != null)
mt.addImage(windowIconImage, WINDOW_ICON_ID);
try
{
if(m_stopIconDB != null)
mt.waitForID(STOP_ICON_DB_ID);
if(m_stopIconBB != null)
mt.waitForID(STOP_ICON_BB_ID);
if(m_stopIconDG != null)
mt.waitForID(STOP_ICON_DG_ID);
if(m_stopIconBG != null)
mt.waitForID(STOP_ICON_BG_ID);
if(splashImageBoot != null)
mt.waitForID(SPLASH_IMAGE_BOOT_ID);
if(splashImage != null)
mt.waitForID(SPLASH_IMAGE_ID);
if(windowIconImage != null)
mt.waitForID(WINDOW_ICON_ID);
}
catch(InterruptedException ie)
{
}
setUndecorated(true);
setTitle(Messages.getString("configuring_materialization_infrastructure")); //$NON-NLS-1$
if(m_stopIconDB != null && mt.isErrorID(STOP_ICON_DB_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_stop_image")); //$NON-NLS-1$
m_stopIconDB = null;
}
if(m_stopIconBB != null && mt.isErrorID(STOP_ICON_BB_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_border_stop_image")); //$NON-NLS-1$
m_stopIconBB = null;
}
if(m_stopIconDG != null && mt.isErrorID(STOP_ICON_DG_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_gray_stop_image")); //$NON-NLS-1$
m_stopIconDG = null;
}
if(m_stopIconBG != null && mt.isErrorID(STOP_ICON_BG_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_gray_border_stop_image")); //$NON-NLS-1$
m_stopIconBG = null;
}
if(windowIconImage != null)
{
if(!mt.isErrorID(WINDOW_ICON_ID))
setIconImage(windowIconImage);
else
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_window_icon")); //$NON-NLS-1$
}
if(splashImageBoot != null && mt.isErrorID(SPLASH_IMAGE_BOOT_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_splash_boot_image")); //$NON-NLS-1$
splashImageBoot = null;
}
if(splashImage != null && mt.isErrorID(SPLASH_IMAGE_ID))
{
System.err.println(Messages.getString("warning_SplashWindow_couldnt_load_splash_image")); //$NON-NLS-1$
splashImage = null;
}
m_stopIcon = m_stopIconDB;
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Abort on failure
if(splashImageBoot == null && splashImage == null)
{
setSize(0, 0);
synchronized(this)
{
m_paintCalled = true;
notifyAll();
}
return;
}
// 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()
{
@Override
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.m_paintCalled = true;
SplashWindow.this.notifyAll();
}
if(isStopLocation(evt.getX(), evt.getY()))
{
if(!stopped)
{
m_stopIconBB = m_stopIconBG;
m_stopIconDB = m_stopIconDG;
if(m_stopIcon != m_stopIconBG)
{
m_stopIcon = m_stopIconBG;
repaint();
}
getDownloadServiceListener().setCanceled(true);
stopped = true;
}
}
else
{
// Dispose was changed to just ICONIFIED so that the window would not completely disappear
// (it is registered on the task bar)
// dispose();
// the latest comment: we don't want to iconify it
// setExtendedState(Frame.ICONIFIED);
}
}
};
addMouseListener(disposeOnClick);
addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseMoved(MouseEvent e)
{
boolean repaint = false;
if(isStopLocation(e.getX(), e.getY()))
{
if(m_stopIcon != m_stopIconBB)
{
m_stopIcon = m_stopIconBB;
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
repaint = true;
}
}
else
{
if(m_stopIcon != m_stopIconDB)
{
m_stopIcon = m_stopIconDB;
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
repaint = true;
}
}
if(repaint)
repaint();
}
});
m_progressColor = new Color(0xd8e5ee);
m_images[SPLASH_IMAGE_BOOT_ID] = splashImageBoot;
m_images[SPLASH_IMAGE_ID] = splashImage;
setImageId((splashImageBoot == null)
? SPLASH_IMAGE_ID
: SPLASH_IMAGE_BOOT_ID);
}
/**
* Paints the image on the window.
*/
@Override
public void paint(Graphics g)
{
g.drawImage(m_images[m_imageId], 0, 0, this);
// 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(!m_paintCalled)
{
m_paintCalled = true;
synchronized(this)
{
notifyAll();
}
}
m_stopXLocation = getWidth() - STOP_ICON_SIZE - STOP_ICON_MARGIN;
m_stopYLocation = STOP_ICON_MARGIN;
if(m_stopIcon != null)
{
g.drawImage(m_stopIcon, m_stopXLocation, m_stopYLocation, this);
}
// Continue with painting progress
int y = getHeight() - PROGRESS_YMARGIN - PROGRESS_TICK_HEIGHT;
int x = PROGRESS_XMARGIN;
int tickw = PROGRESS_TICK_WIDTH + PROGRESS_TICK_GAP;
int w = getWidth() - PROGRESS_XMARGIN * 2;
int n = w / tickw;
// if, by skipping the last gap, there is room for one more...
if(w - n * tickw >= PROGRESS_TICK_WIDTH)
n++;
int j = m_progress == 0
? 0
: (int)(((m_progress / 100.0) * n + 0.5));
g.setColor(m_progressColor);
for(int i = 0; i < j; i++)
{
g.fillRoundRect(x, y, PROGRESS_TICK_WIDTH, PROGRESS_TICK_HEIGHT, 2, 2);
x += PROGRESS_TICK_WIDTH + PROGRESS_TICK_GAP;
}
}
/**
* Updates the display area of the window.
*/
@Override
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);
}
private Image getImageFromResources(String imageName)
{
Class<?> myClass = this.getClass();
String imageResource = "/icons/" + imageName; //$NON-NLS-1$
URL imageUrl = myClass.getResource(imageResource);
return Toolkit.getDefaultToolkit().createImage(imageUrl);
}
private boolean isStopLocation(int x, int y)
{
return x >= m_stopXLocation && x <= (m_stopXLocation + STOP_ICON_SIZE - 1) && y >= m_stopYLocation
&& y <= (m_stopYLocation + STOP_ICON_SIZE - 1);
}
private void setImageId(int imageId)
{
if(!(imageId == SPLASH_IMAGE_BOOT_ID || imageId == SPLASH_IMAGE_ID))
throw new IllegalArgumentException(Messages.getString("splash_imageId_is_out_of_range")); //$NON-NLS-1$
Image image = m_images[imageId];
if(image == null)
//
// We don't permit this since the image is null
//
return;
m_imageId = imageId;
// Center the window on the screen
int imgWidth = image.getWidth(this);
int imgHeight = image.getHeight(this);
setSize(imgWidth, imgHeight);
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((screenDim.width - imgWidth) / 2, (screenDim.height - imgHeight) / 2);
repaint();
}
}