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

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

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