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 Main.MasterWindowListener.setup();
354
355 boolean maximized = Boolean.parseBoolean(Main.pref.get("gui.maximized"));
356 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
357 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
358 // Main.debug("Main window maximized");
359 Main.windowState = JFrame.MAXIMIZED_BOTH;
360 mainFrame.setExtendedState(Main.windowState);
361 } else {
362 Main.debug("Main window: maximizing not supported");
363 }
364 } else {
365 // Main.debug("Main window not maximized");
366 }
367 if(main.menu.fullscreenToggleAction != null) {
368 main.menu.fullscreenToggleAction.initial();
369 }
370
371 final Map<Option, Collection<String>> args_final = args;
372
373 SwingUtilities.invokeLater(new Runnable() {
374 public void run() {
375 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
376 AutosaveTask autosaveTask = new AutosaveTask();
377 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
378 if (!unsavedLayerFiles.isEmpty()) {
379 ExtendedDialog dialog = new ExtendedDialog(
380 Main.parent,
381 tr("Unsaved osm data"),
382 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
383 );
384 dialog.setContent(
385 trn("JOSM found {0} unsaved osm data layer. ",
386 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
387 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
388 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/remove"});
389 int selection = dialog.showDialog().getValue();
390 if (selection == 1) {
391 autosaveTask.recoverUnsavedLayers();
392 } else if (selection == 3) {
393 autosaveTask.dicardUnsavedLayers();
394 }
395 }
396 autosaveTask.schedule();
397 }
398
399 main.postConstructorProcessCmdLine(args_final);
400
401 DownloadDialog.autostartIfNeeded();
402 }
403 });
404
405 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
406 RemoteControl.start();
407 }
408
409 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
410 // 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
411 System.out.println("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
412 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
413 }
414 }
415 }