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

Last change on this file since 12711 was 12643, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.main.menu. Replacement: gui.MainApplication.getMenu()

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