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

Last change on this file since 10345 was 10345, checked in by Don-vip, 8 years ago

fix #12937 - Use the new LayerChangeListener (patch by michael2402) - gsoc-core

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