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

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

fix #13172 - Move ImageryLayer.d[xy] to the settings (patch by michael2402) - gsoc-core

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