001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-10-28 21:01:43 +0000 (Wed, 28 Oct 2015) $' 
007 * '$Revision: 34134 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.gui;
031
032import java.awt.BorderLayout;
033import java.awt.Color;
034import java.awt.Dimension;
035import java.awt.Font;
036import java.awt.FontMetrics;
037import java.awt.Frame;
038import java.awt.Graphics;
039import java.awt.Image;
040import java.awt.MediaTracker;
041import java.awt.Toolkit;
042import java.awt.event.MouseAdapter;
043import java.awt.event.MouseEvent;
044import java.net.URL;
045
046import javax.swing.JLabel;
047import javax.swing.JPanel;
048import javax.swing.JWindow;
049import javax.swing.SwingUtilities;
050
051import org.kepler.build.modules.ModuleTree;
052import org.kepler.configuration.ConfigurationManager;
053import org.kepler.configuration.ConfigurationProperty;
054import org.kepler.util.StatusListener;
055import org.kepler.util.StatusNotifier;
056
057/**
058 * A Splash window.
059 * <p>
060 * 
061 * Usage: MyApplication is your application class. Create a Splasher class which
062 * opens the splash window, invokes the main method of your Application class,
063 * and disposes the splash window afterwards. Please note that we want to keep
064 * the Splasher class and the SplashWindow class as small as possible. The less
065 * code and the less classes must be loaded into the JVM to open the splash
066 * screen, the faster it will appear.
067 * 
068 * <pre>
069 * class Splasher {
070 *     public static void main(String[] args) {
071 *         SplashWindow.splash(Startup.class.getResource(&quot;splash.gif&quot;));
072 *         MyApplication.main(args);
073 *         SplashWindow.disposeSplash();
074 *     }
075 * }
076 * </pre>
077 * 
078 * @author Werner Randelshofer
079 * @version 2.1 2005-04-03 Revised.
080 */
081public class SplashWindow extends JWindow implements StatusListener {
082    /**
083     * The current instance of the splash window. (Singleton design pattern).
084     */
085    private static SplashWindow instance;
086
087    /**
088     * The splash image which is displayed on the splash window.
089     */
090    private Image image;
091
092    /**
093     * The version number to be displayed in the window.
094     */
095    private String version;
096    private String versionLabel;
097
098    /** The status message to be displayed on the splash window. */
099    private String _statusMessage = "";
100    
101    /** The version font. */
102    private Font _versionFont;
103    
104    /** The version color. */
105    private Color _versionColor;
106    
107    /** The status message font. */
108    private Font _statusFont;
109    
110    /** The status message color. */
111    private Color _statusColor;
112    
113    /** The color of the progress bar. */
114    private Color _progressBarColor;
115    
116    /**
117     * Creates a new instance.
118     * 
119     * @param parent
120     *            the parent of the window.
121     * @param image
122     *            the splash image.
123     */
124    private SplashWindow(Frame parent, Image image) {
125        super(parent);
126        this.image = image;
127
128        // Load the image
129        MediaTracker mt = new MediaTracker(this);
130        mt.addImage(image, 0);
131
132        try {
133            mt.waitForID(0);
134        } catch (InterruptedException ie) {
135            // Ignored
136        }
137        
138        // Center the window on the screen
139        int imgWidth = image.getWidth(this);
140        int imgHeight = image.getHeight(this);
141        setSize(imgWidth, imgHeight);
142        
143        Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
144        setLocation((screenDim.width - imgWidth) / 2,
145                (screenDim.height - imgHeight) / 2);
146
147        // Users shall be able to close the splash window by
148        // clicking on its display area. This mouse listener
149        // listens for mouse clicks and disposes the splash window.
150        MouseAdapter disposeOnClick = new MouseAdapter() {
151            @Override
152            public void mouseClicked(MouseEvent evt) {
153                dispose();
154            }
155        };
156        addMouseListener(disposeOnClick);
157
158        ConfigurationProperty commonProperty = ConfigurationManager
159                .getInstance().getProperty(
160                        ConfigurationManager.getModule("common"));
161
162        versionLabel = commonProperty.getProperty("splash.versionLabel")
163                .getValue();
164        version = commonProperty.getProperty("kepler.version").getValue();
165        
166        _versionFont = new Font("SanSerif", Font.BOLD, 24);
167        // set the color to match the "Kepler" in the image.
168        _versionColor = new Color(28, 115, 84);
169
170        _statusFont = new Font("Dialog", Font.BOLD, 12);
171        _statusColor = Color.BLACK;
172        
173        _progressBarColor = _versionColor;
174        
175        JPanel content = (JPanel)getContentPane();
176        content.add(_splashLabel, BorderLayout.CENTER);         
177    }
178    
179
180    /**
181     * Updates the display area of the window.
182     */
183    /*
184    @Override
185    public void update(Graphics g) {
186        // Note: Since the paint method is going to draw an
187        // image that covers the complete area of the component we
188        // do not fill the component with its background color
189        // here. This avoids flickering.
190        paint(g);
191    }
192    */
193
194    private class SplashJLabel extends JLabel {
195        
196        /**
197         * Paints the image on the window.
198         */
199        @Override
200        public void paintComponent(Graphics g) {
201            super.paintComponent(g);
202            
203            g.drawImage(image, 0, 0, this);
204            Font saveFont = g.getFont();
205            Color saveColor = g.getColor();
206    
207            // draw the version string
208            g.setFont(_versionFont);
209            g.setColor(_versionColor);
210    
211            int imgWidth = image.getWidth(this);
212            String versionString = versionLabel + " " + version;
213            int x = getHorizontalStartPosition(versionString, imgWidth, g);
214            g.drawString(versionString, x, VERSION_Y);
215            
216            // draw the status string
217            g.setFont(_statusFont);
218            g.setColor(_statusColor);
219            x = getHorizontalStartPosition(_statusMessage, imgWidth, g);
220            g.drawString(_statusMessage, x, STATUS_Y);
221            
222            g.setColor(_progressBarColor);
223            double percent = (double) _progress / MAX_STEPS;
224            int maxWidth = imgWidth - 46;
225            int width = (int) (maxWidth * percent);
226            if(width > maxWidth) {
227                width = maxWidth;   
228            }
229            g.fillRoundRect((imgWidth - width) / 2, PROGRESS_BAR_Y, width, 5, 5, 5);
230            
231            g.setFont(saveFont);
232            g.setColor(saveColor);
233        }    
234    }
235    
236    /**
237     * Calculate the x position at which to start drawing a string to center it
238     * within a given width.
239     * 
240     * @param s
241     *            the string to be centered
242     * @param w
243     *            the width over which the string should be centered
244     * @param g
245     *            the graphics context used to draw the string
246     * @return the x position to start drawing the string
247     */
248    int getHorizontalStartPosition(String s, int width, Graphics g) {
249        FontMetrics fm = g.getFontMetrics();
250        int xpos = (width - fm.stringWidth(s)) / 2;
251        return xpos;
252    }
253
254    /**
255     * Open's a splash window using the specified image.
256     * 
257     * @param image
258     *            The splash image.
259     */
260    public static void splash(Image image) {
261        splash(image, false);
262    }
263
264    /**
265   *
266   */
267    public static void splash(Image image, boolean reset) {
268        if (reset) {
269            instance = null;
270        }
271        if (instance == null && image != null) {
272            Frame f = new Frame();
273
274            // Create the splash image
275            instance = new SplashWindow(f, image);
276
277            // Show the window.
278            instance.setVisible(true);
279
280            // listen for startup messages.
281            StatusNotifier.addStatusListener(instance);
282        }
283    }
284
285    /**
286     * Opens a splash window using the specified image.
287     * 
288     * @param imageURL
289     *            The url of the splash image.
290     */
291    public static void splash(URL imageURL) {
292        splash(imageURL, false);
293    }
294
295    /**
296     * splash the url. if reset is true, allow the splash to be shown more than
297     * once
298     */
299    public static void splash(URL imageURL, boolean reset) {
300        if (imageURL != null) {
301            splash(Toolkit.getDefaultToolkit().createImage(imageURL), reset);
302        }
303    }
304
305    /**
306     * Closes the splash window.
307     */
308    public static void disposeSplash() {
309        if (instance != null) {
310            instance.getOwner().dispose();
311            instance = null;
312        }
313    }
314
315    /** Update the status text and progress bar. */
316    @Override
317    public void log(String message) {
318        
319        _statusMessage = message;
320        _progress++;
321        
322        if(SwingUtilities.isEventDispatchThread()) {
323                _splashLabel.paintImmediately(_splashLabel.getBounds());
324        } else {
325                _splashLabel.repaint();
326        }
327    }
328
329    /** The label containing the image, version text, and status. */
330    private JLabel _splashLabel = new SplashJLabel();
331    
332    /** The number of steps completed during initialization. */
333    private int _progress = 0;
334    
335    /** Maximum number of steps for the progress bar.
336     *  This is a rough estimate...
337     */
338    private static final int MAX_STEPS = 5 + ModuleTree.instance().getModuleList().size() / 2;
339    
340    /** Version height offset. */
341    private static final int VERSION_Y = 200;
342    
343    /** Status message height offset. */
344    private static final int STATUS_Y = VERSION_Y + 40;
345    
346    /** Progress bar height offset. */
347    private static final int PROGRESS_BAR_Y = STATUS_Y + 10;
348    
349    
350}