Merge branch 'alpha/origin_2022_JAL-3066_Jalview_212_slivka-integration' into spike...
[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
24 import java.awt.BorderLayout;
25 import java.awt.Color;
26 import java.awt.Component;
27 import java.awt.Dimension;
28 import java.awt.Font;
29 import java.awt.Graphics;
30 import java.awt.Image;
31 import java.awt.MediaTracker;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34
35 import javax.swing.JInternalFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JLayeredPane;
38 import javax.swing.JPanel;
39 import javax.swing.JTextPane;
40 import javax.swing.event.HyperlinkEvent;
41 import javax.swing.event.HyperlinkListener;
42
43 import jalview.util.ChannelProperties;
44 import jalview.util.Platform;
45 /**
46  * DOCUMENT ME!
47  * 
48  * @author $author$
49  * @version $Revision$
50  */
51 @SuppressWarnings("serial")
52 public class SplashScreen extends JPanel
53         implements HyperlinkListener, StateMachine
54 {
55   
56   private static final int STATE_INIT = 0;
57
58   private static final int STATE_LOOP = 1;
59
60   private static final int STATE_DONE = 2;
61
62   private static final int SHOW_FOR_SECS = 5;
63
64   private static final int FONT_SIZE = (Platform.isJS() ? 14 : 11);
65
66   private boolean visible = true;
67
68   private JPanel iconimg = new JPanel(new BorderLayout());
69
70   // could change fg, bg, font later to use ChannelProperties (these are not
71   // actually being used!)
72   private static Color bg = Color.WHITE;
73
74   private static Color fg = Color.BLACK;
75
76   /*
77    * as JTextPane in Java, JLabel in javascript
78    */
79   private Component splashText;
80
81   private JInternalFrame iframe;
82
83   private Image image;
84
85   private boolean transientDialog = false;
86
87   private long oldTextLength = -1;
88
89   /*
90    * allow click in the initial splash screen to dismiss it
91    * immediately (not if opened from About menu)
92    */
93   private MouseAdapter closer = new MouseAdapter()
94   {
95     @Override
96     public void mousePressed(MouseEvent evt)
97     {
98       if (transientDialog)
99       {
100         try
101         {
102           closeSplash();
103         } catch (Exception ex)
104         {
105         }
106       }
107     }
108   };
109
110   /**
111    * Constructor that displays the splash screen
112    * 
113    * @param isStartup
114    *          if true the panel removes itself on click or after a few seconds;
115    *          if false it stays up until closed by the user (from Help..About menu)
116    */
117   public SplashScreen(boolean isStartup)
118   {
119     this.transientDialog = isStartup;
120     // we must get the image in JavaScript BEFORE starting the helper,
121     // as it will take a 1 ms clock tick to obtain width and height information.
122     image = ChannelProperties.getImage("banner");
123     logo = ChannelProperties.getImage("logo.48");
124     font = new Font("SansSerif", Font.PLAIN, FONT_SIZE);
125     helper = new StateHelper(this);
126     helper.next(STATE_INIT);
127   }
128
129   protected void initSplashScreenWindow()
130   {
131     addMouseListener(closer);
132     waitForImages();
133     setLayout(new BorderLayout());
134     iframe = new JInternalFrame();
135     iframe.setFrameIcon(null);
136     iframe.setClosable(true);
137     iframe.setContentPane(this);
138     iframe.setLayer(JLayeredPane.PALETTE_LAYER);  
139     SplashImage splashimg = new SplashImage(image);
140     imgPanel.add(splashimg, BorderLayout.CENTER);
141     add(imgPanel, BorderLayout.NORTH);
142     Desktop.getDesktopPane().add(iframe);
143     refreshText();
144   }
145
146   /**
147    * Both Java and JavaScript have to wait for images, but this method will
148    * accomplish nothing for JavaScript. We have already taken care of image
149    * loading with our state loop in JavaScript.
150    * 
151    */
152   private void waitForImages()
153   {
154     if (Platform.isJS())
155       return;
156     MediaTracker mt = new MediaTracker(this);
157     mt.addImage(image, 0);
158     mt.addImage(logo, 1);
159     do
160     {
161       try
162       {
163         mt.waitForAll();
164       } catch (InterruptedException x)
165       {
166       }
167       if (mt.isErrorAny())
168       {
169         System.err.println("Error when loading images!");
170         break;
171       }
172     } while (!mt.checkAll());
173     if (logo != null)
174     {
175       Desktop.getInstance().setIconImage(logo);
176     }
177     this.setBackground(bg);
178     this.setForeground(fg);
179     this.setFont(font);
180   }
181
182   /**
183    * update text in author text panel reflecting current version information
184    */
185   protected boolean refreshText()
186   {
187     String newtext = Desktop.getInstance().getAboutMessage();
188     // System.err.println("Text found: \n"+newtext+"\nEnd of newtext.");
189     if (oldTextLength == newtext.length())
190     {
191       return false;
192     }
193   
194     iframe.setVisible(false);
195     oldTextLength = newtext.length();
196     if (Platform.isJS()) // BH 2019
197     {
198       /*
199        * SwingJS doesn't have HTMLEditorKit, required for a JTextPane
200        * to display formatted html, so we use a simple alternative
201        */
202       String text = "<html><br><img src=\""
203               + ChannelProperties.getImageURL("banner") + "\"/>" + newtext
204               + "<br></html>";
205       JLabel ta = new JLabel(text);
206       ta.setOpaque(true);
207       ta.setBackground(Color.white);
208       splashText = ta;
209     }
210     else
211     /**
212      * Java only
213      *
214      * @j2sIgnore
215      */
216     {
217       JTextPane jtp = new JTextPane();
218       jtp.setEditable(false);
219       jtp.setBackground(bg);
220       jtp.setForeground(fg);
221       jtp.setFont(font);
222       jtp.setContentType("text/html");
223       jtp.setText("<html>" + newtext + "</html>");
224       jtp.addHyperlinkListener(this);
225       splashText = jtp;
226     }
227     splashText.addMouseListener(closer);
228
229     splashText.setVisible(true);
230     splashText.setSize(new Dimension(750,
231             375 + logoSize + (Platform.isJS() ? 40 : 0)));
232     splashText.setBackground(bg);
233     splashText.setForeground(fg);
234     splashText.setFont(font);
235     add(splashText, BorderLayout.CENTER);
236     revalidate();
237     int width = Math.max(splashText.getWidth(), iconimg.getWidth());
238     int height = splashText.getHeight() + iconimg.getHeight();
239     iframe.setBounds((iframe.getParent().getWidth() - width) / 2,
240             (iframe.getParent().getHeight() - height) / 2, 750,
241             width,height);
242     iframe.validate();
243     iframe.setVisible(true);
244     return true;
245   }
246
247   protected void closeSplash()
248   {
249     try
250     {
251
252       iframe.setClosed(true);
253     } catch (Exception ex)
254     {
255     }
256   }
257
258   /**
259    * A simple state machine with just three states: init, loop, and done. Ideal
260    * for a simple while/sleep loop that works in Java and JavaScript
261    * identically.
262    * 
263    */
264   @Override
265   public boolean stateLoop()
266   {
267     while (true)
268     {
269       switch (helper.getState())
270       {
271       case STATE_INIT:
272         initSplashScreenWindow();
273         helper.setState(STATE_LOOP);
274         continue;
275       case STATE_LOOP:
276         if (!isVisible())
277         {
278           helper.setState(STATE_DONE);
279           continue;
280         }
281         if (refreshText())
282         {
283           iframe.repaint();
284         }
285         if (isStartup)
286           helper.delayedState(SHOW_FOR_SECS * 1000, STATE_DONE);
287         return true;
288       default:
289       case STATE_DONE:
290         setVisible(false);
291         closeSplash();
292         Desktop.getInstance().startDialogQueue();
293         return true;
294       }
295     }
296   }
297
298   private class SplashImage extends JPanel
299   {
300     Image image;
301
302     public SplashImage(Image todisplay)
303     {
304       image = todisplay;
305       if (image != null)
306       {
307         setPreferredSize(new Dimension(image.getWidth(this) + 8,
308                 image.getHeight(this)));
309       }
310     }
311
312     @Override
313     public Dimension getPreferredSize()
314     {
315       return new Dimension(image.getWidth(this) + 8, image.getHeight(this));
316     }
317
318     @Override
319     public void paintComponent(Graphics g)
320     {
321       g.setColor(bg);
322       g.fillRect(0, 0, getWidth(), getHeight());
323       g.setColor(fg);
324       g.setFont(new Font(font.getFontName(), Font.BOLD, FONT_SIZE + 6));
325
326       if (image != null)
327       {
328         g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
329                 (getHeight() - image.getHeight(this)) / 2, this);
330       }
331     }
332   }
333
334   @Override
335   public void hyperlinkUpdate(HyperlinkEvent e)
336   {
337     Desktop.hyperlinkUpdate(e);
338
339   }
340 }