source: josm/trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java@ 14121

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

see #15229 - deprecate all Main methods related to projections. New ProjectionRegistry class

  • Property svn:eol-style set to native
File size: 14.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.AWTEvent;
7import java.awt.Cursor;
8import java.awt.GridBagLayout;
9import java.awt.Insets;
10import java.awt.Toolkit;
11import java.awt.event.AWTEventListener;
12import java.awt.event.ActionEvent;
13import java.awt.event.FocusEvent;
14import java.awt.event.FocusListener;
15import java.awt.event.KeyEvent;
16import java.awt.event.MouseEvent;
17import java.util.Formatter;
18import java.util.Locale;
19
20import javax.swing.JLabel;
21import javax.swing.JPanel;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.actions.mapmode.MapMode;
25import org.openstreetmap.josm.data.coor.EastNorth;
26import org.openstreetmap.josm.data.coor.LatLon;
27import org.openstreetmap.josm.data.imagery.OffsetBookmark;
28import org.openstreetmap.josm.data.projection.ProjectionRegistry;
29import org.openstreetmap.josm.gui.ExtendedDialog;
30import org.openstreetmap.josm.gui.MainApplication;
31import org.openstreetmap.josm.gui.MapFrame;
32import org.openstreetmap.josm.gui.MapView;
33import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
34import org.openstreetmap.josm.gui.util.WindowGeometry;
35import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
36import org.openstreetmap.josm.gui.widgets.JosmTextField;
37import org.openstreetmap.josm.tools.GBC;
38import org.openstreetmap.josm.tools.ImageProvider;
39import org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider;
40import org.openstreetmap.josm.tools.Logging;
41
42/**
43 * Adjust the position of an imagery layer.
44 * @since 3715
45 */
46public class ImageryAdjustAction extends MapMode implements AWTEventListener {
47 private static volatile ImageryOffsetDialog offsetDialog;
48 private static Cursor cursor = ImageProvider.getCursor("normal", "move");
49
50 private OffsetBookmark old;
51 private OffsetBookmark tempOffset;
52 private EastNorth prevEastNorth;
53 private transient AbstractTileSourceLayer<?> layer;
54 private MapMode oldMapMode;
55 private boolean exitingMode;
56 private boolean restoreOldMode;
57
58 /**
59 * Constructs a new {@code ImageryAdjustAction} for the given layer.
60 * @param layer The imagery layer
61 */
62 public ImageryAdjustAction(AbstractTileSourceLayer<?> layer) {
63 super(tr("New offset"), "adjustimg", tr("Adjust the position of this imagery layer"), cursor);
64 putValue("toolbar", Boolean.FALSE);
65 this.layer = layer;
66 }
67
68 @Override
69 public void enterMode() {
70 super.enterMode();
71 if (layer == null)
72 return;
73 if (!layer.isVisible()) {
74 layer.setVisible(true);
75 }
76 old = layer.getDisplaySettings().getOffsetBookmark();
77 EastNorth curOff = old == null ? EastNorth.ZERO : old.getDisplacement(ProjectionRegistry.getProjection());
78 LatLon center;
79 if (MainApplication.isDisplayingMapView()) {
80 center = ProjectionRegistry.getProjection().eastNorth2latlon(MainApplication.getMap().mapView.getCenter());
81 } else {
82 center = LatLon.ZERO;
83 }
84 tempOffset = new OffsetBookmark(
85 ProjectionRegistry.getProjection().toCode(),
86 layer.getInfo().getId(),
87 layer.getInfo().getName(),
88 null,
89 curOff, center);
90 layer.getDisplaySettings().setOffsetBookmark(tempOffset);
91 addListeners();
92 showOffsetDialog(new ImageryOffsetDialog());
93 }
94
95 private static void showOffsetDialog(ImageryOffsetDialog dlg) {
96 offsetDialog = dlg;
97 offsetDialog.setVisible(true);
98 }
99
100 private static void hideOffsetDialog() {
101 offsetDialog.setVisible(false);
102 offsetDialog = null;
103 }
104
105 protected void addListeners() {
106 MapView mapView = MainApplication.getMap().mapView;
107 mapView.addMouseListener(this);
108 mapView.addMouseMotionListener(this);
109 try {
110 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
111 } catch (SecurityException ex) {
112 Logging.error(ex);
113 }
114 }
115
116 @Override
117 public void exitMode() {
118 // do not restore old mode here - this is called when the new mode is already known.
119 restoreOldMode = false;
120 doExitMode();
121 }
122
123 private void exitModeAndRestoreOldMode() {
124 restoreOldMode = true;
125 doExitMode();
126 restoreOldMode = false;
127 }
128
129 private void doExitMode() {
130 exitingMode = true;
131 try {
132 super.exitMode();
133 } catch (IllegalArgumentException e) {
134 Logging.trace(e);
135 }
136 if (offsetDialog != null) {
137 if (layer != null) {
138 layer.getDisplaySettings().setOffsetBookmark(old);
139 }
140 hideOffsetDialog();
141 }
142 removeListeners();
143 exitingMode = false;
144 }
145
146 protected void removeListeners() {
147 try {
148 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
149 } catch (SecurityException ex) {
150 Logging.error(ex);
151 }
152 if (MainApplication.isDisplayingMapView()) {
153 MapFrame map = MainApplication.getMap();
154 map.mapView.removeMouseMotionListener(this);
155 map.mapView.removeMouseListener(this);
156 }
157 }
158
159 @Override
160 public void eventDispatched(AWTEvent event) {
161 if (!(event instanceof KeyEvent)
162 || (event.getID() != KeyEvent.KEY_PRESSED)
163 || (layer == null)
164 || (offsetDialog != null && offsetDialog.areFieldsInFocus())) {
165 return;
166 }
167 KeyEvent kev = (KeyEvent) event;
168 int dx = 0;
169 int dy = 0;
170 switch (kev.getKeyCode()) {
171 case KeyEvent.VK_UP : dy = +1; break;
172 case KeyEvent.VK_DOWN : dy = -1; break;
173 case KeyEvent.VK_LEFT : dx = -1; break;
174 case KeyEvent.VK_RIGHT : dx = +1; break;
175 case KeyEvent.VK_ESCAPE:
176 if (offsetDialog != null) {
177 restoreOldMode = true;
178 offsetDialog.setVisible(false);
179 return;
180 }
181 break;
182 default: // Do nothing
183 }
184 if (dx != 0 || dy != 0) {
185 double ppd = layer.getPPD();
186 EastNorth d = tempOffset.getDisplacement().add(new EastNorth(dx / ppd, dy / ppd));
187 tempOffset.setDisplacement(d);
188 layer.getDisplaySettings().setOffsetBookmark(tempOffset);
189 if (offsetDialog != null) {
190 offsetDialog.updateOffset();
191 }
192 if (Logging.isDebugEnabled()) {
193 Logging.debug("{0} consuming event {1}", getClass().getName(), kev);
194 }
195 kev.consume();
196 }
197 }
198
199 @Override
200 public void mousePressed(MouseEvent e) {
201 if (e.getButton() != MouseEvent.BUTTON1)
202 return;
203
204 if (layer.isVisible()) {
205 requestFocusInMapView();
206 MapView mapView = MainApplication.getMap().mapView;
207 prevEastNorth = mapView.getEastNorth(e.getX(), e.getY());
208 mapView.setNewCursor(Cursor.MOVE_CURSOR, this);
209 }
210 }
211
212 @Override
213 public void mouseDragged(MouseEvent e) {
214 if (layer == null || prevEastNorth == null) return;
215 EastNorth eastNorth = MainApplication.getMap().mapView.getEastNorth(e.getX(), e.getY());
216 EastNorth d = tempOffset.getDisplacement().add(eastNorth).subtract(prevEastNorth);
217 tempOffset.setDisplacement(d);
218 layer.getDisplaySettings().setOffsetBookmark(tempOffset);
219 if (offsetDialog != null) {
220 offsetDialog.updateOffset();
221 }
222 prevEastNorth = eastNorth;
223 }
224
225 @Override
226 public void mouseReleased(MouseEvent e) {
227 MapView mapView = MainApplication.getMap().mapView;
228 mapView.repaint();
229 mapView.resetCursor(this);
230 prevEastNorth = null;
231 }
232
233 @Override
234 public void actionPerformed(ActionEvent e) {
235 MapFrame map = MainApplication.getMap();
236 if (offsetDialog != null || layer == null || map == null)
237 return;
238 oldMapMode = map.mapMode;
239 super.actionPerformed(e);
240 }
241
242 private class ImageryOffsetDialog extends ExtendedDialog implements FocusListener {
243 private final JosmTextField tOffset = new JosmTextField();
244 private final JosmTextField tBookmarkName = new JosmTextField();
245 private boolean ignoreListener;
246
247 /**
248 * Constructs a new {@code ImageryOffsetDialog}.
249 */
250 ImageryOffsetDialog() {
251 super(Main.parent,
252 tr("Adjust imagery offset"),
253 new String[] {tr("OK"), tr("Cancel")},
254 false, false); // Do not dispose on close, so HIDE_ON_CLOSE remains the default behaviour and setVisible is called
255 setButtonIcons("ok", "cancel");
256 contentInsets = new Insets(10, 15, 5, 15);
257 JPanel pnl = new JPanel(new GridBagLayout());
258 pnl.add(new JMultilineLabel(tr("Use arrow keys or drag the imagery layer with mouse to adjust the imagery offset.\n" +
259 "You can also enter east and north offset in the {0} coordinates.\n" +
260 "If you want to save the offset as bookmark, enter the bookmark name below",
261 ProjectionRegistry.getProjection().toString())), GBC.eop());
262 pnl.add(new JLabel(tr("Offset:")), GBC.std());
263 pnl.add(tOffset, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 5));
264 pnl.add(new JLabel(tr("Bookmark name: ")), GBC.std());
265 pnl.add(tBookmarkName, GBC.eol().fill(GBC.HORIZONTAL));
266 tOffset.setColumns(16);
267 updateOffsetIntl();
268 tOffset.addFocusListener(this);
269 setContent(pnl);
270 setupDialog();
271 setRememberWindowGeometry(getClass().getName() + ".geometry", WindowGeometry.centerInWindow(Main.parent, getSize()));
272 }
273
274 private boolean areFieldsInFocus() {
275 return tOffset.hasFocus();
276 }
277
278 @Override
279 public void focusGained(FocusEvent e) {
280 // Do nothing
281 }
282
283 @Override
284 public void focusLost(FocusEvent e) {
285 if (ignoreListener) return;
286 String ostr = tOffset.getText();
287 int semicolon = ostr.indexOf(';');
288 if (layer != null && semicolon >= 0 && semicolon + 1 < ostr.length()) {
289 try {
290 String easting = ostr.substring(0, semicolon).trim();
291 String northing = ostr.substring(semicolon + 1).trim();
292 double dx = JosmDecimalFormatSymbolsProvider.parseDouble(easting);
293 double dy = JosmDecimalFormatSymbolsProvider.parseDouble(northing);
294 tempOffset.setDisplacement(new EastNorth(dx, dy));
295 layer.getDisplaySettings().setOffsetBookmark(tempOffset);
296 } catch (NumberFormatException nfe) {
297 // we repaint offset numbers in any case
298 Logging.trace(nfe);
299 }
300 }
301 updateOffsetIntl();
302 if (layer != null) {
303 layer.invalidate();
304 }
305 }
306
307 private void updateOffset() {
308 ignoreListener = true;
309 updateOffsetIntl();
310 ignoreListener = false;
311 }
312
313 private void updateOffsetIntl() {
314 if (layer != null) {
315 // Support projections with very small numbers (e.g. 4326)
316 int precision = ProjectionRegistry.getProjection().getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
317 // US locale to force decimal separator to be '.'
318 try (Formatter us = new Formatter(Locale.US)) {
319 EastNorth displacement = layer.getDisplaySettings().getDisplacement();
320 tOffset.setText(us.format(new StringBuilder()
321 .append("%1.").append(precision).append("f; %1.").append(precision).append('f').toString(),
322 displacement.east(), displacement.north()).toString());
323 }
324 }
325 }
326
327 private boolean confirmOverwriteBookmark() {
328 ExtendedDialog dialog = new ExtendedDialog(
329 Main.parent,
330 tr("Overwrite"),
331 tr("Overwrite"), tr("Cancel")
332 ) { {
333 contentInsets = new Insets(10, 15, 10, 15);
334 } };
335 dialog.setContent(tr("Offset bookmark already exists. Overwrite?"));
336 dialog.setButtonIcons("ok", "cancel");
337 dialog.setupDialog();
338 dialog.setVisible(true);
339 return dialog.getValue() == 1;
340 }
341
342 @Override
343 protected void buttonAction(int buttonIndex, ActionEvent evt) {
344 restoreOldMode = true;
345 if (buttonIndex == 0 && tBookmarkName.getText() != null && !tBookmarkName.getText().isEmpty() &&
346 OffsetBookmark.getBookmarkByName(layer, tBookmarkName.getText()) != null &&
347 !confirmOverwriteBookmark()) {
348 return;
349 }
350 super.buttonAction(buttonIndex, evt);
351 }
352
353 @Override
354 public void setVisible(boolean visible) {
355 super.setVisible(visible);
356 if (visible)
357 return;
358 ignoreListener = true;
359 offsetDialog = null;
360 if (layer != null) {
361 if (getValue() != 1) {
362 layer.getDisplaySettings().setOffsetBookmark(old);
363 } else if (tBookmarkName.getText() != null && !tBookmarkName.getText().isEmpty()) {
364 OffsetBookmark.bookmarkOffset(tBookmarkName.getText(), layer);
365 }
366 }
367 MainApplication.getMenu().imageryMenu.refreshOffsetMenu();
368 restoreMapModeState();
369 }
370
371 private void restoreMapModeState() {
372 MapFrame map = MainApplication.getMap();
373 if (map == null)
374 return;
375 if (oldMapMode != null) {
376 if (restoreOldMode || (!exitingMode && getValue() == ExtendedDialog.DialogClosedOtherwise)) {
377 map.selectMapMode(oldMapMode);
378 }
379 oldMapMode = null;
380 } else if (!exitingMode && !map.selectSelectTool(false)) {
381 exitModeAndRestoreOldMode();
382 map.mapMode = null;
383 }
384 }
385 }
386
387 @Override
388 public void destroy() {
389 super.destroy();
390 removeListeners();
391 this.layer = null;
392 this.oldMapMode = null;
393 }
394}
Note: See TracBrowser for help on using the repository browser.