JAL-3247 Code changes to getdown. Double click open of .jvp files in Windows and...
[jalview.git] / getdown / src / getdown / launcher / src / main / java / com / threerings / getdown / launcher / GetdownApp.java
1 //
2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
5
6 package com.threerings.getdown.launcher;
7
8 import java.awt.Color;
9 import java.awt.Container;
10 import java.awt.EventQueue;
11 import java.awt.Image;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.KeyEvent;
14 import java.awt.event.WindowAdapter;
15 import java.awt.event.WindowEvent;
16 import java.io.BufferedOutputStream;
17 import java.io.File;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import javax.swing.AbstractAction;
25 import javax.swing.JComponent;
26 import javax.swing.JFrame;
27 import javax.swing.KeyStroke;
28 import javax.swing.WindowConstants;
29
30 import com.install4j.api.launcher.StartupNotification;
31 import com.samskivert.swing.util.SwingUtil;
32 import com.threerings.getdown.data.EnvConfig;
33 import com.threerings.getdown.data.SysProps;
34 import com.threerings.getdown.util.LaunchUtil;
35 import com.threerings.getdown.util.StringUtil;
36 import static com.threerings.getdown.Log.log;
37
38 /**
39  * The main application entry point for Getdown.
40  */
41 public class GetdownApp
42 {
43   public static String startupFilesParameterString = "";
44   /**
45    * The main entry point of the Getdown launcher application.
46    */
47   public static void main (String[] argv) {
48     try {
49       start(argv);
50     } catch (Exception e) {
51       log.warning("main() failed.", e);
52     }
53   }
54
55   /**
56    * Runs Getdown as an application, using the arguments supplie as {@code argv}.
57    * @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
58    * called on it.
59    * @throws Exception if anything goes wrong starting Getdown.
60    */
61   public static Getdown start (String[] argv) throws Exception {
62     List<EnvConfig.Note> notes = new ArrayList<>();
63     EnvConfig envc = EnvConfig.create(argv, notes);
64     if (envc == null) {
65       if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
66       else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
67       System.exit(-1);
68     }
69
70     // pipe our output into a file in the application directory
71     if (!SysProps.noLogRedir()) {
72       File logFile = new File(envc.appDir, "launcher.log");
73       try {
74         PrintStream logOut = new PrintStream(
75                 new BufferedOutputStream(new FileOutputStream(logFile)), true);
76         System.setOut(logOut);
77         System.setErr(logOut);
78       } catch (IOException ioe) {
79         log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
80       }
81     }
82
83     // report any notes from reading our env config, and abort if necessary
84     boolean abort = false;
85     for (EnvConfig.Note note : notes) {
86       switch (note.level) {
87       case INFO: log.info(note.message); break;
88       case WARN: log.warning(note.message); break;
89       case ERROR: log.error(note.message); abort = true; break;
90       }
91     }
92     if (abort) System.exit(-1);
93
94     try
95     {
96       StartupNotification.registerStartupListener(
97               new StartupNotification.Listener() {
98                 @Override
99                 public void startupPerformed(String parameters)
100                 {
101                   GetdownApp.setStartupFilesParameterString(parameters);
102                 }
103               });
104     } catch (Exception e)
105     {
106       e.printStackTrace();
107     }
108     
109     //Thread.sleep(200);
110
111     // record a few things for posterity
112     log.info("------------------ VM Info ------------------");
113     log.info("-- OS Name: " + System.getProperty("os.name"));
114     log.info("-- OS Arch: " + System.getProperty("os.arch"));
115     log.info("-- OS Vers: " + System.getProperty("os.version"));
116     log.info("-- Java Vers: " + System.getProperty("java.version"));
117     log.info("-- Java Home: " + System.getProperty("java.home"));
118     log.info("-- User Name: " + System.getProperty("user.name"));
119     log.info("-- User Home: " + System.getProperty("user.home"));
120     log.info("-- Cur dir: " + System.getProperty("user.dir"));
121     log.info("-- startupFilesParameterString: " + startupFilesParameterString);
122     log.info("---------------------------------------------");
123
124     Getdown app = new Getdown(envc) {
125       @Override
126       protected Container createContainer () {
127         // create our user interface, and display it
128         if (_frame == null) {
129           _frame = new JFrame("");
130           _frame.addWindowListener(new WindowAdapter() {
131             @Override
132             public void windowClosing (WindowEvent evt) {
133               handleWindowClose();
134             }
135           });
136           // handle close on ESC
137           String cancelId = "Cancel"; // $NON-NLS-1$
138           _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
139                   KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
140           _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
141             public void actionPerformed (ActionEvent e) {
142               handleWindowClose();
143             }
144           });
145           // this cannot be called in configureContainer as it is only allowed before the
146           // frame has been displayed for the first time
147           _frame.setUndecorated(_ifc.hideDecorations);
148           _frame.setResizable(false);
149         } else {
150           _frame.getContentPane().removeAll();
151         }
152         _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
153         return _frame.getContentPane();
154       }
155
156       @Override
157       protected void configureContainer () {
158         if (_frame == null) return;
159
160         _frame.setTitle(_ifc.name);
161
162         try {
163           _frame.setBackground(new Color(_ifc.background, true));
164         } catch (Exception e) {
165           log.warning("Failed to set background", "bg", _ifc.background, e);
166         }
167
168         if (_ifc.iconImages != null) {
169           ArrayList<Image> icons = new ArrayList<>();
170           for (String path : _ifc.iconImages) {
171             Image img = loadImage(path);
172             if (img == null) {
173               log.warning("Error loading icon image", "path", path);
174             } else {
175               icons.add(img);
176             }
177           }
178           if (icons.isEmpty()) {
179             log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
180           } else {
181             _frame.setIconImages(icons);
182           }
183         }
184       }
185
186       @Override
187       protected void showContainer () {
188         if (_frame != null) {
189           _frame.pack();
190           SwingUtil.centerWindow(_frame);
191           _frame.setVisible(true);
192         }
193       }
194
195       @Override
196       protected void disposeContainer () {
197         if (_frame != null) {
198           _frame.dispose();
199           _frame = null;
200         }
201       }
202
203       @Override
204       protected void showDocument (String url) {
205         if (!StringUtil.couldBeValidUrl(url)) {
206           // command injection would be possible if we allowed e.g. spaces and double quotes
207           log.warning("Invalid document URL.", "url", url);
208           return;
209         }
210         String[] cmdarray;
211         if (LaunchUtil.isWindows()) {
212           String osName = System.getProperty("os.name", "");
213           if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
214             cmdarray = new String[] {
215                 "command.com", "/c", "start", "\"" + url + "\"" };
216           } else {
217             cmdarray = new String[] {
218                 "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
219           }
220         } else if (LaunchUtil.isMacOS()) {
221           cmdarray = new String[] { "open", url };
222         } else { // Linux, Solaris, etc.
223           cmdarray = new String[] { "firefox", url };
224         }
225         try {
226           Runtime.getRuntime().exec(cmdarray);
227         } catch (Exception e) {
228           log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
229         }
230       }
231
232       @Override
233       protected void exit (int exitCode) {
234         // if we're running the app in the same JVM, don't call System.exit, but do
235         // make double sure that the download window is closed.
236         if (invokeDirect()) {
237           disposeContainer();
238         } else {
239           System.exit(exitCode);
240         }
241       }
242
243       @Override
244       protected void fail (String message) {
245         super.fail(message);
246         // super.fail causes the UI to be created (if needed) on the next UI tick, so we
247         // want to wait until that happens before we attempt to redecorate the window
248         EventQueue.invokeLater(new Runnable() {
249           @Override
250           public void run() {
251             // if the frame was set to be undecorated, make window decoration available
252             // to allow the user to close the window
253             if (_frame != null && _frame.isUndecorated()) {
254               _frame.dispose();
255               Color bg = _frame.getBackground();
256               if (bg != null && bg.getAlpha() < 255) {
257                 // decorated windows do not allow alpha backgrounds
258                 _frame.setBackground(
259                         new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
260               }
261               _frame.setUndecorated(false);
262               showContainer();
263             }
264           }
265         });
266       }
267
268       protected JFrame _frame;
269     };
270     if (getStartupFilesParameterString() != null) {
271       app.setStartupFilesFromParameterString(getStartupFilesParameterString());
272     }
273     app.start();
274     return app;
275   }
276   
277   public static void setStartupFilesParameterString(String parameters) {
278     startupFilesParameterString = parameters;
279   }
280   
281   public static String getStartupFilesParameterString() {
282     return startupFilesParameterString;
283   }
284 }