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

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

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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