source: josm/trunk/src/org/openstreetmap/josm/actions/AutoScaleAction.java@ 17786

Last change on this file since 17786 was 17786, checked in by simon04, 4 years ago

see #20741 - Fix typos in code and strings (patch by gaben)

  • Property svn:eol-style set to native
File size: 17.7 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[403]2package org.openstreetmap.josm.actions;
3
[2477]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[948]5import static org.openstreetmap.josm.tools.I18n.marktr;
[403]6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.event.ActionEvent;
[458]9import java.awt.event.KeyEvent;
[10131]10import java.awt.geom.Area;
[8171]11import java.util.ArrayList;
[403]12import java.util.Collection;
[1750]13import java.util.HashSet;
[1953]14import java.util.List;
[14221]15import java.util.Objects;
[11288]16import java.util.concurrent.TimeUnit;
[403]17
18import javax.swing.JOptionPane;
[5958]19import javax.swing.event.ListSelectionListener;
20import javax.swing.event.TreeSelectionListener;
[403]21
[2477]22import org.openstreetmap.josm.data.Bounds;
[8171]23import org.openstreetmap.josm.data.DataSource;
[3973]24import org.openstreetmap.josm.data.conflict.Conflict;
[8200]25import org.openstreetmap.josm.data.osm.DataSet;
[13926]26import org.openstreetmap.josm.data.osm.IPrimitive;
27import org.openstreetmap.josm.data.osm.OsmData;
[403]28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
[5958]30import org.openstreetmap.josm.data.validation.TestError;
[12630]31import org.openstreetmap.josm.gui.MainApplication;
[5958]32import org.openstreetmap.josm.gui.MapFrame;
33import org.openstreetmap.josm.gui.MapFrameListener;
[2759]34import org.openstreetmap.josm.gui.MapView;
[15963]35import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
[12630]36import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
[1953]37import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
[5958]38import org.openstreetmap.josm.gui.dialogs.ValidatorDialog.ValidatorBoundingXYVisitor;
[403]39import org.openstreetmap.josm.gui.layer.Layer;
[12846]40import org.openstreetmap.josm.spi.preferences.Config;
[12620]41import org.openstreetmap.josm.tools.Logging;
[1084]42import org.openstreetmap.josm.tools.Shortcut;
[403]43
44/**
45 * Toggles the autoScale feature of the mapView
46 * @author imi
[14221]47 * @since 17
[403]48 */
49public class AutoScaleAction extends JosmAction {
50
[8900]51 /**
52 * A list of things we can zoom to. The zoom target is given depending on the mode.
[14221]53 * @since 14221
[8900]54 */
[14221]55 public enum AutoScaleMode {
56 /** Zoom the window so that all the data fills the window area */
57 DATA(marktr(/* ICON(dialogs/autoscale/) */ "data")),
58 /** Zoom the window so that all the data on the currently selected layer fills the window area */
59 LAYER(marktr(/* ICON(dialogs/autoscale/) */ "layer")),
60 /** Zoom the window so that only data which is currently selected fills the window area */
61 SELECTION(marktr(/* ICON(dialogs/autoscale/) */ "selection")),
62 /** Zoom to the first selected conflict */
63 CONFLICT(marktr(/* ICON(dialogs/autoscale/) */ "conflict")),
64 /** Zoom the view to last downloaded data */
65 DOWNLOAD(marktr(/* ICON(dialogs/autoscale/) */ "download")),
66 /** Zoom the view to problem */
67 PROBLEM(marktr(/* ICON(dialogs/autoscale/) */ "problem")),
68 /** Zoom to the previous zoomed to scale and location (zoom undo) */
69 PREVIOUS(marktr(/* ICON(dialogs/autoscale/) */ "previous")),
70 /** Zoom to the next zoomed to scale and location (zoom redo) */
71 NEXT(marktr(/* ICON(dialogs/autoscale/) */ "next"));
72
73 private final String label;
74
75 AutoScaleMode(String label) {
76 this.label = label;
77 }
78
79 /**
80 * Returns the English label. Used for retrieving icons.
81 * @return the English label
82 */
83 public String getEnglishLabel() {
84 return label;
85 }
86
87 /**
88 * Returns the localized label. Used for display
89 * @return the localized label
90 */
91 public String getLocalizedLabel() {
92 return tr(label);
93 }
94
95 /**
96 * Returns {@code AutoScaleMode} for a given English label
97 * @param englishLabel English label
98 * @return {@code AutoScaleMode} for given English label
[17786]99 * @throws IllegalArgumentException if English label is unknown
[14221]100 */
101 public static AutoScaleMode of(String englishLabel) {
102 for (AutoScaleMode v : values()) {
103 if (Objects.equals(v.label, englishLabel)) {
104 return v;
105 }
106 }
107 throw new IllegalArgumentException(englishLabel);
108 }
109 }
110
111 /**
112 * One of {@link AutoScaleMode}. Defines what we are zooming to.
[8900]113 */
[14221]114 private final AutoScaleMode mode;
[2685]115
[8171]116 /** Time of last zoom to bounds action */
117 protected long lastZoomTime = -1;
[17786]118 /** Last zoomed bounds */
[8171]119 protected int lastZoomArea = -1;
[5958]120
[2685]121 /**
122 * Zooms the current map view to the currently selected primitives.
[12636]123 * Does nothing if there either isn't a current map view or if there isn't a current data layer.
[2711]124 *
[2685]125 */
126 public static void zoomToSelection() {
[13926]127 OsmData<?, ?, ?, ?> dataSet = MainApplication.getLayerManager().getActiveData();
[10453]128 if (dataSet == null) {
[8171]129 return;
[10453]130 }
[13926]131 Collection<? extends IPrimitive> sel = dataSet.getSelected();
[2685]132 if (sel.isEmpty()) {
133 JOptionPane.showMessageDialog(
[14153]134 MainApplication.getMainFrame(),
[2685]135 tr("Nothing selected to zoom to."),
136 tr("Information"),
[8171]137 JOptionPane.INFORMATION_MESSAGE);
[2685]138 return;
139 }
[3251]140 zoomTo(sel);
141 }
142
[8900]143 /**
144 * Zooms the view to display the given set of primitives.
145 * @param sel The primitives to zoom to, e.g. the current selection.
146 */
[13926]147 public static void zoomTo(Collection<? extends IPrimitive> sel) {
[2685]148 BoundingXYVisitor bboxCalculator = new BoundingXYVisitor();
149 bboxCalculator.computeBoundingBox(sel);
150 if (bboxCalculator.getBounds() != null) {
[12630]151 MainApplication.getMap().mapView.zoomTo(bboxCalculator);
[2685]152 }
153 }
154
[8900]155 /**
156 * Performs the auto scale operation of the given mode without the need to create a new action.
[15007]157 * @param mode One of {@link AutoScaleMode}.
[14221]158 * @since 14221
[8900]159 */
[14221]160 public static void autoScale(AutoScaleMode mode) {
[3327]161 new AutoScaleAction(mode, false).autoScale();
162 }
163
[948]164 private static int getModeShortcut(String mode) {
165 int shortcut = -1;
[458]166
[7012]167 // TODO: convert this to switch/case and make sure the parsing still works
[8513]168 // CHECKSTYLE.OFF: LeftCurly
169 // CHECKSTYLE.OFF: RightCurly
[4921]170 /* leave as single line for shortcut overview parsing! */
171 if (mode.equals("data")) { shortcut = KeyEvent.VK_1; }
172 else if (mode.equals("layer")) { shortcut = KeyEvent.VK_2; }
173 else if (mode.equals("selection")) { shortcut = KeyEvent.VK_3; }
174 else if (mode.equals("conflict")) { shortcut = KeyEvent.VK_4; }
175 else if (mode.equals("download")) { shortcut = KeyEvent.VK_5; }
[5958]176 else if (mode.equals("problem")) { shortcut = KeyEvent.VK_6; }
[4921]177 else if (mode.equals("previous")) { shortcut = KeyEvent.VK_8; }
178 else if (mode.equals("next")) { shortcut = KeyEvent.VK_9; }
[8513]179 // CHECKSTYLE.ON: LeftCurly
180 // CHECKSTYLE.ON: RightCurly
[458]181
[948]182 return shortcut;
183 }
[403]184
[3327]185 /**
[5958]186 * Constructs a new {@code AutoScaleAction}.
[14221]187 * @param mode The autoscale mode (one of {@link AutoScaleMode})
[11713]188 * @param marker Must be set to false. Used only to differentiate from default constructor
[3327]189 */
[14221]190 private AutoScaleAction(AutoScaleMode mode, boolean marker) {
[11713]191 super(marker);
[3327]192 this.mode = mode;
193 }
194
[5958]195 /**
196 * Constructs a new {@code AutoScaleAction}.
[15007]197 * @param mode The autoscale mode (one of {@link AutoScaleMode})
[14221]198 * @since 14221
199 */
200 public AutoScaleAction(final AutoScaleMode mode) {
201 super(tr("Zoom to {0}", mode.getLocalizedLabel()), "dialogs/autoscale/" + mode.getEnglishLabel(),
202 tr("Zoom the view to {0}.", mode.getLocalizedLabel()),
203 Shortcut.registerShortcut("view:zoom" + mode.getEnglishLabel(),
204 tr("View: {0}", tr("Zoom to {0}", mode.getLocalizedLabel())),
205 getModeShortcut(mode.getEnglishLabel()), Shortcut.DIRECT), true, null, false);
206 String label = mode.getEnglishLabel();
207 String modeHelp = Character.toUpperCase(label.charAt(0)) + label.substring(1);
[14397]208 setHelpId("Action/AutoScale/" + modeHelp);
[948]209 this.mode = mode;
[7012]210 switch (mode) {
[14221]211 case DATA:
[14397]212 setHelpId(ht("/Action/ZoomToData"));
[7012]213 break;
[14221]214 case LAYER:
[14397]215 setHelpId(ht("/Action/ZoomToLayer"));
[7012]216 break;
[14221]217 case SELECTION:
[14397]218 setHelpId(ht("/Action/ZoomToSelection"));
[7012]219 break;
[14221]220 case CONFLICT:
[14397]221 setHelpId(ht("/Action/ZoomToConflict"));
[7012]222 break;
[14221]223 case PROBLEM:
[14397]224 setHelpId(ht("/Action/ZoomToProblem"));
[7012]225 break;
[14221]226 case DOWNLOAD:
[14397]227 setHelpId(ht("/Action/ZoomToDownload"));
[7012]228 break;
[14221]229 case PREVIOUS:
[14397]230 setHelpId(ht("/Action/ZoomToPrevious"));
[7012]231 break;
[14221]232 case NEXT:
[14397]233 setHelpId(ht("/Action/ZoomToNext"));
[7012]234 break;
235 default:
[8171]236 throw new IllegalArgumentException("Unknown mode: " + mode);
[2477]237 }
[5958]238 installAdapters();
[948]239 }
[403]240
[8900]241 /**
242 * Performs this auto scale operation for the mode this action is in.
243 */
[8171]244 public void autoScale() {
[12630]245 if (MainApplication.isDisplayingMapView()) {
246 MapView mapView = MainApplication.getMap().mapView;
[8171]247 switch (mode) {
[14221]248 case PREVIOUS:
[12630]249 mapView.zoomPrevious();
[7012]250 break;
[14221]251 case NEXT:
[12630]252 mapView.zoomNext();
[7012]253 break;
[14628]254 case PROBLEM:
255 modeProblem(new ValidatorBoundingXYVisitor());
256 break;
257 case DATA:
258 modeData(new BoundingXYVisitor());
259 break;
260 case LAYER:
261 modeLayer(new BoundingXYVisitor());
262 break;
263 case SELECTION:
264 case CONFLICT:
265 modeSelectionOrConflict(new BoundingXYVisitor());
266 break;
267 case DOWNLOAD:
[14985]268 modeDownload();
[14628]269 break;
[948]270 }
[14628]271 putValue("active", Boolean.TRUE);
[948]272 }
273 }
274
[6084]275 @Override
[1868]276 public void actionPerformed(ActionEvent e) {
277 autoScale();
278 }
279
[1953]280 /**
281 * Replies the first selected layer in the layer list dialog. null, if no
282 * such layer exists, either because the layer list dialog is not yet created
283 * or because no layer is selected.
[2512]284 *
[1953]285 * @return the first selected layer in the layer list dialog
286 */
287 protected Layer getFirstSelectedLayer() {
[12636]288 if (getLayerManager().getActiveLayer() == null) {
[9447]289 return null;
290 }
[12235]291 try {
292 List<Layer> layers = LayerListDialog.getInstance().getModel().getSelectedLayers();
293 if (!layers.isEmpty())
294 return layers.get(0);
295 } catch (IllegalStateException e) {
[12620]296 Logging.error(e);
[12235]297 }
298 return null;
[1953]299 }
300
[14628]301 private static void modeProblem(ValidatorBoundingXYVisitor v) {
[12630]302 TestError error = MainApplication.getMap().validatorDialog.getSelectedError();
[10216]303 if (error == null)
[14628]304 return;
[11383]305 v.visit(error);
[10216]306 if (v.getBounds() == null)
[14628]307 return;
308 MainApplication.getMap().mapView.zoomTo(v);
[10216]309 }
310
[14628]311 private static void modeData(BoundingXYVisitor v) {
[12636]312 for (Layer l : MainApplication.getLayerManager().getLayers()) {
[10216]313 l.visitBoundingBox(v);
314 }
[14628]315 MainApplication.getMap().mapView.zoomTo(v);
[10216]316 }
317
[14628]318 private void modeLayer(BoundingXYVisitor v) {
[10216]319 // try to zoom to the first selected layer
320 Layer l = getFirstSelectedLayer();
321 if (l == null)
[14628]322 return;
[10216]323 l.visitBoundingBox(v);
[14628]324 MainApplication.getMap().mapView.zoomTo(v);
[10216]325 }
326
[14628]327 private void modeSelectionOrConflict(BoundingXYVisitor v) {
[13955]328 Collection<IPrimitive> sel = new HashSet<>();
[14221]329 if (AutoScaleMode.SELECTION == mode) {
[13955]330 OsmData<?, ?, ?, ?> dataSet = getLayerManager().getActiveData();
[10453]331 if (dataSet != null) {
[13955]332 sel.addAll(dataSet.getSelected());
[10453]333 }
[10216]334 } else {
[12630]335 ConflictDialog conflictDialog = MainApplication.getMap().conflictDialog;
[13955]336 Conflict<? extends IPrimitive> c = conflictDialog.getSelectedConflict();
[10216]337 if (c != null) {
338 sel.add(c.getMy());
[12630]339 } else if (conflictDialog.getConflicts() != null) {
[13955]340 sel.addAll(conflictDialog.getConflicts().getMyConflictParties());
[1750]341 }
[10216]342 }
343 if (sel.isEmpty()) {
344 JOptionPane.showMessageDialog(
[14153]345 MainApplication.getMainFrame(),
[14221]346 AutoScaleMode.SELECTION == mode ? tr("Nothing selected to zoom to.") : tr("No conflicts to zoom to"),
[10216]347 tr("Information"),
348 JOptionPane.INFORMATION_MESSAGE);
[14628]349 return;
[10216]350 }
[13955]351 for (IPrimitive osm : sel) {
352 osm.accept(v);
[10216]353 }
[14628]354 if (v.getBounds() == null) {
355 return;
356 }
[6608]357
[14734]358 MainApplication.getMap().mapView.zoomTo(v);
[10216]359 }
[8171]360
[14985]361 private void modeDownload() {
[11288]362 if (lastZoomTime > 0 &&
[12853]363 System.currentTimeMillis() - lastZoomTime > Config.getPref().getLong("zoom.bounds.reset.time", TimeUnit.SECONDS.toMillis(10))) {
[10216]364 lastZoomTime = -1;
365 }
[14985]366 Bounds bbox = null;
[13434]367 final DataSet dataset = getLayerManager().getActiveDataSet();
[10216]368 if (dataset != null) {
369 List<DataSource> dataSources = new ArrayList<>(dataset.getDataSources());
370 int s = dataSources.size();
371 if (s > 0) {
372 if (lastZoomTime == -1 || lastZoomArea == -1 || lastZoomArea > s) {
373 lastZoomArea = s-1;
[14985]374 bbox = dataSources.get(lastZoomArea).bounds;
[10216]375 } else if (lastZoomArea > 0) {
376 lastZoomArea -= 1;
[14985]377 bbox = dataSources.get(lastZoomArea).bounds;
[8171]378 } else {
379 lastZoomArea = -1;
[13434]380 Area sourceArea = getLayerManager().getActiveDataSet().getDataSourceArea();
[10216]381 if (sourceArea != null) {
[14985]382 bbox = new Bounds(sourceArea.getBounds2D());
[10216]383 }
[1302]384 }
[10216]385 lastZoomTime = System.currentTimeMillis();
386 } else {
387 lastZoomTime = -1;
388 lastZoomArea = -1;
[1302]389 }
[14985]390 if (bbox != null) {
391 MainApplication.getMap().mapView.zoomTo(bbox);
392 }
[1302]393 }
[948]394 }
[1820]395
396 @Override
397 protected void updateEnabledState() {
[13926]398 OsmData<?, ?, ?, ?> ds = getLayerManager().getActiveData();
[12630]399 MapFrame map = MainApplication.getMap();
[8171]400 switch (mode) {
[14221]401 case SELECTION:
[10383]402 setEnabled(ds != null && !ds.selectionEmpty());
[7012]403 break;
[14221]404 case LAYER:
[9447]405 setEnabled(getFirstSelectedLayer() != null);
[7012]406 break;
[14221]407 case CONFLICT:
[12630]408 setEnabled(map != null && map.conflictDialog.getSelectedConflict() != null);
[7012]409 break;
[14221]410 case DOWNLOAD:
[10382]411 setEnabled(ds != null && !ds.getDataSources().isEmpty());
[9447]412 break;
[14221]413 case PROBLEM:
[12630]414 setEnabled(map != null && map.validatorDialog.getSelectedError() != null);
[7012]415 break;
[14221]416 case PREVIOUS:
[12630]417 setEnabled(MainApplication.isDisplayingMapView() && map.mapView.hasZoomUndoEntries());
[7012]418 break;
[14221]419 case NEXT:
[12630]420 setEnabled(MainApplication.isDisplayingMapView() && map.mapView.hasZoomRedoEntries());
[7012]421 break;
422 default:
[10382]423 setEnabled(!getLayerManager().getLayers().isEmpty());
[1854]424 }
[1820]425 }
[2256]426
427 @Override
428 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
[14221]429 if (AutoScaleMode.SELECTION == mode) {
[2256]430 setEnabled(selection != null && !selection.isEmpty());
431 }
432 }
[2759]433
434 @Override
[6890]435 protected final void installAdapters() {
[2759]436 super.installAdapters();
[5958]437 // make this action listen to zoom and mapframe change events
[2759]438 //
[9795]439 MapView.addZoomChangeListener(new ZoomChangeAdapter());
[12639]440 MainApplication.addMapFrameListener(new MapFrameAdapter());
[2759]441 initEnabledState();
442 }
443
444 /**
[5958]445 * Adapter for zoom change events
[2759]446 */
[15963]447 private class ZoomChangeAdapter implements ZoomChangeListener {
[6084]448 @Override
[2759]449 public void zoomChanged() {
450 updateEnabledState();
451 }
452 }
453
[5958]454 /**
455 * Adapter for MapFrame change events
456 */
457 private class MapFrameAdapter implements MapFrameListener {
458 private ListSelectionListener conflictSelectionListener;
459 private TreeSelectionListener validatorSelectionListener;
460
[8836]461 MapFrameAdapter() {
[14221]462 if (AutoScaleMode.CONFLICT == mode) {
[10601]463 conflictSelectionListener = e -> updateEnabledState();
[14221]464 } else if (AutoScaleMode.PROBLEM == mode) {
[10601]465 validatorSelectionListener = e -> updateEnabledState();
[5958]466 }
467 }
468
[8171]469 @Override
470 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
[5958]471 if (conflictSelectionListener != null) {
472 if (newFrame != null) {
473 newFrame.conflictDialog.addListSelectionListener(conflictSelectionListener);
474 } else if (oldFrame != null) {
475 oldFrame.conflictDialog.removeListSelectionListener(conflictSelectionListener);
476 }
477 } else if (validatorSelectionListener != null) {
478 if (newFrame != null) {
479 newFrame.validatorDialog.addTreeSelectionListener(validatorSelectionListener);
480 } else if (oldFrame != null) {
481 oldFrame.validatorDialog.removeTreeSelectionListener(validatorSelectionListener);
482 }
483 }
[9447]484 updateEnabledState();
[5958]485 }
486 }
[403]487}
Note: See TracBrowser for help on using the repository browser.