001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.actions;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.awt.AWTEvent;
007 import java.awt.Cursor;
008 import java.awt.GridBagLayout;
009 import java.awt.Insets;
010 import java.awt.Toolkit;
011 import java.awt.event.AWTEventListener;
012 import java.awt.event.ActionEvent;
013 import java.awt.event.FocusEvent;
014 import java.awt.event.FocusListener;
015 import java.awt.event.KeyEvent;
016 import java.awt.event.MouseEvent;
017 import java.awt.event.MouseListener;
018 import java.awt.event.MouseMotionListener;
019
020 import javax.swing.JLabel;
021 import javax.swing.JPanel;
022 import javax.swing.JTextField;
023
024 import org.openstreetmap.josm.Main;
025 import org.openstreetmap.josm.actions.mapmode.MapMode;
026 import org.openstreetmap.josm.data.coor.EastNorth;
027 import org.openstreetmap.josm.data.imagery.OffsetBookmark;
028 import org.openstreetmap.josm.gui.ExtendedDialog;
029 import org.openstreetmap.josm.gui.JMultilineLabel;
030 import org.openstreetmap.josm.gui.layer.ImageryLayer;
031 import org.openstreetmap.josm.tools.GBC;
032 import org.openstreetmap.josm.tools.ImageProvider;
033
034
035 public class ImageryAdjustAction extends MapMode implements MouseListener, MouseMotionListener, AWTEventListener{
036 static ImageryOffsetDialog offsetDialog;
037 static Cursor cursor = ImageProvider.getCursor("normal", "move");
038
039 double oldDx, oldDy;
040 boolean mouseDown;
041 EastNorth prevEastNorth;
042 private ImageryLayer layer;
043 private MapMode oldMapMode;
044
045 public ImageryAdjustAction(ImageryLayer layer) {
046 super(tr("New offset"), "adjustimg",
047 tr("Adjust the position of this imagery layer"), Main.map,
048 cursor);
049 putValue("toolbar", false);
050 this.layer = layer;
051 }
052
053 @Override public void enterMode() {
054 super.enterMode();
055 if (layer == null)
056 return;
057 if (!layer.isVisible()) {
058 layer.setVisible(true);
059 }
060 Main.map.mapView.addMouseListener(this);
061 Main.map.mapView.addMouseMotionListener(this);
062 oldDx = layer.getDx();
063 oldDy = layer.getDy();
064 try {
065 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
066 } catch (SecurityException ex) {
067 }
068 offsetDialog = new ImageryOffsetDialog();
069 offsetDialog.setVisible(true);
070 }
071
072 @Override public void exitMode() {
073 super.exitMode();
074 if (offsetDialog != null) {
075 layer.setOffset(oldDx, oldDy);
076 offsetDialog.setVisible(false);
077 offsetDialog = null;
078 }
079 try {
080 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
081 } catch (SecurityException ex) {
082 }
083 Main.map.mapView.removeMouseListener(this);
084 Main.map.mapView.removeMouseMotionListener(this);
085 }
086
087 @Override
088 public void eventDispatched(AWTEvent event) {
089 if (!(event instanceof KeyEvent)) return;
090 if (event.getID() != KeyEvent.KEY_PRESSED) return;
091 if (layer == null) return;
092 if (offsetDialog != null && offsetDialog.areFieldsInFocus()) return;
093 KeyEvent kev = (KeyEvent)event;
094 double dx = 0, dy = 0;
095 switch (kev.getKeyCode()) {
096 case KeyEvent.VK_UP : dy = +1; break;
097 case KeyEvent.VK_DOWN : dy = -1; break;
098 case KeyEvent.VK_LEFT : dx = -1; break;
099 case KeyEvent.VK_RIGHT : dx = +1; break;
100 }
101 if (dx != 0 || dy != 0) {
102 double ppd = layer.getPPD();
103 layer.displace(dx / ppd, dy / ppd);
104 if (offsetDialog != null) {
105 offsetDialog.updateOffset();
106 }
107 kev.consume();
108 Main.map.repaint();
109 }
110 }
111
112 @Override public void mousePressed(MouseEvent e) {
113 if (e.getButton() != MouseEvent.BUTTON1)
114 return;
115
116 if (layer.isVisible()) {
117 prevEastNorth=Main.map.mapView.getEastNorth(e.getX(),e.getY());
118 Main.map.mapView.setNewCursor(Cursor.MOVE_CURSOR, this);
119 }
120 }
121
122 @Override public void mouseDragged(MouseEvent e) {
123 if (layer == null || prevEastNorth == null) return;
124 EastNorth eastNorth =
125 Main.map.mapView.getEastNorth(e.getX(),e.getY());
126 double dx = layer.getDx()+eastNorth.east()-prevEastNorth.east();
127 double dy = layer.getDy()+eastNorth.north()-prevEastNorth.north();
128 layer.setOffset(dx, dy);
129 if (offsetDialog != null) {
130 offsetDialog.updateOffset();
131 }
132 Main.map.repaint();
133 prevEastNorth = eastNorth;
134 }
135
136 @Override public void mouseReleased(MouseEvent e) {
137 Main.map.mapView.repaint();
138 Main.map.mapView.resetCursor(this);
139 prevEastNorth = null;
140 }
141
142 @Override
143 public void actionPerformed(ActionEvent e) {
144 if (offsetDialog != null || layer == null || Main.map == null)
145 return;
146 oldMapMode = Main.map.mapMode;
147 layer.enableOffsetServer(false);
148 super.actionPerformed(e);
149 }
150
151 class ImageryOffsetDialog extends ExtendedDialog implements FocusListener {
152 public final JTextField tOffset = new JTextField();
153 JTextField tBookmarkName = new JTextField();
154 private boolean ignoreListener;
155 public ImageryOffsetDialog() {
156 super(Main.parent,
157 tr("Adjust imagery offset"),
158 new String[] { tr("OK"),tr("Cancel") },
159 false);
160 setButtonIcons(new String[] { "ok", "cancel" });
161 contentInsets = new Insets(10, 15, 5, 15);
162 JPanel pnl = new JPanel(new GridBagLayout());
163 pnl.add(new JMultilineLabel(tr("Use arrow keys or drag the imagery layer with mouse to adjust the imagery offset.\n" +
164 "You can also enter east and north offset in the {0} coordinates.\n" +
165 "If you want to save the offset as bookmark, enter the bookmark name below",Main.getProjection().toString())), GBC.eop());
166 pnl.add(new JLabel(tr("Offset: ")),GBC.std());
167 pnl.add(tOffset,GBC.eol().fill(GBC.HORIZONTAL).insets(0,0,0,5));
168 pnl.add(new JLabel(tr("Bookmark name: ")),GBC.std());
169 pnl.add(tBookmarkName,GBC.eol().fill(GBC.HORIZONTAL));
170 tOffset.setColumns(16);
171 updateOffsetIntl();
172 tOffset.addFocusListener(this);
173 setContent(pnl);
174 setupDialog();
175 }
176
177 public boolean areFieldsInFocus() {
178 return tOffset.hasFocus();
179 }
180
181 @Override
182 public void focusGained(FocusEvent e) {
183 }
184
185 @Override
186 public void focusLost(FocusEvent e) {
187 if (ignoreListener) return;
188 String ostr = tOffset.getText();
189 int semicolon = ostr.indexOf(';');
190 if( semicolon >= 0 && semicolon + 1 < ostr.length() ) {
191 try {
192 // here we assume that Double.parseDouble() needs '.' as a decimal separator
193 String easting = ostr.substring(0, semicolon).trim().replace(',', '.');
194 String northing = ostr.substring(semicolon + 1).trim().replace(',', '.');
195 double dx = Double.parseDouble(easting);
196 double dy = Double.parseDouble(northing);
197 layer.setOffset(dx, dy);
198 } catch (NumberFormatException nfe) {
199 // we repaint offset numbers in any case
200 }
201 }
202 updateOffsetIntl();
203 if (Main.isDisplayingMapView()) {
204 Main.map.repaint();
205 }
206 }
207
208 public void updateOffset() {
209 ignoreListener = true;
210 updateOffsetIntl();
211 ignoreListener = false;
212 }
213
214 public void updateOffsetIntl() {
215 // Support projections with very small numbers (e.g. 4326)
216 int precision = Main.getProjection().getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
217 // US locale to force decimal separator to be '.'
218 tOffset.setText(new java.util.Formatter(java.util.Locale.US).format(
219 "%1." + precision + "f; %1." + precision + "f",
220 layer.getDx(), layer.getDy()).toString());
221 }
222
223 private boolean confirmOverwriteBookmark() {
224 ExtendedDialog dialog = new ExtendedDialog(
225 Main.parent,
226 tr("Overwrite"),
227 new String[] {tr("Overwrite"), tr("Cancel")}
228 ) {{
229 contentInsets = new Insets(10, 15, 10, 15);
230 }};
231 dialog.setContent(tr("Offset bookmark already exists. Overwrite?"));
232 dialog.setButtonIcons(new String[] {"ok.png", "cancel.png"});
233 dialog.setupDialog();
234 dialog.setVisible(true);
235 return dialog.getValue() == 1;
236 }
237
238 @Override
239 protected void buttonAction(int buttonIndex, ActionEvent evt) {
240 if (buttonIndex == 0 && tBookmarkName.getText() != null && !"".equals(tBookmarkName.getText()) &&
241 OffsetBookmark.getBookmarkByName(layer, tBookmarkName.getText()) != null) {
242 if (!confirmOverwriteBookmark()) return;
243 }
244 super.buttonAction(buttonIndex, evt);
245 }
246
247 @Override
248 public void setVisible(boolean visible) {
249 super.setVisible(visible);
250 if (visible) return;
251 offsetDialog = null;
252 if (getValue() != 1) {
253 layer.setOffset(oldDx, oldDy);
254 } else if (tBookmarkName.getText() != null && !"".equals(tBookmarkName.getText())) {
255 OffsetBookmark.bookmarkOffset(tBookmarkName.getText(), layer);
256 }
257 Main.main.menu.imageryMenu.refreshOffsetMenu();
258 if (Main.map == null) return;
259 if (oldMapMode != null) {
260 Main.map.selectMapMode(oldMapMode);
261 oldMapMode = null;
262 } else {
263 Main.map.selectSelectTool(false);
264 }
265 }
266 }
267 }