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

Last change on this file since 15906 was 15007, checked in by Don-vip, 6 years ago

remove deprecated API

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