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

Last change on this file since 12597 was 12504, checked in by Don-vip, 7 years ago

see #11924 - fix one deprecation warning for InputEvent.ALT_GRAPH_MASK

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