Changeset 4782 in josm


Ignore:
Timestamp:
Jan 10, 2012 7:46:35 PM (16 months ago)
Author:
akks
Message:

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

Location:
trunk
Files:
1 added
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.