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

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

fix #13023 - Replace uses of hasEditLayer() with new layer manager (patch by michael2402, modified) - gsoc-core

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