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

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

sonar - squid:S3398 - "private" methods called only by inner classes should be moved to those classes

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