Changeset 4782 in josm for trunk/src


Ignore:
Timestamp:
2012-01-10T19:46:35+01:00 (8 years ago)
Author:
akks
Message:

Angle snapping shown in Edit menu, holding TAB fixes the angle etc. - see #6694

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java

    r4776 r4782  
    22package org.openstreetmap.josm.actions.mapmode;
    33
     4import javax.swing.JCheckBoxMenuItem;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56import static org.openstreetmap.josm.tools.I18n.trn;
    67import static org.openstreetmap.josm.tools.I18n.marktr;
     8import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    79
    810import java.awt.AWTEvent;
     
    1618import java.awt.event.AWTEventListener;
    1719import java.awt.event.ActionEvent;
     20import java.awt.event.ActionListener;
    1821import java.awt.event.InputEvent;
    1922import java.awt.event.KeyEvent;
     
    3235import java.util.Set;
    3336
     37import java.util.TreeSet;
    3438import javax.swing.AbstractAction;
    3539import javax.swing.JOptionPane;
    3640
    3741import javax.swing.SwingUtilities;
     42import javax.swing.Timer;
    3843import org.openstreetmap.josm.Main;
     44import org.openstreetmap.josm.actions.JosmAction;
    3945import org.openstreetmap.josm.command.AddCommand;
    4046import org.openstreetmap.josm.command.ChangeCommand;
     
    5157import org.openstreetmap.josm.data.osm.WaySegment;
    5258import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
     59import org.openstreetmap.josm.gui.MainMenu;
    5360import org.openstreetmap.josm.gui.MapFrame;
    5461import org.openstreetmap.josm.gui.MapView;
     
    9097    private Shortcut backspaceShortcut;
    9198   
    92     boolean snapOn;
     99    private JCheckBoxMenuItem snapCheckboxMenuItem;
     100   
    93101           
    94102    public DrawAction(MapFrame mapFrame) {
     
    100108        extraShortcut = Shortcut.registerShortcut("mapmode:drawfocus", tr("Mode: Draw Focus"), KeyEvent.VK_N, Shortcut.GROUP_EDIT);
    101109        Main.registerActionShortcut(this, extraShortcut);
    102 
     110       
     111        snapCheckboxMenuItem = MainMenu.addWithCheckbox(Main.main.menu.editMenu, new SnapChangeAction(),  MainMenu.WINDOW_MENU_GROUP.VOLATILE);
     112        snapHelper.setMenuCheckBox(snapCheckboxMenuItem);
    103113        cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode");
    104114        cursorJoinWay = ImageProvider.getCursor("crosshair", "joinway");
     
    135145        wayIsFinished = false;
    136146        snapHelper.init();
     147        snapCheckboxMenuItem.getAction().setEnabled(true);
     148       
     149         timer = new Timer(0, new ActionListener() {
     150            @Override
     151            public void actionPerformed(ActionEvent ae) {
     152                 timer.stop();
     153                 if (set.remove(releaseEvent.getKeyCode())) {
     154                   doKeyReleaseEvent(releaseEvent);
     155                 }
     156            }
     157
     158        });
    137159       
    138160        backspaceShortcut = Shortcut.registerShortcut("mapmode:backspace", tr("Backspace in Add mode"), KeyEvent.VK_BACK_SPACE, Shortcut.GROUP_EDIT);
     
    160182        Main.unregisterActionShortcut(backspaceShortcut);
    161183        snapHelper.unsetFixedMode();
    162 
     184        snapCheckboxMenuItem.getAction().setEnabled(false);
     185       
    163186        removeHighlighting();
    164187        try {
     
    183206            return;
    184207        if (event instanceof KeyEvent) {
    185                 KeyEvent ke = (KeyEvent) event;
    186                 if (ke.getKeyCode() == KeyEvent.VK_TAB &&
    187                     ke.getID()==KeyEvent.KEY_PRESSED) {
    188                     snapHelper.nextSnapMode();
    189                 }
     208                processKeyEvent((KeyEvent) event);
    190209        } //  toggle angle snapping
    191210        updateKeyModifiers((InputEvent) event);
     
    194213        redrawIfRequired();
    195214    }
     215   
     216   
     217    // events for crossplatform key holding processing
     218    // thanks to http://www.arco.in-berlin.de/keyevent.html
     219    private final TreeSet<Integer> set = new TreeSet<Integer>();
     220    private KeyEvent releaseEvent;
     221    private Timer timer;
     222    void processKeyEvent(KeyEvent e) {
     223        if (e.getKeyCode() != KeyEvent.VK_TAB) return;
     224        e.consume();
     225
     226        if (e.getID() == KeyEvent.KEY_PRESSED) {
     227             if (timer.isRunning()) {
     228                  timer.stop();
     229                } else {
     230                  if (set.add((e.getKeyCode()))) doKeyPressEvent(e);
     231                }
     232             
     233        }
     234        if (e.getID() == KeyEvent.KEY_RELEASED) {
     235            if (timer.isRunning()) {
     236              timer.stop();
     237               if (set.remove(e.getKeyCode())) {
     238                  doKeyReleaseEvent(e);
     239               }
     240            } else {
     241              releaseEvent = e;
     242              timer.restart();
     243            }
     244        }
     245       
     246    }
     247   
     248    private void doKeyPressEvent(KeyEvent e) {
     249        if (e.getKeyCode() != KeyEvent.VK_TAB) return;
     250        snapHelper.setFixedMode();
     251        computeHelperLine(); redrawIfRequired();
     252    }
     253    private void doKeyReleaseEvent(KeyEvent e) {
     254        if (e.getKeyCode() != KeyEvent.VK_TAB) return;
     255        snapHelper.unFixOrTurnOff();
     256        computeHelperLine(); redrawIfRequired();
     257    }
     258
    196259    /**
    197260     * redraw to (possibly) get rid of helper line if selection changes.
     
    265328        //
    266329        Main.map.mapView.requestFocus();
    267 
     330       
    268331        if(e.getClickCount() > 1 && mousePos != null && mousePos.equals(oldMousePos)) {
    269332            // A double click equals "user clicked last node again, finish way"
     
    325388            } else { // n==null, no node found in clicked area
    326389                EastNorth mouseEN = Main.map.mapView.getEastNorth(e.getX(), e.getY());
    327                 newEN = snapOn ? snapHelper.getSnapPoint(mouseEN) : mouseEN;
     390                newEN = snapHelper.isSnapOn() ? snapHelper.getSnapPoint(mouseEN) : mouseEN;
    328391                n = new Node(newEN); //create node at clicked point
    329392                newNode = true;
     
    699762        }
    700763       
    701         if (snapOn) snapHelper.checkAngleSnapping(currentMouseEastNorth,angle);
     764        snapHelper.checkAngleSnapping(currentMouseEastNorth,angle);
    702765       
    703766        Main.map.statusLine.setAngle(angle);
     
    9491012       
    9501013        Graphics2D g2 = g;
    951         if (snapOn) snapHelper.draw(g2,mv);
     1014        snapHelper.drawIfNeeded(g2,mv);
    9521015        if (!drawHelperLine || wayIsFinished || shift) return;
    9531016       
     
    9551018            g2.setColor(selectedColor);
    9561019            g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
     1020        } else {
     1021            if (!snapHelper.drawConstructionGeometry) return;
    9571022        }
    9581023        GeneralPath b = new GeneralPath();
     
    10191084                rv += " " + tr("Continue way from last node.");
    10201085            }
    1021             if (snapOn) {
    1022                 rv += " "+ tr("Angle snapping ON.");
     1086            if (snapHelper.isSnapOn()) {
     1087                rv += " "+ tr("Angle snapping active.");
    10231088            }
    10241089        }
     
    10931158
    10941159    private class SnapHelper {
     1160        boolean snapOn; // snapping is turned on
     1161       
    10951162        private boolean active; // snapping is activa for current mouse position
    10961163        private boolean fixed; // snap angle is fixed
    10971164        private boolean absoluteFix; // snap angle is absolute
     1165       
     1166        private boolean drawConstructionGeometry;
     1167        private boolean showProjectedPoint;
     1168        private boolean showAngle;
     1169       
    10981170        EastNorth dir2;
    10991171        EastNorth projected;
     
    11111183        private Stroke normalStroke;
    11121184        private Stroke helperStroke;
    1113 
    1114         private  void init() {
     1185       
     1186        JCheckBoxMenuItem checkBox;
     1187       
     1188        public void init() {
    11151189            snapOn=false;
     1190            checkBox.setState(snapOn);
    11161191            fixed=false; absoluteFix=false;
    1117            
     1192                       
    11181193            Collection<String> angles = Main.pref.getCollection("draw.anglesnap.angles",
    11191194                    Arrays.asList("0","30","45","60","90","120","135","150","210","225","240","270","300","315","330"));
     
    11311206            }
    11321207            snapAngleTolerance = Main.pref.getDouble("draw.anglesnap.tolerance", 5.0);
     1208            drawConstructionGeometry = Main.pref.getBoolean("draw.anglesnap.drawConstructionGeometry", true);
     1209            showProjectedPoint = Main.pref.getBoolean("draw.anglesnap.drawProjectedPoint", true);
     1210            showAngle = Main.pref.getBoolean("draw.anglesnap.showAngle", true);
    11331211
    11341212            normalStroke = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
     
    11411219        }
    11421220       
    1143         private void noSnapNow() {
    1144             active=false;
    1145             dir2=null; projected=null;
    1146             labelText=null;
    1147         }
    1148 
    1149         private void draw(Graphics2D g2, MapView mv) {
     1221        public  void setMenuCheckBox(JCheckBoxMenuItem checkBox) {
     1222            this.checkBox = checkBox;
     1223        }
     1224       
     1225
     1226        public  void drawIfNeeded(Graphics2D g2, MapView mv) {
     1227            if (!snapOn) return;
    11501228            if (!active) return;
    11511229            Point p1=mv.getPoint(currentBaseNode);
     
    11531231            Point p3=mv.getPoint(projected);
    11541232            GeneralPath b;
    1155            
    1156             g2.setColor(snapHelperColor);
    1157             g2.setStroke(helperStroke);
    1158            
    1159             b = new GeneralPath();
    1160             if (absoluteFix) {
    1161                 b.moveTo(p2.x,p2.y);
    1162                 b.lineTo(2*p1.x-p2.x,2*p1.y-p2.y); // bi-directional line
    1163             } else {
    1164                 b.moveTo(p2.x,p2.y);
    1165                 b.lineTo(p3.x,p3.y);
    1166             }
    1167             g2.draw(b);
     1233            if (drawConstructionGeometry) {
     1234                g2.setColor(snapHelperColor);
     1235                g2.setStroke(helperStroke);
     1236
     1237                b = new GeneralPath();
     1238                if (absoluteFix) {
     1239                    b.moveTo(p2.x,p2.y);
     1240                    b.lineTo(2*p1.x-p2.x,2*p1.y-p2.y); // bi-directional line
     1241                } else {
     1242                    b.moveTo(p2.x,p2.y);
     1243                    b.lineTo(p3.x,p3.y);
     1244                }
     1245                g2.draw(b);
     1246            }
    11681247
    11691248            g2.setColor(selectedColor);
     
    11751254           
    11761255            g2.drawString(labelText, p3.x-5, p3.y+20);
    1177             g2.setStroke(normalStroke);
    1178             g2.drawOval(p3.x-5, p3.y-5, 10, 10); // projected point
     1256            if (showProjectedPoint) {
     1257                g2.setStroke(normalStroke);
     1258                g2.drawOval(p3.x-5, p3.y-5, 10, 10); // projected point
     1259            }
    11791260           
    11801261            g2.setColor(snapHelperColor);
     
    11831264        }
    11841265       
    1185         private double getAngleDelta(double a, double b) {
    1186             double delta = Math.abs(a-b);
    1187             if (delta>180) return 360-delta; else return delta;
    1188         }
    1189 
    11901266        /* If mouse position is close to line at 15-30-45-... angle, remembers this direction
    11911267         */
    1192         private void checkAngleSnapping(EastNorth currentEN, double angle) {
     1268        public  void checkAngleSnapping(EastNorth currentEN, double angle) {
     1269            if (!snapOn) return;
    11931270            if (!absoluteFix && previousNode==null) return;
    11941271           
     
    12081285                EastNorth p0 = currentBaseNode.getEastNorth();
    12091286                e0=p0.east(); n0=p0.north();
    1210 
    1211                 if (fixed) {
    1212                     if (absoluteFix) labelText = "=";
    1213                                 else labelText = String.format(fixFmt, (int) nearestAngle);
    1214                 } else labelText = String.format("%d", (int) nearestAngle);
     1287               
     1288                if (showAngle)  {
     1289                    if (fixed) {
     1290                        if (absoluteFix) labelText = "=";
     1291                                    else labelText = String.format(fixFmt, (int) nearestAngle);
     1292                    } else labelText = String.format("%d", (int) nearestAngle);
     1293                } else {
     1294                    if (fixed) {
     1295                        if (absoluteFix) labelText = "=";
     1296                        else labelText = String.format(tr("FIX"),0);
     1297                    } else labelText="";
     1298                }
    12151299               
    12161300                if (absoluteFix) {
     
    12371321        }
    12381322       
    1239         private EastNorth getSnapPoint(EastNorth p) {
     1323        public EastNorth getSnapPoint(EastNorth p) {
    12401324            if (!active) return p;
    12411325            double de=p.east()-e0;
     
    12461330        }
    12471331       
    1248         private void unsetFixedMode() {
    1249             fixed=false; absoluteFix=false;
    1250             lastAngle=0;
    1251             active=false;
    1252         }
    1253        
    1254         private void nextSnapMode() {
    1255             if (snapOn) {
    1256                 // turn off snapping if we are in fixed mode or no actile snapping line exist
    1257                 if (fixed || !active) { snapOn=false; unsetFixedMode(); }
    1258                 else if (active) { fixed=true; }
    1259             } else {
    1260                 snapOn=true;
    1261                 unsetFixedMode();
    1262             }
    1263         }
    1264 
    1265         private boolean isActive() {
    1266             return active;
    1267         }
    1268 
    1269         private double getNearestAngle(double angle) {
    1270             double delta,minDelta=1e5, bestAngle=0.0;
    1271             for (int i=0; i<snapAngles.length; i++) {
    1272                 delta = getAngleDelta(angle,snapAngles[i]);
    1273                 if (delta<minDelta) {
    1274                     minDelta=delta;
    1275                     bestAngle=snapAngles[i];
    1276                 }
    1277             }
    1278             if (Math.abs(bestAngle-360)<1e-3) bestAngle=0;
    1279             return bestAngle;
    1280         }
    1281 
    1282         private void fixToSegment(WaySegment seg) {
     1332       
     1333        public void noSnapNow() {
     1334            active=false;
     1335            dir2=null; projected=null;
     1336            labelText=null;
     1337        }
     1338
     1339        public void fixToSegment(WaySegment seg) {
    12831340            if (seg==null) return;
    12841341            double hdg = seg.getFirstNode().getEastNorth().heading(seg.getSecondNode().getEastNorth());
     
    12901347            lastAngle=hdg;
    12911348        }
     1349
     1350        private void nextSnapMode() {
     1351            if (snapOn) {
     1352                // turn off snapping if we are in fixed mode or no actile snapping line exist
     1353                if (fixed || !active) { snapOn=false; unsetFixedMode(); }
     1354                else setFixedMode();
     1355            } else {
     1356                snapOn=true;
     1357                unsetFixedMode();
     1358            }
     1359            checkBox.setState(snapOn);
     1360        }
     1361       
     1362        private void toggleSnapping() {
     1363            snapOn = !snapOn;
     1364            checkBox.setState(snapOn);
     1365            unsetFixedMode();
     1366        }
     1367               
     1368        public void setFixedMode() {
     1369            if (active) { fixed=true; }
     1370        }
     1371       
     1372       
     1373        public  void unsetFixedMode() {
     1374            fixed=false; absoluteFix=false;
     1375            lastAngle=0;
     1376            active=false;
     1377        }
     1378       
     1379        public  boolean isActive() {
     1380            return active;
     1381        }
     1382       
     1383        public  boolean isSnapOn() {
     1384            return snapOn;
     1385        }
     1386
     1387        private double getNearestAngle(double angle) {
     1388            double delta,minDelta=1e5, bestAngle=0.0;
     1389            for (int i=0; i<snapAngles.length; i++) {
     1390                delta = getAngleDelta(angle,snapAngles[i]);
     1391                if (delta<minDelta) {
     1392                    minDelta=delta;
     1393                    bestAngle=snapAngles[i];
     1394                }
     1395            }
     1396            if (Math.abs(bestAngle-360)<1e-3) bestAngle=0;
     1397            return bestAngle;
     1398        }
     1399
     1400        private double getAngleDelta(double a, double b) {
     1401            double delta = Math.abs(a-b);
     1402            if (delta>180) return 360-delta; else return delta;
     1403        }
     1404
     1405        private void unFixOrTurnOff() {
     1406            if (absoluteFix) unsetFixedMode(); else toggleSnapping();
     1407        }
     1408    }
     1409   
     1410    private class SnapChangeAction extends JosmAction {
     1411        public SnapChangeAction() {
     1412             super(tr("Angle snapping"), "anglesnap",
     1413                   tr("Switch angle snapping mode while drawing"),
     1414                   null, false);
     1415             putValue("help", ht("/Action/Draw/AngleSnap"));
     1416        }
     1417        @Override
     1418        public void actionPerformed(ActionEvent e) {
     1419               if (snapHelper!=null) snapHelper.toggleSnapping();
     1420        }
     1421       
    12921422    }
    12931423}
Note: See TracChangeset for help on using the changeset viewer.