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

Last change on this file since 7817 was 7817, checked in by bastiK, 11 years ago

fixed #10861 - Zoom to layer when opening new file (see #10860)

  • Property svn:eol-style set to native
File size: 13.8 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;
[6246]10import java.util.Arrays;
[403]11import java.util.Collection;
[6246]12import java.util.Collections;
[1750]13import java.util.HashSet;
[1953]14import java.util.List;
[403]15
16import javax.swing.JOptionPane;
[5958]17import javax.swing.event.ListSelectionEvent;
18import javax.swing.event.ListSelectionListener;
19import javax.swing.event.TreeSelectionEvent;
20import javax.swing.event.TreeSelectionListener;
[403]21
22import org.openstreetmap.josm.Main;
[2477]23import org.openstreetmap.josm.data.Bounds;
[3973]24import org.openstreetmap.josm.data.conflict.Conflict;
[403]25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
[5958]27import org.openstreetmap.josm.data.validation.TestError;
28import org.openstreetmap.josm.gui.MapFrame;
29import org.openstreetmap.josm.gui.MapFrameListener;
[2759]30import org.openstreetmap.josm.gui.MapView;
[1953]31import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
[5958]32import org.openstreetmap.josm.gui.dialogs.ValidatorDialog.ValidatorBoundingXYVisitor;
[6509]33import org.openstreetmap.josm.gui.download.DownloadDialog;
[403]34import org.openstreetmap.josm.gui.layer.Layer;
[1084]35import org.openstreetmap.josm.tools.Shortcut;
[403]36
37/**
38 * Toggles the autoScale feature of the mapView
39 * @author imi
40 */
41public class AutoScaleAction extends JosmAction {
42
[6246]43 public static final Collection<String> MODES = Collections.unmodifiableList(Arrays.asList(
[7668]44 marktr(/* ICON(dialogs/autoscale/) */ "data"),
45 marktr(/* ICON(dialogs/autoscale/) */ "layer"),
46 marktr(/* ICON(dialogs/autoscale/) */ "selection"),
47 marktr(/* ICON(dialogs/autoscale/) */ "conflict"),
48 marktr(/* ICON(dialogs/autoscale/) */ "download"),
49 marktr(/* ICON(dialogs/autoscale/) */ "problem"),
50 marktr(/* ICON(dialogs/autoscale/) */ "previous"),
51 marktr(/* ICON(dialogs/autoscale/) */ "next")));
[6069]52
[5958]53 private final String mode;
[2685]54
[5958]55 protected ZoomChangeAdapter zoomChangeAdapter;
56 protected MapFrameAdapter mapFrameAdapter;
57
[2685]58 /**
59 * Zooms the current map view to the currently selected primitives.
60 * Does nothing if there either isn't a current map view or if there isn't a current data
61 * layer.
[2711]62 *
[2685]63 */
64 public static void zoomToSelection() {
[6336]65 if (Main.main == null || !Main.main.hasEditLayer()) return;
[2986]66 Collection<OsmPrimitive> sel = Main.main.getEditLayer().data.getSelected();
[2685]67 if (sel.isEmpty()) {
68 JOptionPane.showMessageDialog(
69 Main.parent,
70 tr("Nothing selected to zoom to."),
71 tr("Information"),
72 JOptionPane.INFORMATION_MESSAGE
73 );
74 return;
75 }
[3251]76 zoomTo(sel);
77 }
78
79 public static void zoomTo(Collection<OsmPrimitive> sel) {
[2685]80 BoundingXYVisitor bboxCalculator = new BoundingXYVisitor();
81 bboxCalculator.computeBoundingBox(sel);
82 // increase bbox by 0.001 degrees on each side. this is required
83 // especially if the bbox contains one single node, but helpful
84 // in most other cases as well.
85 bboxCalculator.enlargeBoundingBox();
86 if (bboxCalculator.getBounds() != null) {
[7817]87 Main.map.mapView.zoomTo(bboxCalculator);
[2685]88 }
89 }
90
[3327]91 public static void autoScale(String mode) {
92 new AutoScaleAction(mode, false).autoScale();
93 }
94
[948]95 private static int getModeShortcut(String mode) {
96 int shortcut = -1;
[458]97
[7012]98 // TODO: convert this to switch/case and make sure the parsing still works
[4921]99 /* leave as single line for shortcut overview parsing! */
100 if (mode.equals("data")) { shortcut = KeyEvent.VK_1; }
101 else if (mode.equals("layer")) { shortcut = KeyEvent.VK_2; }
102 else if (mode.equals("selection")) { shortcut = KeyEvent.VK_3; }
103 else if (mode.equals("conflict")) { shortcut = KeyEvent.VK_4; }
104 else if (mode.equals("download")) { shortcut = KeyEvent.VK_5; }
[5958]105 else if (mode.equals("problem")) { shortcut = KeyEvent.VK_6; }
[4921]106 else if (mode.equals("previous")) { shortcut = KeyEvent.VK_8; }
107 else if (mode.equals("next")) { shortcut = KeyEvent.VK_9; }
[458]108
[948]109 return shortcut;
110 }
[403]111
[3327]112 /**
[5958]113 * Constructs a new {@code AutoScaleAction}.
114 * @param mode The autoscale mode (one of {@link AutoScaleAction#MODES})
[3327]115 * @param marker Used only to differentiate from default constructor
116 */
117 private AutoScaleAction(String mode, boolean marker) {
118 super(false);
119 this.mode = mode;
120 }
121
[5958]122 /**
123 * Constructs a new {@code AutoScaleAction}.
124 * @param mode The autoscale mode (one of {@link AutoScaleAction#MODES})
125 */
126 public AutoScaleAction(final String mode) {
[948]127 super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)),
[6069]128 Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.DIRECT),
[5958]129 true, null, false);
[948]130 String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1);
131 putValue("help", "Action/AutoScale/" + modeHelp);
132 this.mode = mode;
[7012]133 switch (mode) {
134 case "data":
[2323]135 putValue("help", ht("/Action/ZoomToData"));
[7012]136 break;
137 case "layer":
[2323]138 putValue("help", ht("/Action/ZoomToLayer"));
[7012]139 break;
140 case "selection":
[2323]141 putValue("help", ht("/Action/ZoomToSelection"));
[7012]142 break;
143 case "conflict":
[2323]144 putValue("help", ht("/Action/ZoomToConflict"));
[7012]145 break;
146 case "problem":
[5958]147 putValue("help", ht("/Action/ZoomToProblem"));
[7012]148 break;
149 case "download":
[2323]150 putValue("help", ht("/Action/ZoomToDownload"));
[7012]151 break;
152 case "previous":
[3760]153 putValue("help", ht("/Action/ZoomToPrevious"));
[7012]154 break;
155 case "next":
[3760]156 putValue("help", ht("/Action/ZoomToNext"));
[7012]157 break;
158 default:
[5958]159 throw new IllegalArgumentException("Unknown mode: "+mode);
[2477]160 }
[5958]161 installAdapters();
[948]162 }
[403]163
[1868]164 public void autoScale() {
[5460]165 if (Main.isDisplayingMapView()) {
[7012]166 switch(mode) {
167 case "previous":
[2758]168 Main.map.mapView.zoomPrevious();
[7012]169 break;
170 case "next":
[2758]171 Main.map.mapView.zoomNext();
[7012]172 break;
173 default:
[2758]174 BoundingXYVisitor bbox = getBoundingBox();
175 if (bbox != null && bbox.getBounds() != null) {
[7817]176 Main.map.mapView.zoomTo(bbox);
[2758]177 }
[948]178 }
179 }
180 putValue("active", true);
181 }
182
[6084]183 @Override
[1868]184 public void actionPerformed(ActionEvent e) {
185 autoScale();
186 }
187
[1953]188 /**
189 * Replies the first selected layer in the layer list dialog. null, if no
190 * such layer exists, either because the layer list dialog is not yet created
191 * or because no layer is selected.
[2512]192 *
[1953]193 * @return the first selected layer in the layer list dialog
194 */
195 protected Layer getFirstSelectedLayer() {
196 List<Layer> layers = LayerListDialog.getInstance().getModel().getSelectedLayers();
197 if (layers.isEmpty()) return null;
198 return layers.get(0);
199 }
200
[948]201 private BoundingXYVisitor getBoundingBox() {
[7012]202 BoundingXYVisitor v = "problem".equals(mode) ? new ValidatorBoundingXYVisitor() : new BoundingXYVisitor();
[5958]203
[7012]204 switch(mode) {
205 case "problem":
[5958]206 TestError error = Main.map.validatorDialog.getSelectedError();
207 if (error == null) return null;
208 ((ValidatorBoundingXYVisitor) v).visit(error);
209 if (v.getBounds() == null) return null;
210 v.enlargeBoundingBox(Main.pref.getDouble("validator.zoom-enlarge-bbox", 0.0002));
[7012]211 break;
212 case "data":
[1750]213 for (Layer l : Main.map.mapView.getAllLayers()) {
[948]214 l.visitBoundingBox(v);
[1750]215 }
[7012]216 break;
217 case "layer":
[6783]218 if (Main.main.getActiveLayer() == null)
[1903]219 return null;
[1953]220 // try to zoom to the first selected layer
221 Layer l = getFirstSelectedLayer();
222 if (l == null) return null;
223 l.visitBoundingBox(v);
[7012]224 break;
225 case "selection":
226 case "conflict":
[7005]227 Collection<OsmPrimitive> sel = new HashSet<>();
[7012]228 if ("selection".equals(mode)) {
[1814]229 sel = getCurrentDataSet().getSelected();
[7012]230 } else {
[3973]231 Conflict<? extends OsmPrimitive> c = Main.map.conflictDialog.getSelectedConflict();
232 if (c != null) {
233 sel.add(c.getMy());
234 } else if (Main.map.conflictDialog.getConflicts() != null) {
[1750]235 sel = Main.map.conflictDialog.getConflicts().getMyConflictParties();
236 }
237 }
[948]238 if (sel.isEmpty()) {
[2017]239 JOptionPane.showMessageDialog(
[1847]240 Main.parent,
[7012]241 ("selection".equals(mode) ? tr("Nothing selected to zoom to.") : tr("No conflicts to zoom to")),
[1847]242 tr("Information"),
243 JOptionPane.INFORMATION_MESSAGE
244 );
[948]245 return null;
246 }
[1750]247 for (OsmPrimitive osm : sel) {
[6009]248 osm.accept(v);
[1750]249 }
[6608]250
251 // Increase the bounding box by up to 100% to give more context.
252 v.enlargeBoundingBoxLogarithmically(100);
[7053]253 // Make the bounding box at least 100 meter wide to
[6608]254 // ensure reasonable zoom level when zooming onto single nodes.
[7053]255 v.enlargeToMinSize(Main.pref.getDouble("zoom_to_selection_min_size_in_meter", 100));
[7012]256 break;
257 case "download":
[6509]258 Bounds bounds = DownloadDialog.getSavedDownloadBounds();
259 if (bounds != null) {
[1302]260 try {
[6509]261 v.visit(bounds);
[2477]262 } catch (Exception e) {
[6509]263 Main.warn(e);
[1302]264 }
265 }
[7012]266 break;
[1302]267 }
[948]268 return v;
269 }
[1820]270
271 @Override
272 protected void updateEnabledState() {
[7012]273 switch(mode) {
274 case "selection":
[1854]275 setEnabled(getCurrentDataSet() != null && ! getCurrentDataSet().getSelected().isEmpty());
[7012]276 break;
277 case "layer":
[6333]278 if (!Main.isDisplayingMapView() || Main.map.mapView.getAllLayersAsList().isEmpty()) {
[1953]279 setEnabled(false);
280 } else {
281 // FIXME: should also check for whether a layer is selected in the layer list dialog
282 setEnabled(true);
283 }
[7012]284 break;
285 case "conflict":
[5958]286 setEnabled(Main.map != null && Main.map.conflictDialog.getSelectedConflict() != null);
[7012]287 break;
288 case "problem":
[5958]289 setEnabled(Main.map != null && Main.map.validatorDialog.getSelectedError() != null);
[7012]290 break;
291 case "previous":
[5460]292 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomUndoEntries());
[7012]293 break;
294 case "next":
[5460]295 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomRedoEntries());
[7012]296 break;
297 default:
298 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasLayers()
[1854]299 );
300 }
[1820]301 }
[2256]302
303 @Override
304 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
305 if ("selection".equals(mode)) {
306 setEnabled(selection != null && !selection.isEmpty());
307 }
308 }
[2759]309
310 @Override
[6890]311 protected final void installAdapters() {
[2759]312 super.installAdapters();
[5958]313 // make this action listen to zoom and mapframe change events
[2759]314 //
[5958]315 MapView.addZoomChangeListener(zoomChangeAdapter = new ZoomChangeAdapter());
316 Main.addMapFrameListener(mapFrameAdapter = new MapFrameAdapter());
[2759]317 initEnabledState();
318 }
319
320 /**
[5958]321 * Adapter for zoom change events
[2759]322 */
323 private class ZoomChangeAdapter implements MapView.ZoomChangeListener {
[6084]324 @Override
[2759]325 public void zoomChanged() {
326 updateEnabledState();
327 }
328 }
329
[5958]330 /**
331 * Adapter for MapFrame change events
332 */
333 private class MapFrameAdapter implements MapFrameListener {
334 private ListSelectionListener conflictSelectionListener;
335 private TreeSelectionListener validatorSelectionListener;
336
337 public MapFrameAdapter() {
[7012]338 if ("conflict".equals(mode)) {
[5958]339 conflictSelectionListener = new ListSelectionListener() {
340 @Override public void valueChanged(ListSelectionEvent e) {
341 updateEnabledState();
342 }
343 };
[7012]344 } else if ("problem".equals(mode)) {
[5958]345 validatorSelectionListener = new TreeSelectionListener() {
346 @Override public void valueChanged(TreeSelectionEvent e) {
347 updateEnabledState();
348 }
349 };
350 }
351 }
352
353 @Override public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
354 if (conflictSelectionListener != null) {
355 if (newFrame != null) {
356 newFrame.conflictDialog.addListSelectionListener(conflictSelectionListener);
357 } else if (oldFrame != null) {
358 oldFrame.conflictDialog.removeListSelectionListener(conflictSelectionListener);
359 }
360 } else if (validatorSelectionListener != null) {
361 if (newFrame != null) {
362 newFrame.validatorDialog.addTreeSelectionListener(validatorSelectionListener);
363 } else if (oldFrame != null) {
364 oldFrame.validatorDialog.removeTreeSelectionListener(validatorSelectionListener);
365 }
366 }
367 }
368 }
[403]369}
Note: See TracBrowser for help on using the repository browser.