001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.gui;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    import static org.openstreetmap.josm.tools.I18n.trn;
006    
007    import java.awt.Image;
008    import java.awt.Toolkit;
009    import java.awt.event.WindowAdapter;
010    import java.awt.event.WindowEvent;
011    import java.io.File;
012    import java.net.Authenticator;
013    import java.net.ProxySelector;
014    import java.net.URL;
015    import java.security.AllPermission;
016    import java.security.CodeSource;
017    import java.security.PermissionCollection;
018    import java.security.Permissions;
019    import java.security.Policy;
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.HashMap;
023    import java.util.LinkedList;
024    import java.util.List;
025    import java.util.Map;
026    
027    import javax.swing.JFrame;
028    import javax.swing.RepaintManager;
029    import javax.swing.SwingUtilities;
030    
031    import gnu.getopt.Getopt;
032    import gnu.getopt.LongOpt;
033    
034    import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
035    import org.openstreetmap.josm.Main;
036    import org.openstreetmap.josm.data.AutosaveTask;
037    import org.openstreetmap.josm.data.CustomConfigurator;
038    import org.openstreetmap.josm.data.Preferences;
039    import org.openstreetmap.josm.data.Version;
040    import org.openstreetmap.josm.gui.download.DownloadDialog;
041    import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
042    import org.openstreetmap.josm.gui.progress.ProgressMonitor;
043    import org.openstreetmap.josm.io.DefaultProxySelector;
044    import org.openstreetmap.josm.io.auth.CredentialsManager;
045    import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
046    import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
047    import org.openstreetmap.josm.plugins.PluginHandler;
048    import org.openstreetmap.josm.plugins.PluginInformation;
049    import org.openstreetmap.josm.tools.BugReportExceptionHandler;
050    import org.openstreetmap.josm.tools.I18n;
051    import org.openstreetmap.josm.tools.ImageProvider;
052    
053    /**
054     * Main window class application.
055     *
056     * @author imi
057     */
058    public class MainApplication extends Main {
059        /**
060         * Allow subclassing (see JOSM.java)
061         */
062        public MainApplication() {}
063    
064        /**
065         * Construct an main frame, ready sized and operating. Does not
066         * display the frame.
067         */
068        public MainApplication(JFrame mainFrame) {
069            super();
070            mainFrame.setContentPane(contentPanePrivate);
071            mainFrame.setJMenuBar(menu);
072            geometry.applySafe(mainFrame);
073            LinkedList<Image> l = new LinkedList<Image>();
074            l.add(ImageProvider.get("logo_16x16x32").getImage());
075            l.add(ImageProvider.get("logo_16x16x8").getImage());
076            l.add(ImageProvider.get("logo_32x32x32").getImage());
077            l.add(ImageProvider.get("logo_32x32x8").getImage());
078            l.add(ImageProvider.get("logo_48x48x32").getImage());
079            l.add(ImageProvider.get("logo_48x48x8").getImage());
080            l.add(ImageProvider.get("logo").getImage());
081            mainFrame.setIconImages(l);
082            mainFrame.addWindowListener(new WindowAdapter(){
083                @Override public void windowClosing(final WindowEvent arg0) {
084                    Main.exitJosm(true);
085                }
086            });
087            mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
088        }
089    
090        /**
091         * Displays help on the console
092         *
093         */
094        public static void showHelp() {
095            // TODO: put in a platformHook for system that have no console by default
096            System.out.println(tr("Java OpenStreetMap Editor")+" ["
097                    +Version.getInstance().getAgentString()+"]\n\n"+
098                    tr("usage")+":\n"+
099                    "\tjava -jar josm.jar <options>...\n\n"+
100                    tr("options")+":\n"+
101                    "\t--help|-h                                 "+tr("Show this help")+"\n"+
102                    "\t--geometry=widthxheight(+|-)x(+|-)y       "+tr("Standard unix geometry argument")+"\n"+
103                    "\t[--download=]minlat,minlon,maxlat,maxlon  "+tr("Download the bounding box")+"\n"+
104                    "\t[--download=]<URL>                        "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+"\n"+
105                    "\t[--download=]<filename>                   "+tr("Open a file (any file type that can be opened with File/Open)")+"\n"+
106                    "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+"\n"+
107                    "\t--downloadgps=<URL>                       "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+"\n"+
108                    "\t--selection=<searchstring>                "+tr("Select with the given search")+"\n"+
109                    "\t--[no-]maximize                           "+tr("Launch in maximized mode")+"\n"+
110                    "\t--reset-preferences                       "+tr("Reset the preferences to default")+"\n\n"+
111                    "\t--load-preferences=<url-to-xml>           "+tr("Changes preferences according to the XML file")+"\n\n"+
112                    "\t--set=<key>=<value>                       "+tr("Set preference key to value")+"\n\n"+
113                    "\t--language=<language>                     "+tr("Set the language")+"\n\n"+
114                    "\t--version                                 "+tr("Displays the JOSM version and exits")+"\n\n"+
115                    tr("options provided as Java system properties")+":\n"+
116                    "\t-Djosm.home="+tr("/PATH/TO/JOSM/FOLDER/         ")+tr("Change the folder for all user settings")+"\n\n"+
117                    tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
118                            "      Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
119                            "\t-Xmx...m\n\n"+
120                            tr("examples")+":\n"+
121                            "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
122                            "\tjava -jar josm.jar http://www.openstreetmap.org/index.html?lat=43.2&lon=11.1&zoom=13\n"+
123                            "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
124                            "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
125                            "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
126                            "\tjava -Xmx400m -jar josm.jar\n\n"+
127                            tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+"\n"+
128                            tr("Make sure you load some data if you use --selection.")+"\n"
129                    );
130        }
131    
132        public enum Option {
133            HELP(false),
134            VERSION(false),
135            LANGUAGE(true),
136            RESET_PREFERENCES(false),
137            LOAD_PREFERENCES(true),
138            SET(true),
139            GEOMETRY(true),
140            NO_MAXIMIZE(false),
141            MAXIMIZE(false),
142            DOWNLOAD(true),
143            DOWNLOADGPS(true),
144            SELECTION(true);
145    
146            private String name;
147            private boolean requiresArgument;
148    
149            private Option(boolean requiresArgument) {
150                this.name = name().toLowerCase().replace("_", "-");
151                this.requiresArgument = requiresArgument;
152            }
153    
154            public String getName() {
155                return name;
156            }
157    
158            public boolean requiresArgument() {
159                return requiresArgument;
160            }
161    
162            public static Map<Option, Collection<String>> fromStringMap(Map<String, Collection<String>> opts) {
163                Map<Option, Collection<String>> res = new HashMap<Option, Collection<String>>();
164                for (Map.Entry<String, Collection<String>> e : opts.entrySet()) {
165                    Option o = Option.valueOf(e.getKey().toUpperCase().replace("-", "_"));
166                    if (o != null) {
167                        res.put(o, e.getValue());
168                    }
169                }
170                return res;
171            }
172        }
173    
174        private static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) {
175    
176            List<LongOpt> los = new ArrayList<LongOpt>();
177            for (Option o : Option.values()) {
178                los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));
179            }
180    
181            Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[0]));
182    
183            Map<Option, Collection<String>> argMap = new HashMap<Option, Collection<String>>();
184    
185            int c;
186            while ((c = g.getopt()) != -1 ) {
187                Option opt = null;
188                switch (c) {
189                    case 'h':
190                        opt = Option.HELP;
191                        break;
192                    case 'v':
193                        opt = Option.VERSION;
194                        break;
195                    case 0:
196                        opt = Option.values()[g.getLongind()];
197                        break;
198                }
199                if (opt != null) {
200                    Collection<String> values = argMap.get(opt);
201                    if (values == null) {
202                        values = new ArrayList<String>();
203                        argMap.put(opt, values);
204                    }
205                    values.add(g.getOptarg());
206                } else
207                    throw new IllegalArgumentException();
208            }
209            // positional arguments are a shortcut for the --download ... option
210            for (int i = g.getOptind(); i < args.length; ++i) {
211                Collection<String> values = argMap.get(Option.DOWNLOAD);
212                if (values == null) {
213                    values = new ArrayList<String>();
214                    argMap.put(Option.DOWNLOAD, values);
215                }
216                values.add(args[i]);
217            }
218    
219            return argMap;
220        }
221    
222        /**
223         * Main application Startup
224         */
225        public static void main(final String[] argArray) {
226            I18n.init();
227            Main.checkJava6();
228            Main.pref = new Preferences();
229    
230            Policy.setPolicy(new Policy() {
231                // Permissions for plug-ins loaded when josm is started via webstart
232                private PermissionCollection pc;
233    
234                {
235                    pc = new Permissions();
236                    pc.add(new AllPermission());
237                }
238    
239                @Override
240                public void refresh() { }
241    
242                @Override
243                public PermissionCollection getPermissions(CodeSource codesource) {
244                    return pc;
245                }
246            });
247    
248            Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
249            // http://stuffthathappens.com/blog/2007/10/15/one-more-note-on-uncaught-exception-handlers/
250            System.setProperty("sun.awt.exception.handler", BugReportExceptionHandler.class.getName());
251    
252            // initialize the platform hook, and
253            Main.determinePlatformHook();
254            // call the really early hook before we do anything else
255            Main.platform.preStartupHook();
256    
257            // construct argument table
258            Map<Option, Collection<String>> args = null;
259            try {
260                args = buildCommandLineArgumentMap(argArray);
261            } catch (IllegalArgumentException e) {
262                System.exit(1);
263            }
264    
265            if (args.containsKey(Option.VERSION)) {
266                System.out.println(Version.getInstance().getAgentString());
267                System.exit(0);
268            } //else {
269            //    System.out.println(Version.getInstance().getReleaseAttributes());
270            //}
271    
272            Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
273    
274            // Check if passed as parameter
275            if (args.containsKey(Option.LANGUAGE)) {
276                I18n.set(args.get(Option.LANGUAGE).iterator().next());
277            } else {
278                I18n.set(Main.pref.get("language", null));
279            }
280            Main.pref.updateSystemProperties();
281    
282            JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
283            Main.parent = mainFrame;
284    
285            if (args.containsKey(Option.LOAD_PREFERENCES)) {
286                CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
287                for (String i : args.get(Option.LOAD_PREFERENCES)) {
288                    System.out.println("Reading preferences from " + i);
289                    try {
290                        URL url = new URL(i);
291                        config.openAndReadXML(url.openStream());
292                    } catch (Exception ex) {
293                        throw new RuntimeException(ex);
294                    }
295                }
296            }
297    
298            if (args.containsKey(Option.SET)) {
299                for (String i : args.get(Option.SET)) {
300                    String[] kv = i.split("=", 2);
301                    Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
302                }
303            }
304    
305            DefaultAuthenticator.createInstance();
306            Authenticator.setDefault(DefaultAuthenticator.getInstance());
307            ProxySelector.setDefault(new DefaultProxySelector(ProxySelector.getDefault()));
308            OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
309    
310            // asking for help? show help and exit
311            if (args.containsKey(Option.HELP)) {
312                showHelp();
313                System.exit(0);
314            }
315    
316            SplashScreen splash = new SplashScreen();
317            final ProgressMonitor monitor = splash.getProgressMonitor();
318            monitor.beginTask(tr("Initializing"));
319            splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
320            Main.setInitStatusListener(new InitStatusListener() {
321    
322                @Override
323                public void updateStatus(String event) {
324                    monitor.indeterminateSubTask(event);
325                }
326            });
327    
328            List<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false));
329            if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
330                monitor.subTask(tr("Updating plugins"));
331                pluginsToLoad = PluginHandler.updatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
332            }
333    
334            monitor.indeterminateSubTask(tr("Installing updated plugins"));
335            PluginHandler.installDownloadedPlugins(true);
336    
337            monitor.indeterminateSubTask(tr("Loading early plugins"));
338            PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
339    
340            monitor.indeterminateSubTask(tr("Setting defaults"));
341            preConstructorInit(args);
342    
343            monitor.indeterminateSubTask(tr("Creating main GUI"));
344            Main.addListener();
345            final Main main = new MainApplication(mainFrame);
346    
347            monitor.indeterminateSubTask(tr("Loading plugins"));
348            PluginHandler.loadLatePlugins(splash,pluginsToLoad,  monitor.createSubTaskMonitor(1, false));
349            toolbar.refreshToolbarControl();
350            splash.setVisible(false);
351            splash.dispose();
352            mainFrame.setVisible(true);
353    
354            boolean maximized = Boolean.parseBoolean(Main.pref.get("gui.maximized"));
355            if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
356                if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
357                    // Main.debug("Main window maximized");
358                    Main.windowState = JFrame.MAXIMIZED_BOTH;
359                    mainFrame.setExtendedState(Main.windowState);
360                } else {
361                    Main.debug("Main window: maximizing not supported");
362                }
363            } else {
364                // Main.debug("Main window not maximized");
365            }
366            if(main.menu.fullscreenToggleAction != null) {
367                main.menu.fullscreenToggleAction.initial();
368            }
369    
370            final Map<Option, Collection<String>> args_final = args;
371    
372            SwingUtilities.invokeLater(new Runnable() {
373                public void run() {
374                    if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
375                        AutosaveTask autosaveTask = new AutosaveTask();
376                        List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
377                        if (!unsavedLayerFiles.isEmpty()) {
378                            ExtendedDialog dialog = new ExtendedDialog(
379                                    Main.parent,
380                                    tr("Unsaved osm data"),
381                                    new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
382                                    );
383                            dialog.setContent(
384                                    trn("JOSM found {0} unsaved osm data layer. ",
385                                            "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
386                                            tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
387                            dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/remove"});
388                            int selection = dialog.showDialog().getValue();
389                            if (selection == 1) {
390                                autosaveTask.recoverUnsavedLayers();
391                            } else if (selection == 3) {
392                                autosaveTask.dicardUnsavedLayers();
393                            }
394                        }
395                        autosaveTask.schedule();
396                    }
397    
398                    main.postConstructorProcessCmdLine(args_final);
399    
400                    DownloadDialog.autostartIfNeeded();
401                }
402            });
403    
404            if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
405                RemoteControl.start();
406            }
407    
408            if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
409                // Repaint manager is registered so late for a reason - there is lots of violation during startup process but they don't seem to break anything and are difficult to fix
410                System.out.println("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
411                RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
412            }
413        }
414    }