001 // License: GPL. See LICENSE file for details. 002 package org.openstreetmap.josm.gui; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.BorderLayout; 007 import java.awt.Component; 008 import java.awt.Container; 009 import java.awt.Dimension; 010 import java.awt.Font; 011 import java.awt.GridBagLayout; 012 import java.awt.Rectangle; 013 import java.awt.event.ActionEvent; 014 import java.awt.event.KeyEvent; 015 import java.awt.event.MouseWheelEvent; 016 import java.awt.event.MouseWheelListener; 017 import java.util.ArrayList; 018 import java.util.Collection; 019 import java.util.HashMap; 020 import java.util.List; 021 import java.util.Map; 022 import java.util.concurrent.CopyOnWriteArrayList; 023 024 import javax.swing.AbstractAction; 025 import javax.swing.AbstractButton; 026 import javax.swing.Action; 027 import javax.swing.BoxLayout; 028 import javax.swing.ButtonGroup; 029 import javax.swing.JButton; 030 import javax.swing.JCheckBoxMenuItem; 031 import javax.swing.JComponent; 032 import javax.swing.JPanel; 033 import javax.swing.JPopupMenu; 034 import javax.swing.JSplitPane; 035 import javax.swing.JToolBar; 036 import javax.swing.KeyStroke; 037 import javax.swing.SwingUtilities; 038 import javax.swing.border.Border; 039 import javax.swing.plaf.basic.BasicSplitPaneDivider; 040 import javax.swing.plaf.basic.BasicSplitPaneUI; 041 042 import org.openstreetmap.josm.Main; 043 import org.openstreetmap.josm.actions.LassoModeAction; 044 import org.openstreetmap.josm.actions.mapmode.DeleteAction; 045 import org.openstreetmap.josm.actions.mapmode.DrawAction; 046 import org.openstreetmap.josm.actions.mapmode.ExtrudeAction; 047 import org.openstreetmap.josm.actions.mapmode.ImproveWayAccuracyAction; 048 import org.openstreetmap.josm.actions.mapmode.MapMode; 049 import org.openstreetmap.josm.actions.mapmode.ParallelWayAction; 050 import org.openstreetmap.josm.actions.mapmode.SelectAction; 051 import org.openstreetmap.josm.actions.mapmode.ZoomAction; 052 import org.openstreetmap.josm.data.Preferences; 053 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; 054 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 055 import org.openstreetmap.josm.gui.MapView.LayerChangeListener; 056 import org.openstreetmap.josm.gui.dialogs.ChangesetDialog; 057 import org.openstreetmap.josm.gui.dialogs.CommandStackDialog; 058 import org.openstreetmap.josm.gui.dialogs.ConflictDialog; 059 import org.openstreetmap.josm.gui.dialogs.DialogsPanel; 060 import org.openstreetmap.josm.gui.dialogs.FilterDialog; 061 import org.openstreetmap.josm.gui.dialogs.HistoryDialog; 062 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 063 import org.openstreetmap.josm.gui.dialogs.MapPaintDialog; 064 import org.openstreetmap.josm.gui.dialogs.RelationListDialog; 065 import org.openstreetmap.josm.gui.dialogs.SelectionListDialog; 066 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 067 import org.openstreetmap.josm.gui.dialogs.UserListDialog; 068 import org.openstreetmap.josm.gui.dialogs.ValidatorDialog; 069 import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog; 070 import org.openstreetmap.josm.gui.layer.Layer; 071 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 072 import org.openstreetmap.josm.tools.Destroyable; 073 import org.openstreetmap.josm.tools.GBC; 074 075 /** 076 * One Map frame with one dataset behind. This is the container gui class whose 077 * display can be set to the different views. 078 * 079 * @author imi 080 */ 081 public class MapFrame extends JPanel implements Destroyable, LayerChangeListener { 082 083 /** 084 * The current mode, this frame operates. 085 */ 086 public MapMode mapMode; 087 088 private final List<MapMode> mapModes = new ArrayList<MapMode>(); 089 /** 090 * The view control displayed. 091 */ 092 public MapView mapView; 093 /** 094 * The toolbar with the action icons. To add new toggle dialog actions, use addToggleDialog 095 * instead of adding directly to this list. To add a new mode use addMapMode. 096 */ 097 private JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL); 098 private JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL); 099 /** 100 * The status line below the map 101 */ 102 public MapStatus statusLine; 103 104 // Toggle dialogs 105 public ConflictDialog conflictDialog; 106 public FilterDialog filterDialog; 107 public RelationListDialog relationListDialog; 108 public ValidatorDialog validatorDialog; 109 public SelectionListDialog selectionListDialog; 110 public PropertiesDialog propertiesDialog; 111 112 // Map modes 113 public final SelectAction mapModeSelect; 114 private final MapMode mapModeDraw; 115 private final MapMode mapModeZoom; 116 117 /** 118 * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog 119 * instead of adding directly to this list. 120 */ 121 private List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>(); 122 private final JPanel leftPanel; 123 private final DialogsPanel dialogsPanel; 124 125 public final ButtonGroup toolGroup = new ButtonGroup(); 126 127 private List<IconToggleButton> allDialogButtons = new ArrayList<IconToggleButton>(); 128 private List<IconToggleButton> allMapModeButtons = new ArrayList<IconToggleButton>(); 129 130 private final ListAllButtonsAction listAllDialogsAction = new ListAllButtonsAction(allDialogButtons); 131 private final ListAllButtonsAction listAllMapModesAction = new ListAllButtonsAction(allMapModeButtons); 132 private final JButton listAllToggleDialogsButton = new JButton(listAllDialogsAction); 133 private final JButton listAllMapModesButton = new JButton(listAllMapModesAction); 134 { 135 listAllDialogsAction.setButton(listAllToggleDialogsButton); 136 listAllMapModesAction.setButton(listAllMapModesButton); 137 } 138 139 /** 140 * Default width of the toggle dialog area. 141 */ 142 public static final int DEF_TOGGLE_DLG_WIDTH = 330; 143 144 private final Map<Layer, MapMode> lastMapMode = new HashMap<Layer, MapMode>(); 145 146 public MapFrame(JPanel contentPane) { 147 setSize(400,400); 148 setLayout(new BorderLayout()); 149 150 151 mapView = new MapView(contentPane); 152 153 new FileDrop(mapView); 154 155 leftPanel = new JPanel(); 156 leftPanel.setLayout(new GridBagLayout()); 157 158 leftPanel.add(mapView, GBC.std().fill()); 159 160 // toolbar 161 toolBarActions.setFloatable(false); 162 addMapMode(new IconToggleButton(mapModeSelect = new SelectAction(this))); 163 addMapMode(new IconToggleButton(new LassoModeAction(), true)); 164 addMapMode(new IconToggleButton(mapModeDraw = new DrawAction(this))); 165 addMapMode(new IconToggleButton(mapModeZoom = new ZoomAction(this))); 166 addMapMode(new IconToggleButton(new DeleteAction(this), true)); 167 addMapMode(new IconToggleButton(new ParallelWayAction(this), true)); 168 addMapMode(new IconToggleButton(new ExtrudeAction(this), true)); 169 addMapMode(new IconToggleButton(new ImproveWayAccuracyAction(Main.map), true)); 170 171 toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true); 172 173 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); 174 dialogsPanel = new DialogsPanel(splitPane); 175 splitPane.setLeftComponent(leftPanel); 176 splitPane.setRightComponent(dialogsPanel); 177 178 /** 179 * All additional space goes to the mapView 180 */ 181 splitPane.setResizeWeight(1.0); 182 183 /** 184 * Some beautifications. 185 */ 186 splitPane.setDividerSize(5); 187 splitPane.setBorder(null); 188 splitPane.setUI(new BasicSplitPaneUI() { 189 @Override 190 public BasicSplitPaneDivider createDefaultDivider() { 191 return new BasicSplitPaneDivider(this) { 192 @Override 193 public void setBorder(Border b) { 194 } 195 }; 196 } 197 }); 198 199 // JSplitPane supports F6 and F8 shortcuts by default, but we need them for Audio actions 200 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), new Object()); 201 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), new Object()); 202 203 add(splitPane, BorderLayout.CENTER); 204 205 dialogsPanel.setLayout(new BoxLayout(dialogsPanel, BoxLayout.Y_AXIS)); 206 dialogsPanel.setPreferredSize(new Dimension(Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH), 0)); 207 208 dialogsPanel.setMinimumSize(new Dimension(24, 0)); 209 mapView.setMinimumSize(new Dimension(10,0)); 210 211 toolBarToggle.setFloatable(false); 212 LayerListDialog.createInstance(this); 213 addToggleDialog(LayerListDialog.getInstance()); 214 addToggleDialog(propertiesDialog = new PropertiesDialog(this)); 215 addToggleDialog(selectionListDialog = new SelectionListDialog()); 216 addToggleDialog(relationListDialog = new RelationListDialog()); 217 addToggleDialog(new CommandStackDialog(this)); 218 addToggleDialog(new UserListDialog()); 219 addToggleDialog(new HistoryDialog(), true); 220 addToggleDialog(conflictDialog = new ConflictDialog()); 221 addToggleDialog(validatorDialog = new ValidatorDialog()); 222 addToggleDialog(filterDialog = new FilterDialog()); 223 addToggleDialog(new ChangesetDialog(this), true); 224 addToggleDialog(new MapPaintDialog()); 225 226 // status line below the map 227 statusLine = new MapStatus(this); 228 MapView.addLayerChangeListener(this); 229 } 230 231 public boolean selectSelectTool(boolean onlyIfModeless) { 232 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false)) 233 return false; 234 235 return selectMapMode(mapModeSelect); 236 } 237 238 public boolean selectDrawTool(boolean onlyIfModeless) { 239 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false)) 240 return false; 241 242 return selectMapMode(mapModeDraw); 243 } 244 245 public boolean selectZoomTool(boolean onlyIfModeless) { 246 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false)) 247 return false; 248 249 return selectMapMode(mapModeZoom); 250 } 251 252 /** 253 * Called as some kind of destructor when the last layer has been removed. 254 * Delegates the call to all Destroyables within this component (e.g. MapModes) 255 */ 256 public void destroy() { 257 MapView.removeLayerChangeListener(this); 258 dialogsPanel.destroy(); 259 Main.pref.removePreferenceChangeListener(sidetoolbarPreferencesChangedListener); 260 for (int i = 0; i < toolBarActions.getComponentCount(); ++i) { 261 if (toolBarActions.getComponent(i) instanceof Destroyable) { 262 ((Destroyable)toolBarActions.getComponent(i)).destroy(); 263 } 264 } 265 for (int i = 0; i < toolBarToggle.getComponentCount(); ++i) { 266 if (toolBarToggle.getComponent(i) instanceof Destroyable) { 267 ((Destroyable)toolBarToggle.getComponent(i)).destroy(); 268 } 269 } 270 271 // MapFrame gets destroyed when the last layer is removed, but the status line background 272 // thread that collects the information doesn't get destroyed automatically. 273 if(statusLine.thread != null) { 274 try { 275 statusLine.thread.interrupt(); 276 } catch (Exception e) { 277 e.printStackTrace(); 278 } 279 } 280 mapView.destroy(); 281 } 282 283 public Action getDefaultButtonAction() { 284 return ((AbstractButton)toolBarActions.getComponent(0)).getAction(); 285 } 286 287 /** 288 * Open all ToggleDialogs that have their preferences property set. Close all others. 289 */ 290 public void initializeDialogsPane() { 291 dialogsPanel.initialize(allDialogs); 292 } 293 294 public IconToggleButton addToggleDialog(final ToggleDialog dlg) { 295 return addToggleDialog(dlg, false); 296 } 297 298 /** 299 * Call this to add new toggle dialogs to the left button-list 300 * @param dlg The toggle dialog. It must not be in the list already. 301 */ 302 public IconToggleButton addToggleDialog(final ToggleDialog dlg, boolean isExpert) { 303 final IconToggleButton button = new IconToggleButton(dlg.getToggleAction(), isExpert); 304 button.setShowHideButtonListener(dlg); 305 addHideContextMenu(button); 306 dlg.setButton(button); 307 toolBarToggle.add(button); 308 allDialogs.add(dlg); 309 allDialogButtons.add(button); 310 button.applyButtonHiddenPreferences(); 311 if (dialogsPanel.initialized) { 312 dialogsPanel.add(dlg); 313 } 314 return button; 315 } 316 317 318 319 public void addMapMode(IconToggleButton b) { 320 toolBarActions.add(b); 321 toolGroup.add(b); 322 allMapModeButtons.add(b); 323 if (b.getAction() instanceof MapMode) { 324 mapModes.add((MapMode) b.getAction()); 325 } else 326 throw new IllegalArgumentException("MapMode action must be subclass of MapMode"); 327 addHideContextMenu(b); 328 b.applyButtonHiddenPreferences(); 329 } 330 331 /** 332 * Fires an property changed event "visible". 333 */ 334 @Override public void setVisible(boolean aFlag) { 335 boolean old = isVisible(); 336 super.setVisible(aFlag); 337 if (old != aFlag) { 338 firePropertyChange("visible", old, aFlag); 339 } 340 } 341 342 /** 343 * Change the operating map mode for the view. Will call unregister on the 344 * old MapMode and register on the new one. Now this function also verifies 345 * if new map mode is correct mode for current layer and does not change mode 346 * in such cases. 347 * @param mapMode The new mode to set. 348 * @return 349 */ 350 public boolean selectMapMode(MapMode newMapMode) { 351 return selectMapMode(newMapMode, mapView.getActiveLayer()); 352 } 353 354 /** 355 * Another version of the selectMapMode for changing layer action. 356 * Pass newly selected layer to this method. 357 * @param newMapMode 358 * @param newLayer 359 * @return True if mode is really selected 360 */ 361 public boolean selectMapMode(MapMode newMapMode, Layer newLayer) { 362 if (newMapMode == null || !newMapMode.layerIsSupported(newLayer)) 363 return false; 364 365 MapMode oldMapMode = this.mapMode; 366 if (newMapMode == oldMapMode) 367 return true; 368 if (oldMapMode != null) { 369 oldMapMode.exitMode(); 370 } 371 this.mapMode = newMapMode; 372 newMapMode.enterMode(); 373 lastMapMode.put(newLayer, newMapMode); 374 fireMapModeChanged(oldMapMode, newMapMode); 375 return true; 376 } 377 378 /** 379 * Fill the given panel by adding all necessary components to the different 380 * locations. 381 * 382 * @param panel The container to fill. Must have an BorderLayout. 383 */ 384 public void fillPanel(Container panel) { 385 panel.add(this, BorderLayout.CENTER); 386 JToolBar jb = new JToolBar(JToolBar.VERTICAL); 387 jb.setFloatable(false); 388 toolBarActions.setAlignmentX(0.5f); 389 jb.add(toolBarActions); 390 listAllMapModesButton.setAlignmentX(0.5f); 391 listAllMapModesButton.setBorder(null); 392 listAllMapModesButton.setFont(listAllMapModesButton.getFont().deriveFont(Font.PLAIN)); 393 jb.add(listAllMapModesButton); 394 395 if(Main.pref.getBoolean("sidetoolbar.togglevisible", true)) { 396 jb.addSeparator(new Dimension(0,18)); 397 toolBarToggle.setAlignmentX(0.5f); 398 jb.add(toolBarToggle); 399 listAllToggleDialogsButton.setAlignmentX(0.5f); 400 listAllToggleDialogsButton.setBorder(null); 401 listAllToggleDialogsButton.setFont(listAllToggleDialogsButton.getFont().deriveFont(Font.PLAIN)); 402 jb.add(listAllToggleDialogsButton); 403 } 404 405 final Component toToggle; 406 if (Main.pref.getBoolean("sidetoolbar.scrollable", true)) { 407 final ScrollViewport svp = new ScrollViewport(jb, ScrollViewport.VERTICAL_DIRECTION); 408 toToggle = svp; 409 panel.add(svp, BorderLayout.WEST); 410 jb.addMouseWheelListener(new MouseWheelListener() { 411 412 public void mouseWheelMoved(MouseWheelEvent e) { 413 svp.scroll(0, e.getUnitsToScroll() * 5); 414 } 415 }); 416 } else { 417 toToggle = jb; 418 panel.add(jb, BorderLayout.WEST); 419 } 420 toToggle.setVisible(Main.pref.getBoolean("sidetoolbar.visible", true)); 421 422 jb.addMouseListener(new PopupMenuLauncher(new JPopupMenu() { 423 424 { 425 add(new AbstractAction(tr("Hide edit toolbar")) { 426 427 @Override 428 public void actionPerformed(ActionEvent e) { 429 Main.pref.put("sidetoolbar.visible", false); 430 } 431 }); 432 } 433 })); 434 435 sidetoolbarPreferencesChangedListener = new Preferences.PreferenceChangedListener() { 436 437 @Override 438 public void preferenceChanged(PreferenceChangeEvent e) { 439 if ("sidetoolbar.visible".equals(e.getKey())) { 440 toToggle.setVisible(Main.pref.getBoolean("sidetoolbar.visible")); 441 } 442 } 443 }; 444 Main.pref.addPreferenceChangeListener(sidetoolbarPreferencesChangedListener); 445 446 if (statusLine != null && Main.pref.getBoolean("statusline.visible", true)) { 447 panel.add(statusLine, BorderLayout.SOUTH); 448 } 449 } 450 451 private void addHideContextMenu(final IconToggleButton b) { 452 //context menu 453 b.addMouseListener(new PopupMenuLauncher(new JPopupMenu() { 454 { 455 add(new AbstractAction() { 456 { 457 putValue(NAME, tr("Hide this button")); 458 putValue(SHORT_DESCRIPTION, tr("Click the arrow at the bottom to show it again.")); 459 } 460 @Override 461 public void actionPerformed(ActionEvent e) { 462 b.setButtonHidden(true); 463 validateToolBarsVisibility(); 464 } 465 }); 466 } 467 })); 468 } 469 470 class ListAllButtonsAction extends AbstractAction { 471 472 private JButton button; 473 private Collection<? extends HideableButton> buttons; 474 475 476 public ListAllButtonsAction(Collection<? extends HideableButton> buttons) { 477 this.buttons = buttons; 478 putValue(NAME, ">>"); 479 } 480 481 public void setButton(JButton button) { 482 this.button = button; 483 } 484 485 @Override 486 public void actionPerformed(ActionEvent e) { 487 JPopupMenu menu = new JPopupMenu(); 488 for (HideableButton b : buttons) { 489 final HideableButton t = b; 490 menu.add(new JCheckBoxMenuItem(new AbstractAction() { 491 { 492 putValue(NAME, t.getActionName()); 493 putValue(SMALL_ICON, t.getIcon()); 494 putValue(SELECTED_KEY, t.isButtonVisible()); 495 putValue(SHORT_DESCRIPTION, tr("Hide or show this toggle button")); 496 } 497 @Override 498 public void actionPerformed(ActionEvent e) { 499 if ((Boolean) getValue(SELECTED_KEY)) { 500 t.showButton(); 501 } else { 502 t.hideButton(); 503 } 504 validateToolBarsVisibility(); 505 } 506 })); 507 } 508 Rectangle bounds = button.getBounds(); 509 menu.show(button, bounds.x + bounds.width, 0); 510 } 511 } 512 513 public void validateToolBarsVisibility() { 514 for (IconToggleButton b : allDialogButtons) { 515 b.applyButtonHiddenPreferences(); 516 } 517 toolBarToggle.repaint(); 518 for (IconToggleButton b : allMapModeButtons) { 519 b.applyButtonHiddenPreferences(); 520 } 521 toolBarActions.repaint(); 522 } 523 524 /** 525 * Replies the instance of a toggle dialog of type <code>type</code> managed by this 526 * map frame 527 * 528 * @param <T> 529 * @param type the class of the toggle dialog, i.e. UserListDialog.class 530 * @return the instance of a toggle dialog of type <code>type</code> managed by this 531 * map frame; null, if no such dialog exists 532 * 533 */ 534 public <T> T getToggleDialog(Class<T> type) { 535 return dialogsPanel.getToggleDialog(type); 536 } 537 538 /** 539 * Remember the current width of the (possibly resized) toggle dialog area 540 */ 541 public void rememberToggleDialogWidth() { 542 Main.pref.putInteger("toggleDialogs.width", dialogsPanel.getWidth()); 543 } 544 545 /* 546 * Remove panel from top of MapView by class 547 */ 548 public void removeTopPanel(Class<?> type) { 549 int n = leftPanel.getComponentCount(); 550 for (int i=0; i<n; i++) { 551 Component c = leftPanel.getComponent(i); 552 if (type.isInstance(c)) { 553 leftPanel.remove(i); 554 leftPanel.doLayout(); 555 // repaint(); 556 return; 557 } 558 } 559 } 560 561 /* 562 * Find panel on top of MapView by class 563 */ 564 public <T> T getTopPanel(Class<T> type) { 565 int n = leftPanel.getComponentCount(); 566 for (int i=0; i<n; i++) { 567 Component c = leftPanel.getComponent(i); 568 if (type.isInstance(c)) 569 return type.cast(c); 570 } 571 return null; 572 } 573 574 /** 575 * Add component @param c on top of MapView 576 */ 577 public void addTopPanel(Component c) { 578 leftPanel.add(c, GBC.eol().fill(GBC.HORIZONTAL), leftPanel.getComponentCount()-1); 579 leftPanel.doLayout(); 580 c.doLayout(); 581 } 582 583 /** 584 * Interface to notify listeners of the change of the mapMode. 585 */ 586 public interface MapModeChangeListener { 587 void mapModeChange(MapMode oldMapMode, MapMode newMapMode); 588 } 589 590 /** 591 * the mapMode listeners 592 */ 593 private static final CopyOnWriteArrayList<MapModeChangeListener> mapModeChangeListeners = new CopyOnWriteArrayList<MapModeChangeListener>(); 594 595 private PreferenceChangedListener sidetoolbarPreferencesChangedListener; 596 /** 597 * Adds a mapMode change listener 598 * 599 * @param listener the listener. Ignored if null or already registered. 600 */ 601 public static void addMapModeChangeListener(MapModeChangeListener listener) { 602 if (listener != null) { 603 mapModeChangeListeners.addIfAbsent(listener); 604 } 605 } 606 /** 607 * Removes a mapMode change listener 608 * 609 * @param listener the listener. Ignored if null or already registered. 610 */ 611 public static void removeMapModeChangeListener(MapModeChangeListener listener) { 612 mapModeChangeListeners.remove(listener); 613 } 614 615 protected static void fireMapModeChanged(MapMode oldMapMode, MapMode newMapMode) { 616 for (MapModeChangeListener l : mapModeChangeListeners) { 617 l.mapModeChange(oldMapMode, newMapMode); 618 } 619 } 620 621 @Override 622 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 623 boolean modeChanged = false; 624 if (mapMode == null || !mapMode.layerIsSupported(newLayer)) { 625 MapMode newMapMode = getLastMapMode(newLayer); 626 modeChanged = newMapMode != mapMode; 627 if (newMapMode != null) { 628 selectMapMode(newMapMode, newLayer); // it would be nice to select first supported mode when layer is first selected, but it don't work well with for example editgpx layer 629 } else if (mapMode != null) { 630 mapMode.exitMode(); // if new mode is null - simply exit from previous mode 631 } 632 } 633 // if this is really a change (and not the first active layer) 634 if (oldLayer != null) { 635 if (!modeChanged && mapMode != null) { 636 // Let mapmodes know about new active layer 637 mapMode.exitMode(); 638 mapMode.enterMode(); 639 } 640 // invalidate repaint cache 641 Main.map.mapView.preferenceChanged(null); 642 } 643 644 // After all listeners notice new layer, some buttons will be disabled/enabled 645 // and possibly need to be hidden/shown. 646 SwingUtilities.invokeLater(new Runnable() { 647 public void run() { 648 validateToolBarsVisibility(); 649 } 650 }); 651 } 652 653 654 private MapMode getLastMapMode(Layer newLayer) { 655 MapMode mode = lastMapMode.get(newLayer); 656 if (mode == null) { 657 // if no action is selected - try to select default action 658 Action defaultMode = getDefaultButtonAction(); 659 if (defaultMode instanceof MapMode && ((MapMode)defaultMode).layerIsSupported(newLayer)) { 660 mode = (MapMode) defaultMode; 661 } 662 } 663 return mode; 664 } 665 666 @Override 667 public void layerAdded(Layer newLayer) { } 668 669 @Override 670 public void layerRemoved(Layer oldLayer) { 671 lastMapMode.remove(oldLayer); 672 } 673 }