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

Last change on this file since 7813 was 7668, checked in by stoecker, 10 years ago

cleanup icons, mark undetected icons, set proper mimetype, delete unused icons, update geticons script

  • Property svn:eol-style set to native
File size: 13.9 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.util.Arrays;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashSet;
14import java.util.List;
15
16import javax.swing.JOptionPane;
17import javax.swing.event.ListSelectionEvent;
18import javax.swing.event.ListSelectionListener;
19import javax.swing.event.TreeSelectionEvent;
20import javax.swing.event.TreeSelectionListener;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.data.Bounds;
24import org.openstreetmap.josm.data.conflict.Conflict;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
27import org.openstreetmap.josm.data.validation.TestError;
28import org.openstreetmap.josm.gui.MapFrame;
29import org.openstreetmap.josm.gui.MapFrameListener;
30import org.openstreetmap.josm.gui.MapView;
31import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
32import org.openstreetmap.josm.gui.dialogs.ValidatorDialog.ValidatorBoundingXYVisitor;
33import org.openstreetmap.josm.gui.download.DownloadDialog;
34import org.openstreetmap.josm.gui.layer.Layer;
35import org.openstreetmap.josm.tools.Shortcut;
36
37/**
38 * Toggles the autoScale feature of the mapView
39 * @author imi
40 */
41public class AutoScaleAction extends JosmAction {
42
43 public static final Collection<String> MODES = Collections.unmodifiableList(Arrays.asList(
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")));
52
53 private final String mode;
54
55 protected ZoomChangeAdapter zoomChangeAdapter;
56 protected MapFrameAdapter mapFrameAdapter;
57
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.
62 *
63 */
64 public static void zoomToSelection() {
65 if (Main.main == null || !Main.main.hasEditLayer()) return;
66 Collection<OsmPrimitive> sel = Main.main.getEditLayer().data.getSelected();
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 }
76 zoomTo(sel);
77 }
78
79 public static void zoomTo(Collection<OsmPrimitive> sel) {
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) {
87 Main.map.mapView.recalculateCenterScale(bboxCalculator);
88 }
89 }
90
91 public static void autoScale(String mode) {
92 new AutoScaleAction(mode, false).autoScale();
93 }
94
95 private static int getModeShortcut(String mode) {
96 int shortcut = -1;
97
98 // TODO: convert this to switch/case and make sure the parsing still works
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; }
105 else if (mode.equals("problem")) { shortcut = KeyEvent.VK_6; }
106 else if (mode.equals("previous")) { shortcut = KeyEvent.VK_8; }
107 else if (mode.equals("next")) { shortcut = KeyEvent.VK_9; }
108
109 return shortcut;
110 }
111
112 /**
113 * Constructs a new {@code AutoScaleAction}.
114 * @param mode The autoscale mode (one of {@link AutoScaleAction#MODES})
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
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) {
127 super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)),
128 Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.DIRECT),
129 true, null, false);
130 String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1);
131 putValue("help", "Action/AutoScale/" + modeHelp);
132 this.mode = mode;
133 switch (mode) {
134 case "data":
135 putValue("help", ht("/Action/ZoomToData"));
136 break;
137 case "layer":
138 putValue("help", ht("/Action/ZoomToLayer"));
139 break;
140 case "selection":
141 putValue("help", ht("/Action/ZoomToSelection"));
142 break;
143 case "conflict":
144 putValue("help", ht("/Action/ZoomToConflict"));
145 break;
146 case "problem":
147 putValue("help", ht("/Action/ZoomToProblem"));
148 break;
149 case "download":
150 putValue("help", ht("/Action/ZoomToDownload"));
151 break;
152 case "previous":
153 putValue("help", ht("/Action/ZoomToPrevious"));
154 break;
155 case "next":
156 putValue("help", ht("/Action/ZoomToNext"));
157 break;
158 default:
159 throw new IllegalArgumentException("Unknown mode: "+mode);
160 }
161 installAdapters();
162 }
163
164 public void autoScale() {
165 if (Main.isDisplayingMapView()) {
166 switch(mode) {
167 case "previous":
168 Main.map.mapView.zoomPrevious();
169 break;
170 case "next":
171 Main.map.mapView.zoomNext();
172 break;
173 default:
174 BoundingXYVisitor bbox = getBoundingBox();
175 if (bbox != null && bbox.getBounds() != null) {
176 Main.map.mapView.recalculateCenterScale(bbox);
177 }
178 }
179 }
180 putValue("active", true);
181 }
182
183 @Override
184 public void actionPerformed(ActionEvent e) {
185 autoScale();
186 }
187
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.
192 *
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
201 private BoundingXYVisitor getBoundingBox() {
202 BoundingXYVisitor v = "problem".equals(mode) ? new ValidatorBoundingXYVisitor() : new BoundingXYVisitor();
203
204 switch(mode) {
205 case "problem":
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));
211 break;
212 case "data":
213 for (Layer l : Main.map.mapView.getAllLayers()) {
214 l.visitBoundingBox(v);
215 }
216 break;
217 case "layer":
218 if (Main.main.getActiveLayer() == null)
219 return null;
220 // try to zoom to the first selected layer
221 Layer l = getFirstSelectedLayer();
222 if (l == null) return null;
223 l.visitBoundingBox(v);
224 break;
225 case "selection":
226 case "conflict":
227 Collection<OsmPrimitive> sel = new HashSet<>();
228 if ("selection".equals(mode)) {
229 sel = getCurrentDataSet().getSelected();
230 } else {
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) {
235 sel = Main.map.conflictDialog.getConflicts().getMyConflictParties();
236 }
237 }
238 if (sel.isEmpty()) {
239 JOptionPane.showMessageDialog(
240 Main.parent,
241 ("selection".equals(mode) ? tr("Nothing selected to zoom to.") : tr("No conflicts to zoom to")),
242 tr("Information"),
243 JOptionPane.INFORMATION_MESSAGE
244 );
245 return null;
246 }
247 for (OsmPrimitive osm : sel) {
248 osm.accept(v);
249 }
250
251 // Increase the bounding box by up to 100% to give more context.
252 v.enlargeBoundingBoxLogarithmically(100);
253 // Make the bounding box at least 100 meter wide to
254 // ensure reasonable zoom level when zooming onto single nodes.
255 v.enlargeToMinSize(Main.pref.getDouble("zoom_to_selection_min_size_in_meter", 100));
256 break;
257 case "download":
258 Bounds bounds = DownloadDialog.getSavedDownloadBounds();
259 if (bounds != null) {
260 try {
261 v.visit(bounds);
262 } catch (Exception e) {
263 Main.warn(e);
264 }
265 }
266 break;
267 }
268 return v;
269 }
270
271 @Override
272 protected void updateEnabledState() {
273 switch(mode) {
274 case "selection":
275 setEnabled(getCurrentDataSet() != null && ! getCurrentDataSet().getSelected().isEmpty());
276 break;
277 case "layer":
278 if (!Main.isDisplayingMapView() || Main.map.mapView.getAllLayersAsList().isEmpty()) {
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 }
284 break;
285 case "conflict":
286 setEnabled(Main.map != null && Main.map.conflictDialog.getSelectedConflict() != null);
287 break;
288 case "problem":
289 setEnabled(Main.map != null && Main.map.validatorDialog.getSelectedError() != null);
290 break;
291 case "previous":
292 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomUndoEntries());
293 break;
294 case "next":
295 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomRedoEntries());
296 break;
297 default:
298 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasLayers()
299 );
300 }
301 }
302
303 @Override
304 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
305 if ("selection".equals(mode)) {
306 setEnabled(selection != null && !selection.isEmpty());
307 }
308 }
309
310 @Override
311 protected final void installAdapters() {
312 super.installAdapters();
313 // make this action listen to zoom and mapframe change events
314 //
315 MapView.addZoomChangeListener(zoomChangeAdapter = new ZoomChangeAdapter());
316 Main.addMapFrameListener(mapFrameAdapter = new MapFrameAdapter());
317 initEnabledState();
318 }
319
320 /**
321 * Adapter for zoom change events
322 */
323 private class ZoomChangeAdapter implements MapView.ZoomChangeListener {
324 @Override
325 public void zoomChanged() {
326 updateEnabledState();
327 }
328 }
329
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() {
338 if ("conflict".equals(mode)) {
339 conflictSelectionListener = new ListSelectionListener() {
340 @Override public void valueChanged(ListSelectionEvent e) {
341 updateEnabledState();
342 }
343 };
344 } else if ("problem".equals(mode)) {
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 }
369}
Note: See TracBrowser for help on using the repository browser.