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 }