001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm; 003 import static org.openstreetmap.josm.tools.I18n.tr; 004 005 import java.awt.BorderLayout; 006 import java.awt.Component; 007 import java.awt.GridBagConstraints; 008 import java.awt.GridBagLayout; 009 import java.awt.event.ComponentEvent; 010 import java.awt.event.ComponentListener; 011 import java.awt.event.KeyEvent; 012 import java.awt.event.WindowAdapter; 013 import java.awt.event.WindowEvent; 014 import java.io.File; 015 import java.lang.ref.WeakReference; 016 import java.net.URI; 017 import java.net.URISyntaxException; 018 import java.text.MessageFormat; 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.StringTokenizer; 025 import java.util.concurrent.Callable; 026 import java.util.concurrent.ExecutorService; 027 import java.util.concurrent.Executors; 028 import java.util.concurrent.Future; 029 030 import javax.swing.Action; 031 import javax.swing.InputMap; 032 import javax.swing.JComponent; 033 import javax.swing.JFrame; 034 import javax.swing.JLabel; 035 import javax.swing.JOptionPane; 036 import javax.swing.JPanel; 037 import javax.swing.JTextArea; 038 import javax.swing.KeyStroke; 039 import javax.swing.UIManager; 040 041 import org.openstreetmap.gui.jmapviewer.FeatureAdapter; 042 import org.openstreetmap.josm.actions.JosmAction; 043 import org.openstreetmap.josm.actions.OpenFileAction; 044 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask; 045 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 046 import org.openstreetmap.josm.actions.downloadtasks.DownloadTask; 047 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler; 048 import org.openstreetmap.josm.actions.mapmode.MapMode; 049 import org.openstreetmap.josm.actions.search.SearchAction; 050 import org.openstreetmap.josm.data.Bounds; 051 import org.openstreetmap.josm.data.Preferences; 052 import org.openstreetmap.josm.data.UndoRedoHandler; 053 import org.openstreetmap.josm.data.coor.CoordinateFormat; 054 import org.openstreetmap.josm.data.coor.LatLon; 055 import org.openstreetmap.josm.data.osm.DataSet; 056 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy; 057 import org.openstreetmap.josm.data.projection.Projection; 058 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 059 import org.openstreetmap.josm.data.validation.OsmValidator; 060 import org.openstreetmap.josm.gui.GettingStarted; 061 import org.openstreetmap.josm.gui.MainApplication.Option; 062 import org.openstreetmap.josm.gui.MainMenu; 063 import org.openstreetmap.josm.gui.MapFrame; 064 import org.openstreetmap.josm.gui.MapView; 065 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 066 import org.openstreetmap.josm.gui.io.SaveLayersDialog; 067 import org.openstreetmap.josm.gui.layer.Layer; 068 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 069 import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener; 070 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences; 071 import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference; 072 import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference; 073 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 074 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference; 075 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 076 import org.openstreetmap.josm.gui.progress.ProgressMonitorExecutor; 077 import org.openstreetmap.josm.gui.util.RedirectInputMap; 078 import org.openstreetmap.josm.io.OsmApi; 079 import org.openstreetmap.josm.plugins.PluginHandler; 080 import org.openstreetmap.josm.tools.CheckParameterUtil; 081 import org.openstreetmap.josm.tools.I18n; 082 import org.openstreetmap.josm.tools.ImageProvider; 083 import org.openstreetmap.josm.tools.OpenBrowser; 084 import org.openstreetmap.josm.tools.OsmUrlToBounds; 085 import org.openstreetmap.josm.tools.PlatformHook; 086 import org.openstreetmap.josm.tools.PlatformHookOsx; 087 import org.openstreetmap.josm.tools.PlatformHookUnixoid; 088 import org.openstreetmap.josm.tools.PlatformHookWindows; 089 import org.openstreetmap.josm.tools.Shortcut; 090 import org.openstreetmap.josm.tools.Utils; 091 import org.openstreetmap.josm.tools.WindowGeometry; 092 093 abstract public class Main { 094 095 /** 096 * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if 097 * it only shows the MOTD panel. 098 * 099 * @return <code>true</code> if JOSM currently displays a map view 100 */ 101 static public boolean isDisplayingMapView() { 102 if (map == null) return false; 103 if (map.mapView == null) return false; 104 return true; 105 } 106 /** 107 * Global parent component for all dialogs and message boxes 108 */ 109 public static Component parent; 110 /** 111 * Global application. 112 */ 113 public static Main main; 114 /** 115 * The worker thread slave. This is for executing all long and intensive 116 * calculations. The executed runnables are guaranteed to be executed separately 117 * and sequential. 118 */ 119 public final static ExecutorService worker = new ProgressMonitorExecutor(); 120 /** 121 * Global application preferences 122 */ 123 public static Preferences pref; 124 125 /** 126 * The global paste buffer. 127 */ 128 public static final PrimitiveDeepCopy pasteBuffer = new PrimitiveDeepCopy(); 129 public static Layer pasteSource; 130 131 /** 132 * The MapFrame. Use setMapFrame to set or clear it. 133 */ 134 public static MapFrame map; 135 /** 136 * Set to <code>true</code>, when in applet mode 137 */ 138 public static boolean applet = false; 139 140 /** 141 * The toolbar preference control to register new actions. 142 */ 143 public static ToolbarPreferences toolbar; 144 145 public UndoRedoHandler undoRedo = new UndoRedoHandler(); 146 147 public static PleaseWaitProgressMonitor currentProgressMonitor; 148 149 /** 150 * The main menu bar at top of screen. 151 */ 152 public MainMenu menu; 153 154 /** 155 * The data validation handler. 156 */ 157 public OsmValidator validator; 158 /** 159 * The MOTD Layer. 160 */ 161 private GettingStarted gettingStarted = new GettingStarted(); 162 163 /** 164 * Logging level (3 = debug, 2 = info, 1 = warn, 0 = none). 165 */ 166 static public int log_level = 2; 167 /** 168 * Print a warning message if logging is on. 169 * @param msg The message to print. 170 */ 171 static public void warn(String msg) { 172 if (log_level < 1) 173 return; 174 System.out.println(msg); 175 } 176 /** 177 * Print an informational message if logging is on. 178 * @param msg The message to print. 179 */ 180 static public void info(String msg) { 181 if (log_level < 2) 182 return; 183 System.out.println(msg); 184 } 185 /** 186 * Print an debug message if logging is on. 187 * @param msg The message to print. 188 */ 189 static public void debug(String msg) { 190 if (log_level < 3) 191 return; 192 System.out.println(msg); 193 } 194 /** 195 * Print a formated warning message if logging is on. Calls {@link MessageFormat#format} 196 * function to format text. 197 * @param msg The formated message to print. 198 * @param objects The objects to insert into format string. 199 */ 200 static public void warn(String msg, Object... objects) { 201 warn(MessageFormat.format(msg, objects)); 202 } 203 /** 204 * Print a formated informational message if logging is on. Calls {@link MessageFormat#format} 205 * function to format text. 206 * @param msg The formated message to print. 207 * @param objects The objects to insert into format string. 208 */ 209 static public void info(String msg, Object... objects) { 210 info(MessageFormat.format(msg, objects)); 211 } 212 /** 213 * Print a formated debug message if logging is on. Calls {@link MessageFormat#format} 214 * function to format text. 215 * @param msg The formated message to print. 216 * @param objects The objects to insert into format string. 217 */ 218 static public void debug(String msg, Object... objects) { 219 debug(MessageFormat.format(msg, objects)); 220 } 221 222 /** 223 * Platform specific code goes in here. 224 * Plugins may replace it, however, some hooks will be called before any plugins have been loeaded. 225 * So if you need to hook into those early ones, split your class and send the one with the early hooks 226 * to the JOSM team for inclusion. 227 */ 228 public static PlatformHook platform; 229 230 /** 231 * Whether or not the java vm is openjdk 232 * We use this to work around openjdk bugs 233 */ 234 public static boolean isOpenjdk; 235 236 /** 237 * Set or clear (if passed <code>null</code>) the map. 238 */ 239 public final void setMapFrame(final MapFrame map) { 240 MapFrame old = Main.map; 241 panel.setVisible(false); 242 panel.removeAll(); 243 if (map != null) { 244 map.fillPanel(panel); 245 } else { 246 old.destroy(); 247 panel.add(gettingStarted, BorderLayout.CENTER); 248 } 249 panel.setVisible(true); 250 redoUndoListener.commandChanged(0,0); 251 252 Main.map = map; 253 254 PluginHandler.notifyMapFrameChanged(old, map); 255 if (map == null && currentProgressMonitor != null) { 256 currentProgressMonitor.showForegroundDialog(); 257 } 258 } 259 260 /** 261 * Remove the specified layer from the map. If it is the last layer, 262 * remove the map as well. 263 */ 264 public final void removeLayer(final Layer layer) { 265 if (map != null) { 266 map.mapView.removeLayer(layer); 267 if (map != null && map.mapView.getAllLayers().isEmpty()) { 268 setMapFrame(null); 269 } 270 } 271 } 272 273 private static InitStatusListener initListener = null; 274 275 public static interface InitStatusListener { 276 277 void updateStatus(String event); 278 } 279 280 public static void setInitStatusListener(InitStatusListener listener) { 281 initListener = listener; 282 } 283 284 public Main() { 285 main = this; 286 isOpenjdk = System.getProperty("java.vm.name").toUpperCase().indexOf("OPENJDK") != -1; 287 288 if (initListener != null) { 289 initListener.updateStatus(tr("Executing platform startup hook")); 290 } 291 platform.startupHook(); 292 293 if (initListener != null) { 294 initListener.updateStatus(tr("Building main menu")); 295 } 296 contentPanePrivate.add(panel, BorderLayout.CENTER); 297 panel.add(gettingStarted, BorderLayout.CENTER); 298 menu = new MainMenu(); 299 300 undoRedo.addCommandQueueListener(redoUndoListener); 301 302 // creating toolbar 303 contentPanePrivate.add(toolbar.control, BorderLayout.NORTH); 304 305 registerActionShortcut(menu.help, Shortcut.registerShortcut("system:help", tr("Help"), 306 KeyEvent.VK_F1, Shortcut.DIRECT)); 307 308 // contains several initialization tasks to be executed (in parallel) by a ExecutorService 309 List<Callable<Void>> tasks = new ArrayList<Callable<Void>>(); 310 311 tasks.add(new Callable<Void>() { 312 313 @Override 314 public Void call() throws Exception { 315 // We try to establish an API connection early, so that any API 316 // capabilities are already known to the editor instance. However 317 // if it goes wrong that's not critical at this stage. 318 if (initListener != null) { 319 initListener.updateStatus(tr("Initializing OSM API")); 320 } 321 try { 322 OsmApi.getOsmApi().initialize(null, true); 323 } catch (Exception x) { 324 // ignore any exception here. 325 } 326 return null; 327 } 328 }); 329 330 tasks.add(new Callable<Void>() { 331 332 @Override 333 public Void call() throws Exception { 334 if (initListener != null) { 335 initListener.updateStatus(tr("Initializing presets")); 336 } 337 TaggingPresetPreference.initialize(); 338 // some validator tests require the presets to be initialized 339 // TODO remove this dependency for parallel initialization 340 if (initListener != null) { 341 initListener.updateStatus(tr("Initializing validator")); 342 } 343 validator = new OsmValidator(); 344 MapView.addLayerChangeListener(validator); 345 return null; 346 } 347 }); 348 349 tasks.add(new Callable<Void>() { 350 351 @Override 352 public Void call() throws Exception { 353 if (initListener != null) { 354 initListener.updateStatus(tr("Initializing map styles")); 355 } 356 MapPaintPreference.initialize(); 357 return null; 358 } 359 }); 360 361 tasks.add(new Callable<Void>() { 362 363 @Override 364 public Void call() throws Exception { 365 if (initListener != null) { 366 initListener.updateStatus(tr("Loading imagery preferences")); 367 } 368 ImageryPreference.initialize(); 369 return null; 370 } 371 }); 372 373 try { 374 for (Future<Void> i : Executors.newFixedThreadPool( 375 Runtime.getRuntime().availableProcessors()).invokeAll(tasks)) { 376 i.get(); 377 } 378 } catch (Exception ex) { 379 throw new RuntimeException(ex); 380 } 381 382 // hooks for the jmapviewer component 383 FeatureAdapter.registerBrowserAdapter(new FeatureAdapter.BrowserAdapter() { 384 @Override 385 public void openLink(String url) { 386 OpenBrowser.displayUrl(url); 387 } 388 }); 389 FeatureAdapter.registerTranslationAdapter(I18n.getTranslationAdapter()); 390 391 if (initListener != null) { 392 initListener.updateStatus(tr("Updating user interface")); 393 } 394 395 toolbar.refreshToolbarControl(); 396 397 toolbar.control.updateUI(); 398 contentPanePrivate.updateUI(); 399 400 } 401 402 /** 403 * Add a new layer to the map. If no map exists, create one. 404 */ 405 public final synchronized void addLayer(final Layer layer) { 406 if (map == null) { 407 final MapFrame mapFrame = new MapFrame(contentPanePrivate); 408 setMapFrame(mapFrame); 409 mapFrame.selectMapMode((MapMode)mapFrame.getDefaultButtonAction(), layer); 410 mapFrame.setVisible(true); 411 mapFrame.initializeDialogsPane(); 412 // bootstrapping problem: make sure the layer list dialog is going to 413 // listen to change events of the very first layer 414 // 415 layer.addPropertyChangeListener(LayerListDialog.getInstance().getModel()); 416 } 417 layer.hookUpMapView(); 418 map.mapView.addLayer(layer); 419 } 420 421 /** 422 * Replies <code>true</code> if there is an edit layer 423 * 424 * @return <code>true</code> if there is an edit layer 425 */ 426 public boolean hasEditLayer() { 427 if (getEditLayer() == null) return false; 428 return true; 429 } 430 431 /** 432 * Replies the current edit layer 433 * 434 * @return the current edit layer. <code>null</code>, if no current edit layer exists 435 */ 436 public OsmDataLayer getEditLayer() { 437 if (map == null) return null; 438 if (map.mapView == null) return null; 439 return map.mapView.getEditLayer(); 440 } 441 442 /** 443 * Replies the current data set. 444 * 445 * @return the current data set. <code>null</code>, if no current data set exists 446 */ 447 public DataSet getCurrentDataSet() { 448 if (!hasEditLayer()) return null; 449 return getEditLayer().data; 450 } 451 452 /** 453 * Returns the currently active layer 454 * 455 * @return the currently active layer. <code>null</code>, if currently no active layer exists 456 */ 457 public Layer getActiveLayer() { 458 if (map == null) return null; 459 if (map.mapView == null) return null; 460 return map.mapView.getActiveLayer(); 461 } 462 463 protected static final JPanel contentPanePrivate = new JPanel(new BorderLayout()); 464 465 public static void redirectToMainContentPane(JComponent source) { 466 RedirectInputMap.redirect(source, contentPanePrivate); 467 } 468 469 public static void registerActionShortcut(JosmAction action) { 470 registerActionShortcut(action, action.getShortcut()); 471 } 472 473 public static void registerActionShortcut(Action action, Shortcut shortcut) { 474 KeyStroke keyStroke = shortcut.getKeyStroke(); 475 if (keyStroke == null) 476 return; 477 478 InputMap inputMap = contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); 479 Object existing = inputMap.get(keyStroke); 480 if (existing != null && !existing.equals(action)) { 481 System.out.println(String.format("Keystroke %s is already assigned to %s, will be overridden by %s", keyStroke, existing, action)); 482 } 483 inputMap.put(keyStroke, action); 484 485 contentPanePrivate.getActionMap().put(action, action); 486 } 487 488 public static void unregisterShortcut(Shortcut shortcut) { 489 contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(shortcut.getKeyStroke()); 490 } 491 492 public static void unregisterActionShortcut(JosmAction action) { 493 unregisterActionShortcut(action, action.getShortcut()); 494 } 495 496 public static void unregisterActionShortcut(Action action, Shortcut shortcut) { 497 unregisterShortcut(shortcut); 498 contentPanePrivate.getActionMap().remove(action); 499 } 500 501 /////////////////////////////////////////////////////////////////////////// 502 // Implementation part 503 /////////////////////////////////////////////////////////////////////////// 504 505 public static final JPanel panel = new JPanel(new BorderLayout()); 506 507 protected static WindowGeometry geometry; 508 protected static int windowState = JFrame.NORMAL; 509 510 private final CommandQueueListener redoUndoListener = new CommandQueueListener(){ 511 public void commandChanged(final int queueSize, final int redoSize) { 512 menu.undo.setEnabled(queueSize > 0); 513 menu.redo.setEnabled(redoSize > 0); 514 } 515 }; 516 517 /** 518 * Should be called before the main constructor to setup some parameter stuff 519 * @param args The parsed argument list. 520 */ 521 public static void preConstructorInit(Map<Option, Collection<String>> args) { 522 ProjectionPreference.setProjection(); 523 524 try { 525 String defaultlaf = platform.getDefaultStyle(); 526 String laf = Main.pref.get("laf", defaultlaf); 527 try { 528 UIManager.setLookAndFeel(laf); 529 } 530 catch (final java.lang.ClassNotFoundException e) { 531 System.out.println("Look and Feel not found: " + laf); 532 Main.pref.put("laf", defaultlaf); 533 } 534 catch (final javax.swing.UnsupportedLookAndFeelException e) { 535 System.out.println("Look and Feel not supported: " + laf); 536 Main.pref.put("laf", defaultlaf); 537 } 538 toolbar = new ToolbarPreferences(); 539 contentPanePrivate.updateUI(); 540 panel.updateUI(); 541 } catch (final Exception e) { 542 e.printStackTrace(); 543 } 544 UIManager.put("OptionPane.okIcon", ImageProvider.get("ok")); 545 UIManager.put("OptionPane.yesIcon", UIManager.get("OptionPane.okIcon")); 546 UIManager.put("OptionPane.cancelIcon", ImageProvider.get("cancel")); 547 UIManager.put("OptionPane.noIcon", UIManager.get("OptionPane.cancelIcon")); 548 549 I18n.translateJavaInternalMessages(); 550 551 // init default coordinate format 552 // 553 try { 554 //CoordinateFormat format = CoordinateFormat.valueOf(Main.pref.get("coordinates")); 555 CoordinateFormat.setCoordinateFormat(CoordinateFormat.valueOf(Main.pref.get("coordinates"))); 556 } catch (IllegalArgumentException iae) { 557 CoordinateFormat.setCoordinateFormat(CoordinateFormat.DECIMAL_DEGREES); 558 } 559 560 geometry = WindowGeometry.mainWindow("gui.geometry", 561 (args.containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null), 562 !args.containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false)); 563 } 564 565 public void postConstructorProcessCmdLine(Map<Option, Collection<String>> args) { 566 if (args.containsKey(Option.DOWNLOAD)) { 567 List<File> fileList = new ArrayList<File>(); 568 for (String s : args.get(Option.DOWNLOAD)) { 569 File f = null; 570 switch(paramType(s)) { 571 case httpUrl: 572 downloadFromParamHttp(false, s); 573 break; 574 case bounds: 575 downloadFromParamBounds(false, s); 576 break; 577 case fileUrl: 578 try { 579 f = new File(new URI(s)); 580 } catch (URISyntaxException e) { 581 JOptionPane.showMessageDialog( 582 Main.parent, 583 tr("Ignoring malformed file URL: \"{0}\"", s), 584 tr("Warning"), 585 JOptionPane.WARNING_MESSAGE 586 ); 587 } 588 if (f!=null) { 589 fileList.add(f); 590 } 591 break; 592 case fileName: 593 f = new File(s); 594 fileList.add(f); 595 break; 596 } 597 } 598 if(!fileList.isEmpty()) 599 { 600 OpenFileAction.openFiles(fileList, true); 601 } 602 } 603 if (args.containsKey(Option.DOWNLOADGPS)) { 604 for (String s : args.get(Option.DOWNLOADGPS)) { 605 switch(paramType(s)) { 606 case httpUrl: 607 downloadFromParamHttp(true, s); 608 break; 609 case bounds: 610 downloadFromParamBounds(true, s); 611 break; 612 case fileUrl: 613 case fileName: 614 JOptionPane.showMessageDialog( 615 Main.parent, 616 tr("Parameter \"downloadgps\" does not accept file names or file URLs"), 617 tr("Warning"), 618 JOptionPane.WARNING_MESSAGE 619 ); 620 } 621 } 622 } 623 if (args.containsKey(Option.SELECTION)) { 624 for (String s : args.get(Option.SELECTION)) { 625 SearchAction.search(s, SearchAction.SearchMode.add); 626 } 627 } 628 } 629 630 public static boolean saveUnsavedModifications() { 631 if (map == null) return true; 632 SaveLayersDialog dialog = new SaveLayersDialog(Main.parent); 633 List<OsmDataLayer> layersWithUnmodifiedChanges = new ArrayList<OsmDataLayer>(); 634 for (OsmDataLayer l: Main.map.mapView.getLayersOfType(OsmDataLayer.class)) { 635 if ((l.requiresSaveToFile() || l.requiresUploadToServer()) && l.data.isModified()) { 636 layersWithUnmodifiedChanges.add(l); 637 } 638 } 639 dialog.prepareForSavingAndUpdatingLayersBeforeExit(); 640 if (!layersWithUnmodifiedChanges.isEmpty()) { 641 dialog.getModel().populate(layersWithUnmodifiedChanges); 642 dialog.setVisible(true); 643 switch(dialog.getUserAction()) { 644 case CANCEL: return false; 645 case PROCEED: return true; 646 default: return false; 647 } 648 } 649 650 return true; 651 } 652 653 public static boolean exitJosm(boolean exit) { 654 if (Main.saveUnsavedModifications()) { 655 geometry.remember("gui.geometry"); 656 if (map != null) { 657 map.rememberToggleDialogWidth(); 658 } 659 pref.put("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0); 660 // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask) 661 if (Main.isDisplayingMapView()) { 662 Collection<Layer> layers = new ArrayList<Layer>(Main.map.mapView.getAllLayers()); 663 for (Layer l: layers) { 664 Main.map.mapView.removeLayer(l); 665 } 666 } 667 if (exit) { 668 System.exit(0); 669 return true; 670 } else 671 return true; 672 } else 673 return false; 674 } 675 676 /** 677 * The type of a command line parameter, to be used in switch statements. 678 * @see #paramType 679 */ 680 private enum DownloadParamType { httpUrl, fileUrl, bounds, fileName } 681 682 /** 683 * Guess the type of a parameter string specified on the command line with --download= or --downloadgps. 684 * @param s A parameter string 685 * @return The guessed parameter type 686 */ 687 private DownloadParamType paramType(String s) { 688 if(s.startsWith("http:")) return DownloadParamType.httpUrl; 689 if(s.startsWith("file:")) return DownloadParamType.fileUrl; 690 String coorPattern = "\\s*[+-]?[0-9]+(\\.[0-9]+)?\\s*"; 691 if(s.matches(coorPattern+"(,"+coorPattern+"){3}")) return DownloadParamType.bounds; 692 // everything else must be a file name 693 return DownloadParamType.fileName; 694 } 695 696 /** 697 * Download area specified on the command line as OSM URL. 698 * @param rawGps Flag to download raw GPS tracks 699 * @param s The URL parameter 700 */ 701 private static void downloadFromParamHttp(final boolean rawGps, String s) { 702 final Bounds b = OsmUrlToBounds.parse(s); 703 if (b == null) { 704 JOptionPane.showMessageDialog( 705 Main.parent, 706 tr("Ignoring malformed URL: \"{0}\"", s), 707 tr("Warning"), 708 JOptionPane.WARNING_MESSAGE 709 ); 710 } else { 711 downloadFromParamBounds(rawGps, b); 712 } 713 } 714 715 /** 716 * Download area specified on the command line as bounds string. 717 * @param rawGps Flag to download raw GPS tracks 718 * @param s The bounds parameter 719 */ 720 private static void downloadFromParamBounds(final boolean rawGps, String s) { 721 final StringTokenizer st = new StringTokenizer(s, ","); 722 if (st.countTokens() == 4) { 723 Bounds b = new Bounds( 724 new LatLon(Double.parseDouble(st.nextToken()),Double.parseDouble(st.nextToken())), 725 new LatLon(Double.parseDouble(st.nextToken()),Double.parseDouble(st.nextToken())) 726 ); 727 downloadFromParamBounds(rawGps, b); 728 } 729 } 730 731 /** 732 * Download area specified as Bounds value. 733 * @param rawGps Flag to download raw GPS tracks 734 * @param b The bounds value 735 * @see #downloadFromParamBounds(boolean, String) 736 * @see #downloadFromParamHttp 737 */ 738 private static void downloadFromParamBounds(final boolean rawGps, Bounds b) { 739 DownloadTask task = rawGps ? new DownloadGpsTask() : new DownloadOsmTask(); 740 // asynchronously launch the download task ... 741 Future<?> future = task.download(true, b, null); 742 // ... and the continuation when the download is finished (this will wait for the download to finish) 743 Main.worker.execute(new PostDownloadHandler(task, future)); 744 } 745 746 public static void determinePlatformHook() { 747 String os = System.getProperty("os.name"); 748 if (os == null) { 749 System.err.println("Your operating system has no name, so I'm guessing its some kind of *nix."); 750 platform = new PlatformHookUnixoid(); 751 } else if (os.toLowerCase().startsWith("windows")) { 752 platform = new PlatformHookWindows(); 753 } else if (os.equals("Linux") || os.equals("Solaris") || 754 os.equals("SunOS") || os.equals("AIX") || 755 os.equals("FreeBSD") || os.equals("NetBSD") || os.equals("OpenBSD")) { 756 platform = new PlatformHookUnixoid(); 757 } else if (os.toLowerCase().startsWith("mac os x")) { 758 platform = new PlatformHookOsx(); 759 } else { 760 System.err.println("I don't know your operating system '"+os+"', so I'm guessing its some kind of *nix."); 761 platform = new PlatformHookUnixoid(); 762 } 763 } 764 765 private static class WindowPositionSizeListener extends WindowAdapter implements 766 ComponentListener { 767 @Override 768 public void windowStateChanged(WindowEvent e) { 769 Main.windowState = e.getNewState(); 770 } 771 772 @Override 773 public void componentHidden(ComponentEvent e) { 774 } 775 776 @Override 777 public void componentMoved(ComponentEvent e) { 778 handleComponentEvent(e); 779 } 780 781 @Override 782 public void componentResized(ComponentEvent e) { 783 handleComponentEvent(e); 784 } 785 786 @Override 787 public void componentShown(ComponentEvent e) { 788 } 789 790 private void handleComponentEvent(ComponentEvent e) { 791 Component c = e.getComponent(); 792 if (c instanceof JFrame && c.isVisible() && Main.windowState == JFrame.NORMAL) { 793 Main.geometry = new WindowGeometry((JFrame) c); 794 } 795 } 796 } 797 public static void addListener() { 798 parent.addComponentListener(new WindowPositionSizeListener()); 799 ((JFrame)parent).addWindowStateListener(new WindowPositionSizeListener()); 800 } 801 802 public static void checkJava6() { 803 String version = System.getProperty("java.version"); 804 if (version != null) { 805 if (version.startsWith("1.6") || version.startsWith("6") || 806 version.startsWith("1.7") || version.startsWith("7")) 807 return; 808 if (version.startsWith("1.5") || version.startsWith("5")) { 809 JLabel ho = new JLabel("<html>"+ 810 tr("<h2>JOSM requires Java version 6.</h2>"+ 811 "Detected Java version: {0}.<br>"+ 812 "You can <ul><li>update your Java (JRE) or</li>"+ 813 "<li>use an earlier (Java 5 compatible) version of JOSM.</li></ul>"+ 814 "More Info:", version)+"</html>"); 815 JTextArea link = new JTextArea("http://josm.openstreetmap.de/wiki/Help/SystemRequirements"); 816 link.setEditable(false); 817 link.setBackground(panel.getBackground()); 818 JPanel panel = new JPanel(new GridBagLayout()); 819 GridBagConstraints gbc = new GridBagConstraints(); 820 gbc.gridwidth = GridBagConstraints.REMAINDER; 821 gbc.anchor = GridBagConstraints.WEST; 822 gbc.weightx = 1.0; 823 panel.add(ho, gbc); 824 panel.add(link, gbc); 825 final String EXIT = tr("Exit JOSM"); 826 final String CONTINUE = tr("Continue, try anyway"); 827 int ret = JOptionPane.showOptionDialog(null, panel, tr("Error"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null, new String[] {EXIT, CONTINUE}, EXIT); 828 if (ret == 0) { 829 System.exit(0); 830 } 831 return; 832 } 833 } 834 System.err.println("Error: Could not recognize Java Version: "+version); 835 } 836 837 /* ----------------------------------------------------------------------------------------- */ 838 /* projection handling - Main is a registry for a single, global projection instance */ 839 /* */ 840 /* TODO: For historical reasons the registry is implemented by Main. An alternative approach */ 841 /* would be a singleton org.openstreetmap.josm.data.projection.ProjectionRegistry class. */ 842 /* ----------------------------------------------------------------------------------------- */ 843 /** 844 * The projection method used. 845 * use {@link #getProjection()} and {@link #setProjection(Projection)} for access. 846 * Use {@link #setProjection(Projection)} in order to trigger a projection change event. 847 */ 848 private static Projection proj; 849 850 /** 851 * Replies the current projection. 852 * 853 * @return the currently active projection 854 */ 855 public static Projection getProjection() { 856 return proj; 857 } 858 859 /** 860 * Sets the current projection 861 * 862 * @param p the projection 863 */ 864 public static void setProjection(Projection p) { 865 CheckParameterUtil.ensureParameterNotNull(p); 866 Projection oldValue = proj; 867 Bounds b = isDisplayingMapView() ? map.mapView.getRealBounds() : null; 868 proj = p; 869 fireProjectionChanged(oldValue, proj, b); 870 } 871 872 /* 873 * Keep WeakReferences to the listeners. This relieves clients from the burden of 874 * explicitly removing the listeners and allows us to transparently register every 875 * created dataset as projection change listener. 876 */ 877 private static final ArrayList<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<WeakReference<ProjectionChangeListener>>(); 878 879 private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) { 880 if (newValue == null ^ oldValue == null 881 || (newValue != null && oldValue != null && !Utils.equal(newValue.toCode(), oldValue.toCode()))) { 882 883 synchronized(Main.class) { 884 Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator(); 885 while(it.hasNext()){ 886 WeakReference<ProjectionChangeListener> wr = it.next(); 887 ProjectionChangeListener listener = wr.get(); 888 if (listener == null) { 889 it.remove(); 890 continue; 891 } 892 listener.projectionChanged(oldValue, newValue); 893 } 894 } 895 if (newValue != null && oldBounds != null) { 896 Main.map.mapView.zoomTo(oldBounds); 897 } 898 /* TODO - remove layers with fixed projection */ 899 } 900 } 901 902 /** 903 * Register a projection change listener 904 * 905 * @param listener the listener. Ignored if <code>null</code>. 906 */ 907 public static void addProjectionChangeListener(ProjectionChangeListener listener) { 908 if (listener == null) return; 909 synchronized (Main.class) { 910 for (WeakReference<ProjectionChangeListener> wr : listeners) { 911 // already registered ? => abort 912 if (wr.get() == listener) return; 913 } 914 listeners.add(new WeakReference<ProjectionChangeListener>(listener)); 915 } 916 } 917 918 /** 919 * Removes a projection change listener 920 * 921 * @param listener the listener. Ignored if <code>null</code>. 922 */ 923 public static void removeProjectionChangeListener(ProjectionChangeListener listener) { 924 if (listener == null) return; 925 synchronized(Main.class){ 926 Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator(); 927 while(it.hasNext()){ 928 WeakReference<ProjectionChangeListener> wr = it.next(); 929 // remove the listener - and any other listener which god garbage 930 // collected in the meantime 931 if (wr.get() == null || wr.get() == listener) { 932 it.remove(); 933 } 934 } 935 } 936 } 937 }