View Javadoc

1   /*
2    * @(#)SplashWindow.java  2.2  2005-04-03
3    *
4    * Copyright (c) 2003-2005 Werner Randelshofer
5    * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
6    * All rights reserved.
7    *
8    * This software is in the public domain.
9    */
10  
11  package splash;
12  import java.awt.Dimension;
13  import java.awt.EventQueue;
14  import java.awt.Frame;
15  import java.awt.Graphics;
16  import java.awt.Image;
17  import java.awt.MediaTracker;
18  import java.awt.Toolkit;
19  import java.awt.Window;
20  import java.awt.event.MouseAdapter;
21  import java.awt.event.MouseEvent;
22  import java.net.URL;
23  
24  /***
25   * A Splash window.
26   *  <p>
27   * Usage: MyApplication is your application class. Create a Splasher class which
28   * opens the splash window, invokes the main method of your Application class,
29   * and disposes the splash window afterwards.
30   * Please note that we want to keep the Splasher class and the SplashWindow class
31   * as small as possible. The less code and the less classes must be loaded into
32   * the JVM to open the splash screen, the faster it will appear.
33   * <pre>
34   * class Splasher {
35   *    public static void main(String[] args) {
36   *         SplashWindow.splash(Startup.class.getResource("splash.gif"));
37   *         MyApplication.main(args);
38   *         SplashWindow.disposeSplash();
39   *    }
40   * }
41   * </pre>
42   *
43   * @author  Werner Randelshofer
44   * @version 2.1 2005-04-03 Revised.
45   */
46  public class SplashWindow extends Window {
47      /***
48       * The current instance of the splash window.
49       * (Singleton design pattern).
50       */
51      private static SplashWindow instance;
52      
53      /***
54       * The splash image which is displayed on the splash window.
55       */
56      private Image image;
57      
58      /***
59       * This attribute indicates whether the method
60       * paint(Graphics) has been called at least once since the
61       * construction of this window.<br>
62       * This attribute is used to notify method splash(Image)
63       * that the window has been drawn at least once
64       * by the AWT event dispatcher thread.<br>
65       * This attribute acts like a latch. Once set to true,
66       * it will never be changed back to false again.
67       *
68       * @see #paint
69       * @see #splash
70       */
71      private boolean paintCalled = false;
72      
73      /***
74       * Creates a new instance.
75       * @param parent the parent of the window.
76       * @param image the splash image.
77       */
78      private SplashWindow(Frame parent, Image image) {
79          super(parent);
80          this.image = image;
81  
82          // Load the image
83          MediaTracker mt = new MediaTracker(this);
84          mt.addImage(image,0);
85          try {
86              mt.waitForID(0);
87          } catch(InterruptedException ie){}
88          
89          // Center the window on the screen
90          int imgWidth = image.getWidth(this);
91          int imgHeight = image.getHeight(this);
92          setSize(imgWidth, imgHeight);
93          Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
94          setLocation(
95          (screenDim.width - imgWidth) / 2,
96          (screenDim.height - imgHeight) / 2
97          );
98          
99          // Users shall be able to close the splash window by
100         // clicking on its display area. This mouse listener
101         // listens for mouse clicks and disposes the splash window.
102         MouseAdapter disposeOnClick = new MouseAdapter() {
103             public void mouseClicked(MouseEvent evt) {
104                 // Note: To avoid that method splash hangs, we
105                 // must set paintCalled to true and call notifyAll.
106                 // This is necessary because the mouse click may
107                 // occur before the contents of the window
108                 // has been painted.
109                 synchronized(SplashWindow.this) {
110                     SplashWindow.this.paintCalled = true;
111                     SplashWindow.this.notifyAll();
112                 }
113                 dispose();
114             }
115         };
116         addMouseListener(disposeOnClick);
117     }
118     
119     /***
120      * Updates the display area of the window.
121      */
122     public void update(Graphics g) {
123         // Note: Since the paint method is going to draw an
124         // image that covers the complete area of the component we
125         // do not fill the component with its background color
126         // here. This avoids flickering.
127         paint(g);
128     }
129     /***
130      * Paints the image on the window.
131      */
132     public void paint(Graphics g) {
133         g.drawImage(image, 0, 0, this);
134         
135         // Notify method splash that the window
136         // has been painted.
137         // Note: To improve performance we do not enter
138         // the synchronized block unless we have to.
139         if (! paintCalled) {
140             paintCalled = true;
141             synchronized (this) { notifyAll(); }
142         }
143     }
144     
145     /***
146      * Open's a splash window using the specified image.
147      * @param image The splash image.
148      */
149     public static void splash(Image image) {
150         if (instance == null && image != null) {
151             Frame f = new Frame();
152             
153             // Create the splash image
154             instance = new SplashWindow(f, image);
155             
156             // Show the window.
157             instance.show();
158             
159             // Note: To make sure the user gets a chance to see the
160             // splash window we wait until its paint method has been
161             // called at least once by the AWT event dispatcher thread.
162             // If more than one processor is available, we don't wait,
163             // and maximize CPU throughput instead.
164             if (! EventQueue.isDispatchThread() 
165             && Runtime.getRuntime().availableProcessors() == 1) {
166                 synchronized (instance) {
167                     while (! instance.paintCalled) {
168                         try { instance.wait(); } catch (InterruptedException e) {}
169                     }
170                 }
171             }
172         }
173     }
174     /***
175      * Open's a splash window using the specified image.
176      * @param imageURL The url of the splash image.
177      */
178     public static void splash(URL imageURL) {
179         if (imageURL != null) {
180             splash(Toolkit.getDefaultToolkit().createImage(imageURL));
181         }
182     }
183     
184     /***
185      * Closes the splash window.
186      */
187     public static void disposeSplash() {
188         if (instance != null) {
189             instance.getOwner().dispose();
190             instance = null;
191         }
192     }
193     
194     /***
195      * Invokes the main method of the provided class name.
196      * @param args the command line arguments
197      */
198     public static void invokeMain(String className, String[] args) {
199         try {
200             Class.forName(className)
201             .getMethod("main", new Class[] {String[].class})
202             .invoke(null, new Object[] {args});
203         } catch (Exception e) {
204             InternalError error = new InternalError("Failed to invoke main method");
205             error.initCause(e);
206             throw error;
207         }
208     }
209 }