Ignore:
Timestamp:
2009-02-24T17:58:50+01:00 (16 years ago)
Author:
stoecker
Message:

close #383. patch by xeen

File:
1 edited

Legend:

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

    r1430 r1438  
    132132     */
    133133    private void addHighlighting() {
     134        removeHighlighting();
    134135        // if ctrl key is held ("no join"), don't highlight anything
    135136        if (ctrl) {
    136             removeHighlighting();
    137137            resetCursor();
    138138            return;
    139139        }
     140
     141        // This happens when nothing is selected
     142        if(mouseOnExistingNode == null && Main.ds.getSelected().size() == 0 && mousePos != null)
     143            mouseOnExistingNode = Main.map.mapView.getNearestNode(mousePos);
    140144
    141145        if (mouseOnExistingNode != null) {
    142146            setJoinCursor(false);
    143             // Clean old highlights
    144             removeHighlighting();
    145             if(drawTargetHighlight) {
    146                 oldHighlights.add(mouseOnExistingNode);
     147            // We also need this list for the statusbar help text
     148            oldHighlights.add(mouseOnExistingNode);
     149            if(drawTargetHighlight)
    147150                mouseOnExistingNode.highlighted = true;
    148             }
    149151            return;
    150152        }
     
    152154        // Insert the node into all the nearby way segments
    153155        if(mouseOnExistingWays.size() == 0) {
    154             removeHighlighting();
    155156            resetCursor();
    156157            return;
     
    158159
    159160        setJoinCursor(true);
    160         // Clean old highlights
    161         removeHighlighting();
    162 
     161
     162        // We also need this list for the statusbar help text
     163        oldHighlights.addAll(mouseOnExistingWays);
    163164        if(!drawTargetHighlight) return;
    164         oldHighlights.addAll(mouseOnExistingWays);
    165165        for(Way w : mouseOnExistingWays) {
    166166            w.highlighted = true;
     
    175175            prim.highlighted = false;
    176176        }
     177        oldHighlights = new HashSet<OsmPrimitive>();
    177178    }
    178179
     
    215216     */
    216217    public void eventDispatched(AWTEvent event) {
    217         if(Main.map == null ||Main.map.mapView == null || !Main.map.mapView.isDrawableLayer())
    218             return;
    219         InputEvent e = (InputEvent) event;
    220         ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    221         alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    222         shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     218        if(Main.map == null || Main.map.mapView == null || !Main.map.mapView.isDrawableLayer())
     219            return;
     220        updateKeyModifiers((InputEvent) event);
     221        addHighlighting();
    223222        computeHelperLine();
    224223    }
     
    235234        Main.ds.setSelected();
    236235        mouseClicked(e);
     236    }
     237
     238    /**
     239     * This function should be called when the user wishes to finish his current draw action.
     240     * If Potlatch Style is enabled, it will switch to select tool, otherwise simply disable
     241     * the helper line until the user chooses to draw something else.
     242     */
     243    private void finishDrawing() {
     244        lastUsedNode = null;
     245        wayIsFinished = true;
     246        Main.map.selectSelectTool(true);
     247
     248        // Redraw to remove the helper line stub
     249        removeHighlighting();
     250        computeHelperLine();
     251        Main.map.mapView.repaint();
    237252    }
    238253
     
    248263        if(!Main.map.mapView.isDrawableLayer())
    249264            return;
    250        
     265
    251266        if(e.getClickCount() > 1 && mousePos != null && mousePos.equals(oldMousePos)) {
    252267            // A double click equals "user clicked last node again, finish way"
    253268            // Change draw tool only if mouse position is nearly the same, as
    254269            // otherwise fast clicks will count as a double click
    255             lastUsedNode = null;
    256             wayIsFinished = true;
    257 
    258             Main.map.selectSelectTool(true);
     270            finishDrawing();
    259271            return;
    260272        }
    261273        oldMousePos = mousePos;
    262        
     274
    263275        // we copy ctrl/alt/shift from the event just in case our global
    264276        // AWTEvent didn't make it through the security manager. Unclear
    265277        // if that can ever happen but better be safe.
    266         ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    267         alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    268         shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     278        updateKeyModifiers(e);
    269279        mousePos = e.getPoint();
    270280
     
    277287        Node n = null;
    278288
    279         if (!ctrl)
     289        if (!ctrl && !shift)
    280290            n = Main.map.mapView.getNearestNode(mousePos);
    281291
    282292        if (n != null) {
    283293            // user clicked on node
    284             if (shift || selection.isEmpty()) {
     294            if (selection.isEmpty()) {
    285295                // select the clicked node and do nothing else
    286296                // (this is just a convenience option so that people don't
     
    330340                    for (int i : is) wnew.addNode(i + 1, n);
    331341
     342                    // If ALT is pressed, a new way should be created and that new way should get
     343                    // selected. This works everytime unless the ways the nodes get inserted into
     344                    // are already selected. This is the case when creating a self-overlapping way
     345                    // but pressing ALT prevents this. Therefore we must de-select the way manually
     346                    // here so /only/ the new way will be selected after this method finishes.
     347                    if(alt) wnew.selected = false;
     348
    332349                    cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
    333350                    replacedWays.add(insertPoint.getKey());
     
    351368        boolean wayIsFinishedTemp = wayIsFinished;
    352369        wayIsFinished = false;
    353         if (!shift && selection.size() > 0 && !wayIsFinishedTemp) {
     370        if (selection.size() > 0 && !wayIsFinishedTemp) {
    354371            Node selectedNode = null;
    355372            Way selectedWay = null;
     
    373390            }
    374391
    375             // No nodes or ways have been selected, try again with no selection
    376             // This occurs when a relation has been selected
    377             if(selectedNode == null && selectedWay == null) {
     392            // the node from which we make a connection
     393            Node n0 = findNodeToContinueFrom(selectedNode, selectedWay);
     394            // We have a selection but it isn't suitable. Try again.
     395            if(n0 == null) {
    378396                tryAgain(e);
    379397                return;
    380398            }
    381399
    382             // the node from which we make a connection
    383             Node n0 = null;
    384 
    385             if (selectedNode == null) {
    386                 if (selectedWay.isFirstLastNode(lastUsedNode)) {
    387                     n0 = lastUsedNode;
    388                 } else {
    389                     // We have a way selected, but no suitable node to continue from. Start anew.
    390                     tryAgain(e);
    391                     return;
    392                 }
    393             } else if (selectedWay == null) {
    394                 n0 = selectedNode;
    395             } else {
    396                 if (selectedWay.isFirstLastNode(selectedNode)) {
    397                     n0 = selectedNode;
    398                 } else {
    399                     // We have a way and node selected, but it's not at the start/end of the way. Start anew.
    400                     tryAgain(e);
    401                     return;
    402                 }
    403             }
    404 
    405             // Prevent creation of ways that look like this: <---->
    406             // This happens if users want to draw a no-exit-sideway from the main way like this:
    407             // ^
    408             // |<---->
    409             // |
    410             // The solution isn't ideal because the main way will end in the side way, which is bad for
    411             // navigation software ("drive straight on") but at least easier to fix. Maybe users will fix
    412             // it on their own, too. At least it's better than producing an error.
    413             if(selectedWay != null && selectedWay.nodes != null) {
    414                 int posn0 = selectedWay.nodes.indexOf(n0);
    415                 if( posn0 != -1 && // n0 is part of way
    416                     (posn0 >= 1                          && n.equals(selectedWay.nodes.get(posn0-1))) || // previous node
    417                     (posn0 < selectedWay.nodes.size()-1) && n.equals(selectedWay.nodes.get(posn0+1))) {  // next node
    418                     Main.ds.setSelected(n);
    419                     lastUsedNode = n;
    420                     return;
    421                 }
    422             }
     400            if(isSelfContainedWay(selectedWay, n0, n))
     401                return;
    423402
    424403            // User clicked last node again, finish way
    425404            if(n0 == n) {
    426                 lastUsedNode = null;
    427                 wayIsFinished = true;
    428                 Main.map.selectSelectTool(true);
     405                finishDrawing();
    429406                return;
    430407            }
     
    460437            // Connected to a node that's already in the way
    461438            if(way.nodes.contains(n)) {
    462                 //System.out.println("Stop drawing, node is part of current way");
    463439                wayIsFinished = true;
    464440                selection.clear();
     
    498474        Main.main.undoRedo.add(c);
    499475        if(!wayIsFinished) lastUsedNode = n;
     476
    500477        computeHelperLine();
    501478        removeHighlighting();
     
    503480    }
    504481
     482    /**
     483     * Prevent creation of ways that look like this: <---->
     484     * This happens if users want to draw a no-exit-sideway from the main way like this:
     485     * ^
     486     * |<---->
     487     * |
     488     * The solution isn't ideal because the main way will end in the side way, which is bad for
     489     * navigation software ("drive straight on") but at least easier to fix. Maybe users will fix
     490     * it on their own, too. At least it's better than producing an error.
     491     *
     492     * @param Way the way to check
     493     * @param Node the current node (i.e. the one the connection will be made from)
     494     * @param Node the target node (i.e. the one the connection will be made to)
     495     * @return Boolean True if this would create a selfcontaining way, false otherwise.
     496     */
     497    private boolean isSelfContainedWay(Way selectedWay, Node currentNode, Node targetNode) {
     498        if(selectedWay != null && selectedWay.nodes != null) {
     499            int posn0 = selectedWay.nodes.indexOf(currentNode);
     500            if( posn0 != -1 && // n0 is part of way
     501                (posn0 >= 1                          && targetNode.equals(selectedWay.nodes.get(posn0-1))) || // previous node
     502                (posn0 < selectedWay.nodes.size()-1) && targetNode.equals(selectedWay.nodes.get(posn0+1))) {  // next node
     503                Main.ds.setSelected(targetNode);
     504                lastUsedNode = targetNode;
     505                return true;
     506            }
     507        }
     508
     509        return false;
     510    }
     511
     512    /**
     513     * Finds a node to continue drawing from. Decision is based upon given node and way.
     514     * @param selectedNode Currently selected node, may be null
     515     * @param selectedWay Currently selected way, may be null
     516     * @return Node if a suitable node is found, null otherwise
     517     */
     518    private Node findNodeToContinueFrom(Node selectedNode, Way selectedWay) {
     519        // No nodes or ways have been selected, this occurs when a relation
     520        // has been selected or the selection is empty
     521        if(selectedNode == null && selectedWay == null)
     522            return null;
     523
     524        if (selectedNode == null) {
     525            if (selectedWay.isFirstLastNode(lastUsedNode))
     526                return lastUsedNode;
     527
     528            // We have a way selected, but no suitable node to continue from. Start anew.
     529            return null;
     530        }
     531
     532        if (selectedWay == null)
     533            return selectedNode;
     534
     535        if (selectedWay.isFirstLastNode(selectedNode))
     536            return selectedNode;
     537
     538        // We have a way and node selected, but it's not at the start/end of the way. Start anew.
     539        return null;
     540    }
     541
    505542    @Override public void mouseMoved(MouseEvent e) {
    506543        if(!Main.map.mapView.isDrawableLayer())
     
    510547        // AWTEvent didn't make it through the security manager. Unclear
    511548        // if that can ever happen but better be safe.
    512 
     549        updateKeyModifiers(e);
     550        mousePos = e.getPoint();
     551
     552        addHighlighting();
     553        computeHelperLine();
     554    }
     555
     556    private void updateKeyModifiers(InputEvent e) {
    513557        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    514558        alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    515559        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    516         mousePos = e.getPoint();
    517 
    518         addHighlighting();
    519         computeHelperLine();
     560    }
     561
     562    private void updateKeyModifiers(MouseEvent e) {
     563        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     564        alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     565        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    520566    }
    521567
     
    532578            return;
    533579        }
    534        
     580
    535581        double distance = -1;
    536582        double angle = -1;
     
    548594        Main.map.statusLine.setDist(-1);
    549595
    550         if (!ctrl && mousePos != null) {
     596        if (!ctrl && !shift && mousePos != null) {
    551597            currentMouseNode = Main.map.mapView.getNearestNode(mousePos);
    552598        }
     
    586632        if (selectedNode == null) {
    587633            if (selectedWay == null) return;
    588             if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
     634            if (selectedWay.isFirstLastNode(lastUsedNode)) {
    589635                currentBaseNode = lastUsedNode;
    590636                if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) {
     
    601647
    602648        if (currentBaseNode == null || currentBaseNode == currentMouseNode) {
     649            updateStatusLine();
    603650            return; // Don't create zero length way segments.
    604651        }
     
    617664        updateStatusLine();
    618665
    619         if ((!drawHelperLine || wayIsFinished) && !drawTargetHighlight) return;
     666        if (!drawHelperLine || wayIsFinished) return;
    620667        Main.map.mapView.repaint();
    621668    }
     
    739786
    740787    // helper for adjustNode
    741     static double det(double a, double b, double c, double d)
    742     {
     788    static double det(double a, double b, double c, double d) {
    743789        return a * d - b * c;
    744790    }
     
    750796        if (Main.map.mapView == null) return;
    751797        if (mousePos == null) return;
    752 
    753         // if shift key is held ("no auto-connect"), don't draw a line
    754         if (shift) return;
    755798
    756799        // don't draw line if we don't know where from or where to
     
    782825
    783826    @Override public String getModeHelpText() {
    784         String rv;
    785 
    786         if (currentBaseNode != null && !shift) {
    787             if (mouseOnExistingNode != null) {
    788                 if (alt && /* FIXME: way exists */true)
    789                     rv = tr("Click to create a new way to the existing node.");
     827        String rv = "";
     828        /*
     829         *  No modifiers: all (Connect, Node Re-Use, Auto-Weld)
     830         *  CTRL: disables node re-use, auto-weld
     831         *  Shift: disables node re-use
     832         *  ALT: disables connect
     833         */
     834
     835        /*
     836         * Status line text generation is split into two parts to keep it maintainable.
     837         * First part looks at what will happen to the new node inserted on click and
     838         * the second part will look if a connection is made or not.
     839         *
     840         * Note that this help text is not absolutely accurate as it doesn't catch any special
     841         * cases (e.g. when preventing <---> ways). The only special that it catches is when
     842         * a way is about to be finished.
     843         *
     844         * First check what happens to the new node.
     845         */
     846
     847        // oldHighlights stores the current highlights. If this
     848        // list is empty we can assume that we won't do any joins
     849        if(ctrl || oldHighlights.isEmpty())
     850            rv = tr("Create new node.");
     851        else if(shift) {
     852            // We already know oldHighlights is not empty, but shift is pressed.
     853            // We can assume the new node will be joined into an existing way
     854            rv = tr("Insert new node into {0} way(s).", oldHighlights.size());
     855        } else {
     856            // oldHighlights may store a node or way, check if it's a node
     857            for(OsmPrimitive x : oldHighlights) {
     858                if(x instanceof Node)
     859                    rv = tr("Select node under cursor.");
    790860                else
    791                     rv =tr("Click to make a connection to the existing node.");
    792             } else {
    793                 if (alt && /* FIXME: way exists */true)
    794                     rv = tr("Click to insert a node and create a new way.");
    795                 else
    796                     rv = tr("Click to insert a new node and make a connection.");
    797             }
    798         }
    799         else {
    800             rv = tr("Click to insert a new node.");
    801         }
    802 
    803         //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
    804         //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
    805         return rv.toString();
     861                    rv = tr("Insert new node into {0} way(s).", oldHighlights.size());
     862                break;
     863            }
     864        }
     865
     866        /*
     867         * Check whether a connection will be made
     868         */
     869        if (currentBaseNode != null) {
     870            if(alt)
     871                rv += " " + tr("Start new way from last node.");
     872            else
     873                rv += " " + tr("Continue way from last node.");
     874        }
     875
     876        /*
     877         * Handle special case: Highlighted node == selected node => finish drawing
     878         */
     879        Node n = mouseOnExistingNode;
     880        if(n != null && Main.ds.getSelectedNodes().contains(n)) {
     881            rv = tr("Finish drawing.");
     882        }
     883
     884        /*
     885         * Handle special case: Self-Overlapping or closing way
     886         */
     887        if(Main.ds.getSelectedWays().size() > 0 && !wayIsFinished && !alt) {
     888            Way w = (Way) Main.ds.getSelectedWays().iterator().next();
     889            for(Node m : w.nodes) {
     890                if(m.equals(mouseOnExistingNode) || mouseOnExistingWays.contains(w)) {
     891                    rv += " " + tr("Finish drawing.");
     892                    break;
     893                }
     894            }
     895        }
     896
     897        return rv;
    806898    }
    807899
Note: See TracChangeset for help on using the changeset viewer.