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

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

add Ant target to run PMD (only few rules for now), fix violations

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