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

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

FindBugs - ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD

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