source: josm/trunk/src/org/openstreetmap/josm/Main.java@ 2491

Last change on this file since 2491 was 2485, checked in by jttt, 14 years ago

Update relation list dialog only when dialog is visible (not even collapsed), call keysChangedImpl in OsmPrimitive when keys are changed

  • Property svn:eol-style set to native
File size: 21.0 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm;
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.BorderLayout;
6import java.awt.Component;
7import java.awt.Dimension;
8import java.awt.Rectangle;
9import java.awt.Toolkit;
10import java.awt.event.KeyEvent;
11import java.io.File;
12import java.io.IOException;
13import java.net.URI;
14import java.net.URISyntaxException;
15import java.util.ArrayList;
16import java.util.Collection;
17import java.util.List;
18import java.util.Map;
19import java.util.StringTokenizer;
20import java.util.concurrent.ExecutorService;
21import java.util.concurrent.Executors;
22import java.util.concurrent.Future;
23import java.util.regex.Matcher;
24import java.util.regex.Pattern;
25
26import javax.swing.JComponent;
27import javax.swing.JFrame;
28import javax.swing.JOptionPane;
29import javax.swing.JPanel;
30import javax.swing.UIManager;
31
32import org.openstreetmap.josm.actions.OpenFileAction;
33import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
34import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
35import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
36import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
37import org.openstreetmap.josm.actions.mapmode.MapMode;
38import org.openstreetmap.josm.actions.search.SearchAction;
39import org.openstreetmap.josm.data.Bounds;
40import org.openstreetmap.josm.data.Preferences;
41import org.openstreetmap.josm.data.UndoRedoHandler;
42import org.openstreetmap.josm.data.coor.CoordinateFormat;
43import org.openstreetmap.josm.data.coor.LatLon;
44import org.openstreetmap.josm.data.osm.DataSet;
45import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
46import org.openstreetmap.josm.data.projection.Projection;
47import org.openstreetmap.josm.gui.GettingStarted;
48import org.openstreetmap.josm.gui.MainMenu;
49import org.openstreetmap.josm.gui.MapFrame;
50import org.openstreetmap.josm.gui.SplashScreen;
51import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
52import org.openstreetmap.josm.gui.help.HelpBrowserProxy;
53import org.openstreetmap.josm.gui.io.SaveLayersDialog;
54import org.openstreetmap.josm.gui.layer.Layer;
55import org.openstreetmap.josm.gui.layer.OsmDataLayer;
56import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
57import org.openstreetmap.josm.gui.preferences.MapPaintPreference;
58import org.openstreetmap.josm.gui.preferences.ProjectionPreference;
59import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
60import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
61import org.openstreetmap.josm.io.IllegalDataException;
62import org.openstreetmap.josm.plugins.PluginHandler;
63import org.openstreetmap.josm.tools.ImageProvider;
64import org.openstreetmap.josm.tools.OsmUrlToBounds;
65import org.openstreetmap.josm.tools.PlatformHook;
66import org.openstreetmap.josm.tools.PlatformHookOsx;
67import org.openstreetmap.josm.tools.PlatformHookUnixoid;
68import org.openstreetmap.josm.tools.PlatformHookWindows;
69import org.openstreetmap.josm.tools.Shortcut;
70
71abstract public class Main {
72
73 /**
74 * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if
75 * it only shows the MOTD panel.
76 *
77 * @return true if JOSM currently displays a map view
78 */
79 static public boolean isDisplayingMapView() {
80 if (map == null) return false;
81 if (map.mapView == null) return false;
82 return true;
83 }
84 /**
85 * Global parent component for all dialogs and message boxes
86 */
87 public static Component parent;
88 /**
89 * Global application.
90 */
91 public static Main main;
92 /**
93 * The worker thread slave. This is for executing all long and intensive
94 * calculations. The executed runnables are guaranteed to be executed separately
95 * and sequential.
96 */
97 public final static ExecutorService worker = Executors.newSingleThreadExecutor();
98 /**
99 * Global application preferences
100 */
101 public static Preferences pref = new Preferences();
102
103 /**
104 * The global paste buffer.
105 */
106 public static PrimitiveDeepCopy pasteBuffer = new PrimitiveDeepCopy();
107 public static Layer pasteSource;
108 /**
109 * The projection method used.
110 */
111 public static Projection proj;
112 /**
113 * The MapFrame. Use setMapFrame to set or clear it.
114 */
115 public static MapFrame map;
116 /**
117 * The dialog that gets displayed during background task execution.
118 */
119 //public static PleaseWaitDialog pleaseWaitDlg;
120
121 /**
122 * True, when in applet mode
123 */
124 public static boolean applet = false;
125
126 /**
127 * The toolbar preference control to register new actions.
128 */
129 public static ToolbarPreferences toolbar;
130
131
132 public UndoRedoHandler undoRedo = new UndoRedoHandler();
133
134 /**
135 * The main menu bar at top of screen.
136 */
137 public final MainMenu menu;
138
139 /**
140 * The MOTD Layer.
141 */
142 private GettingStarted gettingStarted=new GettingStarted();
143
144 /**
145 * Print a debug message if debugging is on.
146 */
147 static public int debug_level = 1;
148 static public final void debug(String msg) {
149 if (debug_level <= 0)
150 return;
151 System.out.println(msg);
152 }
153
154 /**
155 * Platform specific code goes in here.
156 * Plugins may replace it, however, some hooks will be called before any plugins have been loeaded.
157 * So if you need to hook into those early ones, split your class and send the one with the early hooks
158 * to the JOSM team for inclusion.
159 */
160 public static PlatformHook platform;
161
162 /**
163 * Set or clear (if passed <code>null</code>) the map.
164 */
165 public final void setMapFrame(final MapFrame map) {
166 MapFrame old = Main.map;
167 panel.setVisible(false);
168 panel.removeAll();
169 if (map != null) {
170 map.fillPanel(panel);
171 } else {
172 old.destroy();
173 panel.add(gettingStarted, BorderLayout.CENTER);
174 }
175 panel.setVisible(true);
176 redoUndoListener.commandChanged(0,0);
177
178 Main.map = map;
179
180 PluginHandler.setMapFrame(old, map);
181 }
182
183 /**
184 * Remove the specified layer from the map. If it is the last layer,
185 * remove the map as well.
186 */
187 public final void removeLayer(final Layer layer) {
188 if (map != null) {
189 map.mapView.removeLayer(layer);
190 if (map.mapView.getAllLayers().isEmpty()) {
191 setMapFrame(null);
192 }
193 }
194 }
195
196 public Main() {
197 this(null);
198 }
199
200 public Main(SplashScreen splash) {
201 main = this;
202 // platform = determinePlatformHook();
203 platform.startupHook();
204 contentPane.add(panel, BorderLayout.CENTER);
205 panel.add(gettingStarted, BorderLayout.CENTER);
206
207 if(splash != null) {
208 splash.setStatus(tr("Creating main GUI"));
209 }
210 menu = new MainMenu();
211
212 undoRedo.listenerCommands.add(redoUndoListener);
213
214 // creating toolbar
215 contentPane.add(toolbar.control, BorderLayout.NORTH);
216
217 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
218 .put(Shortcut.registerShortcut("system:help", tr("Help"),
219 KeyEvent.VK_F1, Shortcut.GROUP_DIRECT).getKeyStroke(), "Help");
220 contentPane.getActionMap().put("Help", menu.help);
221
222 TaggingPresetPreference.initialize();
223 MapPaintPreference.initialize();
224
225 toolbar.refreshToolbarControl();
226
227 toolbar.control.updateUI();
228 contentPane.updateUI();
229 }
230
231 /**
232 * Add a new layer to the map. If no map exists, create one.
233 */
234 public final void addLayer(final Layer layer) {
235 if (map == null) {
236 final MapFrame mapFrame = new MapFrame();
237 setMapFrame(mapFrame);
238 mapFrame.selectMapMode((MapMode)mapFrame.getDefaultButtonAction());
239 mapFrame.setVisible(true);
240 mapFrame.initializeDialogsPane();
241 // bootstrapping problem: make sure the layer list dialog is going to
242 // listen to change events of the very first layer
243 //
244 layer.addPropertyChangeListener(LayerListDialog.getInstance().getModel());
245 }
246 map.mapView.addLayer(layer);
247 }
248
249 /**
250 * Replies true if there is an edit layer
251 *
252 * @return true if there is an edit layer
253 */
254 public boolean hasEditLayer() {
255 if (map == null) return false;
256 if (map.mapView == null) return false;
257 if (map.mapView.getEditLayer() == null) return false;
258 return true;
259 }
260
261 /**
262 * Replies the current edit layer
263 *
264 * @return the current edit layer. null, if no current edit layer exists
265 */
266 public OsmDataLayer getEditLayer() {
267 if (map == null) return null;
268 if (map.mapView == null) return null;
269 return map.mapView.getEditLayer();
270 }
271
272 /**
273 * Replies the current data set.
274 *
275 * @return the current data set. null, if no current data set exists
276 */
277 public DataSet getCurrentDataSet() {
278 if (!hasEditLayer()) return null;
279 return getEditLayer().data;
280 }
281
282 /**
283 * Use this to register shortcuts to
284 */
285 public static final JPanel contentPane = new JPanel(new BorderLayout());
286
287 ///////////////////////////////////////////////////////////////////////////
288 // Implementation part
289 ///////////////////////////////////////////////////////////////////////////
290
291 public static JPanel panel = new JPanel(new BorderLayout());
292
293 protected static Rectangle bounds;
294
295 private final CommandQueueListener redoUndoListener = new CommandQueueListener(){
296 public void commandChanged(final int queueSize, final int redoSize) {
297 menu.undo.setEnabled(queueSize > 0);
298 menu.redo.setEnabled(redoSize > 0);
299 }
300 };
301
302 /**
303 * Should be called before the main constructor to setup some parameter stuff
304 * @param args The parsed argument list.
305 */
306 public static void preConstructorInit(Map<String, Collection<String>> args) {
307 ProjectionPreference.setProjection();
308
309 try {
310 try {
311 String laf = Main.pref.get("laf", platform.getDefaultStyle());
312 if(laf != null && laf.length() > 0) {
313 UIManager.setLookAndFeel(laf);
314 }
315 }
316 catch (final javax.swing.UnsupportedLookAndFeelException e) {
317 System.out.println("Look and Feel not supported: " + Main.pref.get("laf"));
318 }
319 toolbar = new ToolbarPreferences();
320 contentPane.updateUI();
321 panel.updateUI();
322 } catch (final Exception e) {
323 e.printStackTrace();
324 }
325 UIManager.put("OptionPane.okIcon", ImageProvider.get("ok"));
326 UIManager.put("OptionPane.yesIcon", UIManager.get("OptionPane.okIcon"));
327 UIManager.put("OptionPane.cancelIcon", ImageProvider.get("cancel"));
328 UIManager.put("OptionPane.noIcon", UIManager.get("OptionPane.cancelIcon"));
329
330 // init default coordinate format
331 //
332 try {
333 //CoordinateFormat format = CoordinateFormat.valueOf(Main.pref.get("coordinates"));
334 CoordinateFormat.setCoordinateFormat(CoordinateFormat.valueOf(Main.pref.get("coordinates")));
335 } catch (IllegalArgumentException iae) {
336 CoordinateFormat.setCoordinateFormat(CoordinateFormat.DECIMAL_DEGREES);
337 }
338
339
340 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
341 String geometry = Main.pref.get("gui.geometry");
342 if (args.containsKey("geometry")) {
343 geometry = args.get("geometry").iterator().next();
344 }
345 if (geometry.length() != 0) {
346 final Matcher m = Pattern.compile("(\\d+)x(\\d+)(([+-])(\\d+)([+-])(\\d+))?").matcher(geometry);
347 if (m.matches()) {
348 int w = Integer.valueOf(m.group(1));
349 int h = Integer.valueOf(m.group(2));
350 int x = 0, y = 0;
351 if (m.group(3) != null) {
352 x = Integer.valueOf(m.group(5));
353 y = Integer.valueOf(m.group(7));
354 if (m.group(4).equals("-")) {
355 x = screenDimension.width - x - w;
356 }
357 if (m.group(6).equals("-")) {
358 y = screenDimension.height - y - h;
359 }
360 }
361 bounds = new Rectangle(x,y,w,h);
362 if(!Main.pref.get("gui.geometry").equals(geometry)) {
363 // remember this geometry
364 Main.pref.put("gui.geometry", geometry);
365 }
366 } else {
367 System.out.println("Ignoring malformed geometry: "+geometry);
368 }
369 }
370 if (bounds == null) {
371 bounds = !args.containsKey("no-maximize") ? new Rectangle(0,0,screenDimension.width,screenDimension.height) : new Rectangle(1000,740);
372 }
373 }
374
375 public void postConstructorProcessCmdLine(Map<String, Collection<String>> args) {
376 if (args.containsKey("download")) {
377 for (String s : args.get("download")) {
378 downloadFromParamString(false, s);
379 }
380 }
381 if (args.containsKey("downloadgps")) {
382 for (String s : args.get("downloadgps")) {
383 downloadFromParamString(true, s);
384 }
385 }
386 if (args.containsKey("selection")) {
387 for (String s : args.get("selection")) {
388 SearchAction.search(s, SearchAction.SearchMode.add, false, false);
389 }
390 }
391 }
392
393 /**
394 * Run any cleanup operation before exit
395 *
396 */
397 public static void cleanupBeforeExit() {
398 // try to close and exit the help browser running in another process
399 //
400 HelpBrowserProxy.getInstance().exit();
401 }
402
403 public static boolean saveUnsavedModifications() {
404 if (map == null) return true;
405 SaveLayersDialog dialog = new SaveLayersDialog(Main.parent);
406 List<OsmDataLayer> layersWithUnmodifiedChanges = new ArrayList<OsmDataLayer>();
407 for (OsmDataLayer l: Main.map.mapView.getLayersOfType(OsmDataLayer.class)) {
408 if (l.requiresSaveToFile() || l.requiresUploadToServer()) {
409 layersWithUnmodifiedChanges.add(l);
410 }
411 }
412 dialog.prepareForSavingAndUpdatingLayersBeforeExit();
413 if (!layersWithUnmodifiedChanges.isEmpty()) {
414 dialog.getModel().populate(layersWithUnmodifiedChanges);
415 dialog.setVisible(true);
416 switch(dialog.getUserAction()) {
417 case CANCEL: return false;
418 case PROCEED: return true;
419 default: return false;
420 }
421 }
422
423 return true;
424 }
425
426 private static void downloadFromParamString(final boolean rawGps, String s) {
427 if (s.startsWith("http:")) {
428 final Bounds b = OsmUrlToBounds.parse(s);
429 if (b == null) {
430 JOptionPane.showMessageDialog(
431 Main.parent,
432 tr("Ignoring malformed URL: \"{0}\"", s),
433 tr("Warning"),
434 JOptionPane.WARNING_MESSAGE
435 );
436 } else {
437 //DownloadTask osmTask = main.menu.download.downloadTasks.get(0);
438 DownloadTask osmTask = new DownloadOsmTask();
439 Future<?> future = osmTask.download(true, b, null);
440 Main.worker.submit(new PostDownloadHandler(osmTask, future));
441 }
442 return;
443 }
444
445 if (s.startsWith("file:")) {
446 File f = null;
447 try {
448 f = new File(new URI(s));
449 } catch (URISyntaxException e) {
450 JOptionPane.showMessageDialog(
451 Main.parent,
452 tr("Ignoring malformed file URL: \"{0}\"", s),
453 tr("Warning"),
454 JOptionPane.WARNING_MESSAGE
455 );
456 }
457 try {
458 if (f!=null) {
459 OpenFileAction.openFile(f);
460 }
461 } catch(IllegalDataException e) {
462 e.printStackTrace();
463 JOptionPane.showMessageDialog(
464 Main.parent,
465 tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
466 tr("Error"),
467 JOptionPane.ERROR_MESSAGE
468 );
469 }catch(IOException e) {
470 e.printStackTrace();
471 JOptionPane.showMessageDialog(
472 Main.parent,
473 tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
474 tr("Error"),
475 JOptionPane.ERROR_MESSAGE
476 );
477 }
478 return;
479 }
480
481 final StringTokenizer st = new StringTokenizer(s, ",");
482 if (st.countTokens() == 4) {
483 Bounds b = new Bounds(
484 new LatLon(Double.parseDouble(st.nextToken()),Double.parseDouble(st.nextToken())),
485 new LatLon(Double.parseDouble(st.nextToken()),Double.parseDouble(st.nextToken()))
486 );
487 try {
488 DownloadTask task = rawGps ? new DownloadGpsTask() : new DownloadOsmTask();
489 // asynchronously launch the download task ...
490 Future<?> future = task.download(true, b, null);
491 // ... and the continuation when the download is finished (this will wait for the download to finish)
492 Main.worker.execute(new PostDownloadHandler(task, future));
493 return;
494 } catch (final NumberFormatException e) {
495 }
496 }
497 File f = new File(s);
498 try {
499 OpenFileAction.openFile(f);
500 }catch(IllegalDataException e) {
501 e.printStackTrace();
502 JOptionPane.showMessageDialog(
503 Main.parent,
504 tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
505 tr("Error"),
506 JOptionPane.ERROR_MESSAGE
507 );
508 }catch(IOException e) {
509 e.printStackTrace();
510 JOptionPane.showMessageDialog(
511 Main.parent,
512 tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
513 tr("Error"),
514 JOptionPane.ERROR_MESSAGE
515 );
516 }
517 }
518
519 public static void determinePlatformHook() {
520 String os = System.getProperty("os.name");
521 if (os == null) {
522 System.err.println("Your operating system has no name, so I'm guessing its some kind of *nix.");
523 platform = new PlatformHookUnixoid();
524 } else if (os.toLowerCase().startsWith("windows")) {
525 platform = new PlatformHookWindows();
526 } else if (os.equals("Linux") || os.equals("Solaris") ||
527 os.equals("SunOS") || os.equals("AIX") ||
528 os.equals("FreeBSD") || os.equals("NetBSD") || os.equals("OpenBSD")) {
529 platform = new PlatformHookUnixoid();
530 } else if (os.toLowerCase().startsWith("mac os x")) {
531 platform = new PlatformHookOsx();
532 } else {
533 System.err.println("I don't know your operating system '"+os+"', so I'm guessing its some kind of *nix.");
534 platform = new PlatformHookUnixoid();
535 }
536 }
537
538 static public void saveGuiGeometry() {
539 // save the current window geometry and the width of the toggle dialog area
540 String newGeometry = "";
541 String newToggleDlgWidth = null;
542 try {
543 if (((JFrame)parent).getExtendedState() == JFrame.NORMAL) {
544 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
545 Rectangle bounds = parent.getBounds();
546 int width = (int)bounds.getWidth();
547 int height = (int)bounds.getHeight();
548 int x = (int)bounds.getX();
549 int y = (int)bounds.getY();
550 if (width > screenDimension.width) {
551 width = screenDimension.width;
552 }
553 if (height > screenDimension.height) {
554 width = screenDimension.height;
555 }
556 if (x < 0) {
557 x = 0;
558 }
559 if (y < 0) {
560 y = 0;
561 }
562 newGeometry = width + "x" + height + "+" + x + "+" + y;
563 }
564
565 if (map != null) {
566 newToggleDlgWidth = Integer.toString(map.getToggleDlgWidth());
567 if (newToggleDlgWidth.equals(Integer.toString(map.DEF_TOGGLE_DLG_WIDTH))) {
568 newToggleDlgWidth = "";
569 }
570 }
571 }
572 catch (Exception e) {
573 System.out.println("Failed to save GUI geometry: " + e);
574 e.printStackTrace();
575 }
576 pref.put("gui.geometry", newGeometry);
577 if (newToggleDlgWidth != null) {
578 pref.put("toggleDialogs.width", newToggleDlgWidth);
579 }
580 }
581}
Note: See TracBrowser for help on using the repository browser.