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

Last change on this file since 8148 was 8126, checked in by Don-vip, 9 years ago

fix Sonar issue squid:S2444 - Lazy initialization of "static" fields should be "synchronized"

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