source: josm/trunk/src/org/openstreetmap/josm/gui/MapFrame.java@ 6020

Last change on this file since 6020 was 6020, checked in by akks, 11 years ago

see #8652: use Tab for toggling panels even if focus is on toolbar buttons or scaling slider

  • Property svn:eol-style set to native
File size: 27.9 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Container;
9import java.awt.Dimension;
10import java.awt.Font;
11import java.awt.GridBagLayout;
12import java.awt.Rectangle;
13import java.awt.event.ActionEvent;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseWheelEvent;
16import java.awt.event.MouseWheelListener;
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.HashMap;
20import java.util.List;
21import java.util.Map;
22import java.util.concurrent.CopyOnWriteArrayList;
23
24import javax.swing.AbstractAction;
25import javax.swing.AbstractButton;
26import javax.swing.Action;
27import javax.swing.BoxLayout;
28import javax.swing.ButtonGroup;
29import javax.swing.JButton;
30import javax.swing.JCheckBoxMenuItem;
31import javax.swing.JComponent;
32import javax.swing.JPanel;
33import javax.swing.JPopupMenu;
34import javax.swing.JSplitPane;
35import javax.swing.JToolBar;
36import javax.swing.KeyStroke;
37import javax.swing.SwingUtilities;
38import javax.swing.border.Border;
39import javax.swing.event.PopupMenuEvent;
40import javax.swing.event.PopupMenuListener;
41import javax.swing.plaf.basic.BasicSplitPaneDivider;
42import javax.swing.plaf.basic.BasicSplitPaneUI;
43
44import org.openstreetmap.josm.Main;
45import org.openstreetmap.josm.actions.LassoModeAction;
46import org.openstreetmap.josm.actions.mapmode.DeleteAction;
47import org.openstreetmap.josm.actions.mapmode.DrawAction;
48import org.openstreetmap.josm.actions.mapmode.ExtrudeAction;
49import org.openstreetmap.josm.actions.mapmode.ImproveWayAccuracyAction;
50import org.openstreetmap.josm.actions.mapmode.MapMode;
51import org.openstreetmap.josm.actions.mapmode.ParallelWayAction;
52import org.openstreetmap.josm.actions.mapmode.SelectAction;
53import org.openstreetmap.josm.actions.mapmode.ZoomAction;
54import org.openstreetmap.josm.data.Preferences;
55import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
56import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
57import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
58import org.openstreetmap.josm.gui.NavigatableComponent.ViewportData;
59import org.openstreetmap.josm.gui.dialogs.ChangesetDialog;
60import org.openstreetmap.josm.gui.dialogs.CommandStackDialog;
61import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
62import org.openstreetmap.josm.gui.dialogs.DialogsPanel;
63import org.openstreetmap.josm.gui.dialogs.FilterDialog;
64import org.openstreetmap.josm.gui.dialogs.HistoryDialog;
65import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
66import org.openstreetmap.josm.gui.dialogs.MapPaintDialog;
67import org.openstreetmap.josm.gui.dialogs.RelationListDialog;
68import org.openstreetmap.josm.gui.dialogs.SelectionListDialog;
69import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
70import org.openstreetmap.josm.gui.dialogs.UserListDialog;
71import org.openstreetmap.josm.gui.dialogs.ValidatorDialog;
72import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog;
73import org.openstreetmap.josm.gui.layer.Layer;
74import org.openstreetmap.josm.tools.Destroyable;
75import org.openstreetmap.josm.tools.GBC;
76import org.openstreetmap.josm.tools.Shortcut;
77
78
79/**
80 * One Map frame with one dataset behind. This is the container gui class whose
81 * display can be set to the different views.
82 *
83 * @author imi
84 */
85public class MapFrame extends JPanel implements Destroyable, LayerChangeListener {
86
87 /**
88 * The current mode, this frame operates.
89 */
90 public MapMode mapMode;
91
92 /**
93 * The view control displayed.
94 */
95 public final MapView mapView;
96
97 /**
98 * The toolbar with the action icons. To add new toggle dialog buttons,
99 * use addToggleDialog, to add a new map mode button use addMapMode.
100 */
101 private JComponent sideToolBar = new JToolBar(JToolBar.VERTICAL);
102 private final ButtonGroup toolBarActionsGroup = new ButtonGroup();
103 private final JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
104 private final JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL);
105
106 private final List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
107 private final List<MapMode> mapModes = new ArrayList<MapMode>();
108 private final List<IconToggleButton> allDialogButtons = new ArrayList<IconToggleButton>();
109 public final List<IconToggleButton> allMapModeButtons = new ArrayList<IconToggleButton>();
110
111 private final ListAllButtonsAction listAllDialogsAction = new ListAllButtonsAction(allDialogButtons);
112 private final ListAllButtonsAction listAllMapModesAction = new ListAllButtonsAction(allMapModeButtons);
113 private final JButton listAllToggleDialogsButton = new JButton(listAllDialogsAction);
114 private final JButton listAllMapModesButton = new JButton(listAllMapModesAction);
115 {
116 listAllDialogsAction.setButton(listAllToggleDialogsButton);
117 listAllMapModesAction.setButton(listAllMapModesButton);
118 }
119
120 // Toggle dialogs
121 public ConflictDialog conflictDialog;
122 public FilterDialog filterDialog;
123 public RelationListDialog relationListDialog;
124 public ValidatorDialog validatorDialog;
125 public SelectionListDialog selectionListDialog;
126 public PropertiesDialog propertiesDialog;
127
128 // Map modes
129 public final SelectAction mapModeSelect;
130 private final Map<Layer, MapMode> lastMapMode = new HashMap<Layer, MapMode>();
131 private final MapMode mapModeDraw;
132 private final MapMode mapModeZoom;
133
134 /**
135 * The status line below the map
136 */
137 public MapStatus statusLine;
138
139 /**
140 * The split pane with the mapview (leftPanel) and toggle dialogs (dialogsPanel).
141 */
142 private final JSplitPane splitPane;
143 private final JPanel leftPanel;
144 private final DialogsPanel dialogsPanel;
145
146 private final boolean unregisterTab;
147
148 /**
149 * Default width of the toggle dialog area.
150 */
151 public static final int DEF_TOGGLE_DLG_WIDTH = 330;
152
153 /**
154 * Constructs a new {@code MapFrame}.
155 * @param contentPane The content pane used to register shortcuts in its
156 * {@link javax.swing.InputMap} and {@link javax.swing.ActionMap}
157 * @param viewportData the initial viewport of the map. Can be null, then
158 * the viewport is derived from the layer data.
159 */
160 public MapFrame(JPanel contentPane, ViewportData viewportData) {
161 setSize(400,400);
162 setLayout(new BorderLayout());
163
164 mapView = new MapView(contentPane, viewportData);
165 new FileDrop(mapView);
166
167 splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
168
169 leftPanel = new JPanel();
170 leftPanel.setLayout(new GridBagLayout());
171 leftPanel.add(mapView, GBC.std().fill());
172 splitPane.setLeftComponent(leftPanel);
173
174 dialogsPanel = new DialogsPanel(splitPane);
175 splitPane.setRightComponent(dialogsPanel);
176
177 /**
178 * All additional space goes to the mapView
179 */
180 splitPane.setResizeWeight(1.0);
181
182 /**
183 * Some beautifications.
184 */
185 splitPane.setDividerSize(5);
186 splitPane.setBorder(null);
187 splitPane.setUI(new BasicSplitPaneUI() {
188 @Override
189 public BasicSplitPaneDivider createDefaultDivider() {
190 return new BasicSplitPaneDivider(this) {
191 @Override
192 public void setBorder(Border b) {
193 }
194 };
195 }
196 });
197
198 // JSplitPane supports F6 and F8 shortcuts by default, but we need them for Audio actions
199 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), new Object());
200 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), new Object());
201
202 add(splitPane, BorderLayout.CENTER);
203
204 dialogsPanel.setLayout(new BoxLayout(dialogsPanel, BoxLayout.Y_AXIS));
205 dialogsPanel.setPreferredSize(new Dimension(Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH), 0));
206 dialogsPanel.setMinimumSize(new Dimension(24, 0));
207 mapView.setMinimumSize(new Dimension(10,0));
208
209 // toolBarActions, map mode buttons
210 addMapMode(new IconToggleButton(mapModeSelect = new SelectAction(this)));
211 addMapMode(new IconToggleButton(new LassoModeAction(), true));
212 addMapMode(new IconToggleButton(mapModeDraw = new DrawAction(this)));
213 addMapMode(new IconToggleButton(mapModeZoom = new ZoomAction(this)));
214 addMapMode(new IconToggleButton(new DeleteAction(this), true));
215 addMapMode(new IconToggleButton(new ParallelWayAction(this), true));
216 addMapMode(new IconToggleButton(new ExtrudeAction(this), true));
217 addMapMode(new IconToggleButton(new ImproveWayAccuracyAction(Main.map), true));
218 toolBarActionsGroup.setSelected(allMapModeButtons.get(0).getModel(), true);
219 toolBarActions.setFloatable(false);
220
221 // toolBarToggles, toggle dialog buttons
222 LayerListDialog.createInstance(this);
223 addToggleDialog(LayerListDialog.getInstance());
224 addToggleDialog(propertiesDialog = new PropertiesDialog(this));
225 addToggleDialog(selectionListDialog = new SelectionListDialog());
226 addToggleDialog(relationListDialog = new RelationListDialog());
227 addToggleDialog(new CommandStackDialog(this));
228 addToggleDialog(new UserListDialog());
229 addToggleDialog(new HistoryDialog(), true);
230 addToggleDialog(conflictDialog = new ConflictDialog());
231 addToggleDialog(validatorDialog = new ValidatorDialog());
232 addToggleDialog(filterDialog = new FilterDialog());
233 addToggleDialog(new ChangesetDialog(this), true);
234 addToggleDialog(new MapPaintDialog());
235 toolBarToggle.setFloatable(false);
236
237 // status line below the map
238 statusLine = new MapStatus(this);
239 MapView.addLayerChangeListener(this);
240
241 unregisterTab = Shortcut.findShortcut(KeyEvent.VK_TAB, 0)!=null;
242 if (unregisterTab) {
243 for (JComponent c: allDialogButtons) c.setFocusTraversalKeysEnabled(false);
244 for (JComponent c: allMapModeButtons) c.setFocusTraversalKeysEnabled(false);
245 }
246 }
247
248 public boolean selectSelectTool(boolean onlyIfModeless) {
249 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false))
250 return false;
251
252 return selectMapMode(mapModeSelect);
253 }
254
255 public boolean selectDrawTool(boolean onlyIfModeless) {
256 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false))
257 return false;
258
259 return selectMapMode(mapModeDraw);
260 }
261
262 public boolean selectZoomTool(boolean onlyIfModeless) {
263 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false))
264 return false;
265
266 return selectMapMode(mapModeZoom);
267 }
268
269 /**
270 * Called as some kind of destructor when the last layer has been removed.
271 * Delegates the call to all Destroyables within this component (e.g. MapModes)
272 */
273 @Override
274 public void destroy() {
275 MapView.removeLayerChangeListener(this);
276 dialogsPanel.destroy();
277 Main.pref.removePreferenceChangeListener(sidetoolbarPreferencesChangedListener);
278 for (int i = 0; i < toolBarActions.getComponentCount(); ++i) {
279 if (toolBarActions.getComponent(i) instanceof Destroyable) {
280 ((Destroyable)toolBarActions.getComponent(i)).destroy();
281 }
282 }
283 for (int i = 0; i < toolBarToggle.getComponentCount(); ++i) {
284 if (toolBarToggle.getComponent(i) instanceof Destroyable) {
285 ((Destroyable)toolBarToggle.getComponent(i)).destroy();
286 }
287 }
288
289 // MapFrame gets destroyed when the last layer is removed, but the status line background
290 // thread that collects the information doesn't get destroyed automatically.
291 if(statusLine.thread != null) {
292 try {
293 statusLine.thread.interrupt();
294 } catch (Exception e) {
295 e.printStackTrace();
296 }
297 }
298 mapView.destroy();
299 }
300
301 public Action getDefaultButtonAction() {
302 return ((AbstractButton)toolBarActions.getComponent(0)).getAction();
303 }
304
305 /**
306 * Open all ToggleDialogs that have their preferences property set. Close all others.
307 */
308 public void initializeDialogsPane() {
309 dialogsPanel.initialize(allDialogs);
310 }
311
312 public IconToggleButton addToggleDialog(final ToggleDialog dlg) {
313 return addToggleDialog(dlg, false);
314 }
315
316 /**
317 * Call this to add new toggle dialogs to the left button-list
318 * @param dlg The toggle dialog. It must not be in the list already.
319 */
320 public IconToggleButton addToggleDialog(final ToggleDialog dlg, boolean isExpert) {
321 final IconToggleButton button = new IconToggleButton(dlg.getToggleAction(), isExpert);
322 button.setShowHideButtonListener(dlg);
323 button.setInheritsPopupMenu(true);
324 dlg.setButton(button);
325 toolBarToggle.add(button);
326 allDialogs.add(dlg);
327 allDialogButtons.add(button);
328 button.applyButtonHiddenPreferences();
329 if (dialogsPanel.initialized) {
330 dialogsPanel.add(dlg);
331 }
332 return button;
333 }
334
335
336
337 public void addMapMode(IconToggleButton b) {
338 if (b.getAction() instanceof MapMode) {
339 mapModes.add((MapMode) b.getAction());
340 } else
341 throw new IllegalArgumentException("MapMode action must be subclass of MapMode");
342 allMapModeButtons.add(b);
343 toolBarActionsGroup.add(b);
344 toolBarActions.add(b);
345 b.applyButtonHiddenPreferences();
346 b.setInheritsPopupMenu(true);
347 }
348
349 /**
350 * Fires an property changed event "visible".
351 * @param aFlag {@code true} if display should be visible
352 */
353 @Override public void setVisible(boolean aFlag) {
354 boolean old = isVisible();
355 super.setVisible(aFlag);
356 if (old != aFlag) {
357 firePropertyChange("visible", old, aFlag);
358 }
359 }
360
361 /**
362 * Change the operating map mode for the view. Will call unregister on the
363 * old MapMode and register on the new one. Now this function also verifies
364 * if new map mode is correct mode for current layer and does not change mode
365 * in such cases.
366 * @param newMapMode The new mode to set.
367 * @return {@code true} if mode is really selected
368 */
369 public boolean selectMapMode(MapMode newMapMode) {
370 return selectMapMode(newMapMode, mapView.getActiveLayer());
371 }
372
373 /**
374 * Another version of the selectMapMode for changing layer action.
375 * Pass newly selected layer to this method.
376 * @param newMapMode The new mode to set.
377 * @param newLayer newly selected layer
378 * @return {@code true} if mode is really selected
379 */
380 public boolean selectMapMode(MapMode newMapMode, Layer newLayer) {
381 if (newMapMode == null || !newMapMode.layerIsSupported(newLayer))
382 return false;
383
384 MapMode oldMapMode = this.mapMode;
385 if (newMapMode == oldMapMode)
386 return true;
387 if (oldMapMode != null) {
388 oldMapMode.exitMode();
389 }
390 this.mapMode = newMapMode;
391 newMapMode.enterMode();
392 lastMapMode.put(newLayer, newMapMode);
393 fireMapModeChanged(oldMapMode, newMapMode);
394 return true;
395 }
396
397 /**
398 * Fill the given panel by adding all necessary components to the different
399 * locations.
400 *
401 * @param panel The container to fill. Must have an BorderLayout.
402 */
403 public void fillPanel(Container panel) {
404 panel.add(this, BorderLayout.CENTER);
405
406 /**
407 * sideToolBar: add map modes icons
408 */
409 if(Main.pref.getBoolean("sidetoolbar.mapmodes.visible", true)) {
410 toolBarActions.setAlignmentX(0.5f);
411 toolBarActions.setInheritsPopupMenu(true);
412 sideToolBar.add(toolBarActions);
413 listAllMapModesButton.setAlignmentX(0.5f);
414 listAllMapModesButton.setBorder(null);
415 listAllMapModesButton.setFont(listAllMapModesButton.getFont().deriveFont(Font.PLAIN));
416 listAllMapModesButton.setInheritsPopupMenu(true);
417 sideToolBar.add(listAllMapModesButton);
418 }
419
420 /**
421 * sideToolBar: add toggle dialogs icons
422 */
423 if(Main.pref.getBoolean("sidetoolbar.toggledialogs.visible", true)) {
424 ((JToolBar)sideToolBar).addSeparator(new Dimension(0,18));
425 toolBarToggle.setAlignmentX(0.5f);
426 toolBarToggle.setInheritsPopupMenu(true);
427 sideToolBar.add(toolBarToggle);
428 listAllToggleDialogsButton.setAlignmentX(0.5f);
429 listAllToggleDialogsButton.setBorder(null);
430 listAllToggleDialogsButton.setFont(listAllToggleDialogsButton.getFont().deriveFont(Font.PLAIN));
431 listAllToggleDialogsButton.setInheritsPopupMenu(true);
432 sideToolBar.add(listAllToggleDialogsButton);
433 }
434
435 /**
436 * sideToolBar: add dynamic popup menu
437 */
438 sideToolBar.setComponentPopupMenu(new JPopupMenu() {
439 final int staticMenuEntryCount = 2;
440 JCheckBoxMenuItem doNotHide = new JCheckBoxMenuItem(new AbstractAction(tr("Do not hide toolbar")) {
441 @Override
442 public void actionPerformed(ActionEvent e) {
443 boolean sel = ((JCheckBoxMenuItem) e.getSource()).getState();
444 Main.pref.put("sidetoolbar.always-visible", sel);
445 }
446 });
447 {
448 addPopupMenuListener(new PopupMenuListener() {
449 @Override
450 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
451 final Object src = ((JPopupMenu)e.getSource()).getInvoker();
452 if (src instanceof IconToggleButton) {
453 insert(new Separator(), 0);
454 insert(new AbstractAction() {
455 {
456 putValue(NAME, tr("Hide this button"));
457 putValue(SHORT_DESCRIPTION, tr("Click the arrow at the bottom to show it again."));
458 }
459 @Override
460 public void actionPerformed(ActionEvent e) {
461 ((IconToggleButton)src).setButtonHidden(true);
462 validateToolBarsVisibility();
463 }
464 }, 0);
465 }
466 doNotHide.setSelected(Main.pref.getBoolean("sidetoolbar.always-visible", true));
467 }
468 @Override
469 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
470 while (getComponentCount() > staticMenuEntryCount) {
471 remove(0);
472 }
473 }
474 @Override
475 public void popupMenuCanceled(PopupMenuEvent e) {}
476 });
477
478 add(new AbstractAction(tr("Hide edit toolbar")) {
479 @Override
480 public void actionPerformed(ActionEvent e) {
481 Main.pref.put("sidetoolbar.visible", false);
482 }
483 });
484 add(doNotHide);
485 }
486 });
487 ((JToolBar)sideToolBar).setFloatable(false);
488
489 /**
490 * sideToolBar: decide scroll- and visibility
491 */
492 if(Main.pref.getBoolean("sidetoolbar.scrollable", true)) {
493 final ScrollViewport svp = new ScrollViewport(sideToolBar, ScrollViewport.VERTICAL_DIRECTION);
494 svp.addMouseWheelListener(new MouseWheelListener() {
495 @Override
496 public void mouseWheelMoved(MouseWheelEvent e) {
497 svp.scroll(0, e.getUnitsToScroll() * 5);
498 }
499 });
500 sideToolBar = svp;
501 }
502 sideToolBar.setVisible(Main.pref.getBoolean("sidetoolbar.visible", true));
503 sidetoolbarPreferencesChangedListener = new Preferences.PreferenceChangedListener() {
504 @Override
505 public void preferenceChanged(PreferenceChangeEvent e) {
506 if ("sidetoolbar.visible".equals(e.getKey())) {
507 sideToolBar.setVisible(Main.pref.getBoolean("sidetoolbar.visible"));
508 }
509 }
510 };
511 Main.pref.addPreferenceChangeListener(sidetoolbarPreferencesChangedListener);
512
513 /**
514 * sideToolBar: add it to the panel
515 */
516 panel.add(sideToolBar, BorderLayout.WEST);
517
518 /**
519 * statusLine: add to panel
520 */
521 if (statusLine != null && Main.pref.getBoolean("statusline.visible", true)) {
522 panel.add(statusLine, BorderLayout.SOUTH);
523 }
524 }
525
526 class ListAllButtonsAction extends AbstractAction {
527
528 private JButton button;
529 private Collection<? extends HideableButton> buttons;
530
531
532 public ListAllButtonsAction(Collection<? extends HideableButton> buttons) {
533 this.buttons = buttons;
534 putValue(NAME, ">>");
535 }
536
537 public void setButton(JButton button) {
538 this.button = button;
539 }
540
541 @Override
542 public void actionPerformed(ActionEvent e) {
543 JPopupMenu menu = new JPopupMenu();
544 for (HideableButton b : buttons) {
545 final HideableButton t = b;
546 menu.add(new JCheckBoxMenuItem(new AbstractAction() {
547 {
548 putValue(NAME, t.getActionName());
549 putValue(SMALL_ICON, t.getIcon());
550 putValue(SELECTED_KEY, t.isButtonVisible());
551 putValue(SHORT_DESCRIPTION, tr("Hide or show this toggle button"));
552 }
553 @Override
554 public void actionPerformed(ActionEvent e) {
555 if ((Boolean) getValue(SELECTED_KEY)) {
556 t.showButton();
557 } else {
558 t.hideButton();
559 }
560 validateToolBarsVisibility();
561 }
562 }));
563 }
564 Rectangle bounds = button.getBounds();
565 menu.show(button, bounds.x + bounds.width, 0);
566 }
567 }
568
569 public void validateToolBarsVisibility() {
570 for (IconToggleButton b : allDialogButtons) {
571 b.applyButtonHiddenPreferences();
572 }
573 toolBarToggle.repaint();
574 for (IconToggleButton b : allMapModeButtons) {
575 b.applyButtonHiddenPreferences();
576 }
577 toolBarActions.repaint();
578 }
579
580 /**
581 * Replies the instance of a toggle dialog of type <code>type</code> managed by this
582 * map frame
583 *
584 * @param <T>
585 * @param type the class of the toggle dialog, i.e. UserListDialog.class
586 * @return the instance of a toggle dialog of type <code>type</code> managed by this
587 * map frame; null, if no such dialog exists
588 *
589 */
590 public <T> T getToggleDialog(Class<T> type) {
591 return dialogsPanel.getToggleDialog(type);
592 }
593
594 public void setDialogsPanelVisible(boolean visible) {
595 rememberToggleDialogWidth();
596 dialogsPanel.setVisible(visible);
597 splitPane.setDividerLocation(visible?splitPane.getWidth()-Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH):0);
598 splitPane.setDividerSize(visible?5:0);
599 }
600
601 /**
602 * Remember the current width of the (possibly resized) toggle dialog area
603 */
604 public void rememberToggleDialogWidth() {
605 if (dialogsPanel.isVisible()) {
606 Main.pref.putInteger("toggleDialogs.width", splitPane.getWidth()-splitPane.getDividerLocation());
607 }
608 }
609
610 /*
611 * Remove panel from top of MapView by class */
612 public void removeTopPanel(Class<?> type) {
613 int n = leftPanel.getComponentCount();
614 for (int i=0; i<n; i++) {
615 Component c = leftPanel.getComponent(i);
616 if (type.isInstance(c)) {
617 leftPanel.remove(i);
618 leftPanel.doLayout();
619 // repaint();
620 return;
621 }
622 }
623 }
624
625 /*
626 * Find panel on top of MapView by class
627 */
628 public <T> T getTopPanel(Class<T> type) {
629 int n = leftPanel.getComponentCount();
630 for (int i=0; i<n; i++) {
631 Component c = leftPanel.getComponent(i);
632 if (type.isInstance(c))
633 return type.cast(c);
634 }
635 return null;
636 }
637
638 /**
639 * Add component @param c on top of MapView
640 */
641 public void addTopPanel(Component c) {
642 leftPanel.add(c, GBC.eol().fill(GBC.HORIZONTAL), leftPanel.getComponentCount()-1);
643 leftPanel.doLayout();
644 c.doLayout();
645 }
646
647 /**
648 * Interface to notify listeners of the change of the mapMode.
649 */
650 public interface MapModeChangeListener {
651 void mapModeChange(MapMode oldMapMode, MapMode newMapMode);
652 }
653
654 /**
655 * the mapMode listeners
656 */
657 private static final CopyOnWriteArrayList<MapModeChangeListener> mapModeChangeListeners = new CopyOnWriteArrayList<MapModeChangeListener>();
658
659 private PreferenceChangedListener sidetoolbarPreferencesChangedListener;
660 /**
661 * Adds a mapMode change listener
662 *
663 * @param listener the listener. Ignored if null or already registered.
664 */
665 public static void addMapModeChangeListener(MapModeChangeListener listener) {
666 if (listener != null) {
667 mapModeChangeListeners.addIfAbsent(listener);
668 }
669 }
670 /**
671 * Removes a mapMode change listener
672 *
673 * @param listener the listener. Ignored if null or already registered.
674 */
675 public static void removeMapModeChangeListener(MapModeChangeListener listener) {
676 mapModeChangeListeners.remove(listener);
677 }
678
679 protected static void fireMapModeChanged(MapMode oldMapMode, MapMode newMapMode) {
680 for (MapModeChangeListener l : mapModeChangeListeners) {
681 l.mapModeChange(oldMapMode, newMapMode);
682 }
683 }
684
685 @Override
686 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
687 boolean modeChanged = false;
688 if (mapMode == null || !mapMode.layerIsSupported(newLayer)) {
689 MapMode newMapMode = getLastMapMode(newLayer);
690 modeChanged = newMapMode != mapMode;
691 if (newMapMode != null) {
692 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
693 } else if (mapMode != null) {
694 mapMode.exitMode(); // if new mode is null - simply exit from previous mode
695 }
696 }
697 // if this is really a change (and not the first active layer)
698 if (oldLayer != null) {
699 if (!modeChanged && mapMode != null) {
700 // Let mapmodes know about new active layer
701 mapMode.exitMode();
702 mapMode.enterMode();
703 }
704 // invalidate repaint cache
705 Main.map.mapView.preferenceChanged(null);
706 }
707
708 // After all listeners notice new layer, some buttons will be disabled/enabled
709 // and possibly need to be hidden/shown.
710 SwingUtilities.invokeLater(new Runnable() {
711 @Override public void run() {
712 validateToolBarsVisibility();
713 }
714 });
715 }
716
717
718 private MapMode getLastMapMode(Layer newLayer) {
719 MapMode mode = lastMapMode.get(newLayer);
720 if (mode == null) {
721 // if no action is selected - try to select default action
722 Action defaultMode = getDefaultButtonAction();
723 if (defaultMode instanceof MapMode && ((MapMode)defaultMode).layerIsSupported(newLayer)) {
724 mode = (MapMode) defaultMode;
725 }
726 }
727 return mode;
728 }
729
730 @Override
731 public void layerAdded(Layer newLayer) { }
732
733 @Override
734 public void layerRemoved(Layer oldLayer) {
735 lastMapMode.remove(oldLayer);
736 }
737}
Note: See TracBrowser for help on using the repository browser.