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

Last change on this file since 13037 was 13034, checked in by bastiK, 7 years ago

remove obsolete comment

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