JAL-3446 defer size calculations until panel added to window
[jalview.git] / src / jalview / gui / SplashScreen.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.Font;
27 import java.awt.Graphics;
28 import java.awt.Image;
29 import java.awt.MediaTracker;
30 import java.awt.Toolkit;
31 import java.awt.event.MouseAdapter;
32 import java.awt.event.MouseEvent;
33 import java.net.URL;
34
35 import javax.swing.JInternalFrame;
36 import javax.swing.JLayeredPane;
37 import javax.swing.JPanel;
38 import javax.swing.JTextPane;
39 import javax.swing.event.HyperlinkEvent;
40 import javax.swing.event.HyperlinkListener;
41
42 import jalview.util.Platform;
43 import javajs.async.SwingJSUtils.StateHelper;
44 import javajs.async.SwingJSUtils.StateMachine;
45
46 /**
47  * DOCUMENT ME!
48  * 
49  * @author $author$
50  * @version $Revision$
51  */
52 @SuppressWarnings("serial")
53 public class SplashScreen extends JPanel
54         implements HyperlinkListener, StateMachine
55 {
56   
57   private static final int STATE_INIT = 0;
58
59   private static final int STATE_LOOP = 1;
60
61   private static final int STATE_DONE = 2;
62
63   private static final int SHOW_FOR_SECS = 5;
64
65   private int FONT_SIZE = (Platform.isJS() ? 14 : 11);
66
67   private JPanel imgPanel = new JPanel(new BorderLayout());
68
69   private JInternalFrame iframe;
70
71   private Image image, logo;
72
73   protected boolean isStartup = false;
74
75   private long oldTextLength = -1;
76
77   private StateHelper helper;
78   /*
79    * allow click in the initial splash screen to dismiss it
80    * immediately (not if opened from About menu)
81    */
82   private MouseAdapter closer = new MouseAdapter()
83   {
84     @Override
85     public void mousePressed(MouseEvent evt)
86     {
87       if (isStartup)
88       {
89         try
90         {
91           closeSplash();
92         } catch (Exception ex)
93         {
94         }
95       }
96     }
97   };
98
99   /**
100    * Constructor that displays the splash screen
101    * 
102    * @param isStartup
103    *          if true the panel removes itself on click or after a few seconds;
104    *          if false it stays up until closed by the user (from Help..About menu)
105    */
106   public SplashScreen(boolean isStartup)
107   {
108     this.isStartup = isStartup;
109     // we must get the image in JavaScript BEFORE starting the helper,
110     // as it will take a 1 ms clock tick to obtain width and height information.
111     image = Toolkit.getDefaultToolkit().createImage(
112             getClass().getResource("/images/Jalview_Logo.png"));
113     helper = new StateHelper(this);
114     helper.next(STATE_INIT);
115   }
116
117   protected void initSplashScreenWindow()
118   {
119     addMouseListener(closer);
120     waitForImages();
121     setLayout(new BorderLayout());
122     iframe = new JInternalFrame();
123     iframe.setFrameIcon(null);
124     iframe.setClosable(true);
125     iframe.setContentPane(this);
126     iframe.setLayer(JLayeredPane.PALETTE_LAYER);  
127     SplashImage splashimg = new SplashImage(image);
128     imgPanel.add(splashimg, BorderLayout.CENTER);
129     add(imgPanel, BorderLayout.NORTH);
130     Desktop.getDesktopPane().add(iframe);
131     refreshText();
132   }
133
134   /**
135    * Both Java and JavaScript have to wait for images, but this method will
136    * accomplish nothing for JavaScript. We have already taken care of image
137    * loading with our state loop in JavaScript.
138    * 
139    */
140   private void waitForImages()
141   {
142     if (Platform.isJS())
143       return;
144     MediaTracker mt = new MediaTracker(this);
145     try
146     {
147       mt.addImage(image, 0);
148       logo = Toolkit.getDefaultToolkit().createImage(
149               getClass().getResource("/images/Jalview_Logo_small.png"));
150     } catch (Exception ex)
151     {
152     }
153     if (logo != null)
154     {
155       mt.addImage(logo, 1);
156     }
157     do
158     {
159       try
160       {
161         mt.waitForAll();
162       } catch (InterruptedException x)
163       {
164       }
165       if (mt.isErrorAny())
166       {
167         System.err.println("Error when loading images!");
168         break;
169       }
170     } while (!mt.checkAll());
171     if (logo != null)
172     {
173       Desktop.getInstance().setIconImage(logo);
174     }
175   }
176
177   /**
178    * update text in author text panel reflecting current version information
179    */
180   protected boolean refreshText()
181   {
182     String newtext = Desktop.getInstance().getAboutMessage();
183     if (oldTextLength == newtext.length())
184     {
185       return false;
186     }
187     oldTextLength = newtext.length();
188     iframe.setVisible(false);
189     JTextPane jtp = new JTextPane();
190     jtp.setEditable(false);
191     jtp.setContentType("text/html");
192     jtp.setText("<html>" + newtext + "</html>");
193     jtp.addHyperlinkListener(this);
194     jtp.setFont(new Font("Verdana", Font.PLAIN, FONT_SIZE));
195     jtp.addMouseListener(closer);
196     jtp.setVisible(true);
197     jtp.setSize(new Dimension(750, 425));
198     add(jtp, BorderLayout.CENTER);
199     revalidate();
200     int h = jtp.getHeight() + imgPanel.getHeight();
201     iframe.setBounds(Math.max(0, (iframe.getParent().getWidth() - 750) / 2),
202            Math.max(0,  (iframe.getParent().getHeight() - h)/2), 750, h);
203     iframe.validate();
204     iframe.setVisible(true);
205     return true;
206   }
207
208   protected void closeSplash()
209   {
210     try
211     {
212
213       iframe.setClosed(true);
214     } catch (Exception ex)
215     {
216     }
217   }
218
219   /**
220    * A simple state machine with just three states: init, loop, and done. Ideal
221    * for a simple while/sleep loop that works in Java and JavaScript
222    * identically.
223    * 
224    */
225   @Override
226   public boolean stateLoop()
227   {
228     while (true)
229     {
230       switch (helper.getState())
231       {
232       case STATE_INIT:
233         initSplashScreenWindow();
234         helper.setState(STATE_LOOP);
235         continue;
236       case STATE_LOOP:
237         if (!isVisible())
238         {
239           helper.setState(STATE_DONE);
240           continue;
241         }
242         if (refreshText())
243         {
244           iframe.repaint();
245         }
246         if (isStartup)
247           helper.delayedState(SHOW_FOR_SECS * 1000, STATE_DONE);
248         return true;
249       default:
250       case STATE_DONE:
251         setVisible(false);
252         closeSplash();
253         Desktop.getInstance().startDialogQueue();
254         return true;
255       }
256     }
257   }
258
259   private class SplashImage extends JPanel
260   {
261     Image image;
262
263     public SplashImage(Image todisplay)
264     {
265       image = todisplay;
266       if (image != null)
267       {
268         setPreferredSize(new Dimension(image.getWidth(this) + 8,
269                 image.getHeight(this)));
270       }
271     }
272
273     @Override
274     public Dimension getPreferredSize()
275     {
276       return new Dimension(image.getWidth(this) + 8, image.getHeight(this));
277     }
278
279     @Override
280     public void paintComponent(Graphics g)
281     {
282       g.setColor(Color.white);
283       g.fillRect(0, 0, getWidth(), getHeight());
284       g.setColor(Color.black);
285
286       if (image != null)
287       {
288         g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
289                 (getHeight() - image.getHeight(this)) / 2, this);
290       }
291     }
292   }
293
294   @Override
295   public void hyperlinkUpdate(HyperlinkEvent e)
296   {
297     Desktop.hyperlinkUpdate(e);
298
299   }
300 }