Add a continuous selection

From:  <>

Select all continuous segments
---

 images/mapmode/selection/continuous.png            |  Bin
 .../josm/actions/mapmode/SelectionAction.java      |  121 ++++++++++++++++++++---
 2 files changed, 106 insertions(+), 15 deletions(-)

diff --git a/images/mapmode/selection/continuous.png b/images/mapmode/selection/continuous.png
new file mode 100644
index 0000000..6c05cb7
Binary files /dev/null and b/images/mapmode/selection/continuous.png differ
diff --git a/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java b/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java
index 62d747b..3ad577d 100644
--- a/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java
+++ b/src/org/openstreetmap/josm/actions/mapmode/SelectionAction.java
@@ -63,7 +63,7 @@ import org.openstreetmap.josm.tools.ImageProvider;
  */
 public class SelectionAction extends MapMode implements SelectionEnded {
 
-	enum Mode {select, straight}
+	enum Mode {select, straight, continuous}
 	private final Mode mode;
 
 	public static class Group extends GroupAction {
@@ -72,6 +72,7 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 			putValue("help", "Action/Selection");
 			actions.add(new SelectionAction(mf, tr("Selection"), Mode.select, tr("Select objects by dragging or clicking.")));
 			actions.add(new SelectionAction(mf, tr("Straight line"), Mode.straight, tr("Select objects in a straight line.")));
+			actions.add(new SelectionAction(mf, tr("Continuous segments"), Mode.continuous, tr("Select continuous segments.")));
 			setCurrent(0);
 		}
 	}
@@ -85,10 +86,10 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 	private Node straightStart = null;
 	private Node lastEnd = null;
 	private Collection<OsmPrimitive> oldSelection = null;
-
+	
 	//TODO: Implement reverse references into data objects and remove this
 	private final Map<Node, Collection<Segment>> reverseSegmentMap = new HashMap<Node, Collection<Segment>>();
-
+	
 	/**
 	 * Create a new SelectionAction in the given frame.
 	 * @param mapFrame The frame this action belongs to
@@ -104,13 +105,19 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 		super.enterMode();
 		if (mode == Mode.select)
 			selectionManager.register(Main.map.mapView);
-		else {
+		else if (mode == Mode.straight) {
 			Main.map.mapView.addMouseMotionListener(this);
 			Main.map.mapView.addMouseListener(this);
 			for (Segment s : Main.ds.segments) {
 				addBackReference(s.from, s);
 				addBackReference(s.to, s);
 			}
+		} else if (mode == Mode.continuous) {
+			Main.map.mapView.addMouseListener(this);
+			for (Segment s : Main.ds.segments) {
+				addBackReference(s.from, s);
+				addBackReference(s.to, s);
+			}
 		}
 	}
 
@@ -127,10 +134,12 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 		super.exitMode();
 		if (mode == Mode.select)
 			selectionManager.unregister(Main.map.mapView);
-		else {
+		else if (mode == Mode.straight) {
 			Main.map.mapView.removeMouseMotionListener(this);
 			Main.map.mapView.removeMouseListener(this);
 			reverseSegmentMap.clear();
+		} else if (mode == Mode.continuous) {
+			Main.map.mapView.removeMouseListener(this);
 		}
 	}
 
@@ -169,25 +178,60 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 			straightStart = lastEnd;
 		if (straightStart != null && lastEnd != null && straightStart != lastEnd && old != lastEnd) {
 			Collection<OsmPrimitive> path = new HashSet<OsmPrimitive>();
-			Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
 			path.add(straightStart);
 			calculateShortestPath(path, straightStart, lastEnd);
-			if ((e.getModifiers() & MouseEvent.CTRL_MASK) != 0) {
-				sel.addAll(oldSelection);
-				sel.removeAll(path);
-			} else if ((e.getModifiers() & MouseEvent.SHIFT_MASK) != 0) {
-				sel = path;
-				sel.addAll(oldSelection);
-			} else
-				sel = path;
-			Main.ds.setSelected(sel);
+			updateSelection(e, path);
 		}
 	}
 
+	/**
+	 * Update the selection.
+	 * 
+	 * Handles the modifier key.
+	 *  
+	 * @param e
+	 * @param path the path to use for updating.
+	 */
+	private void updateSelection(MouseEvent e, Collection<OsmPrimitive> path) {
+		Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
+		if ((e.getModifiers() & MouseEvent.CTRL_MASK) != 0) {
+	    	sel.addAll(oldSelection);
+	    	sel.removeAll(path);
+	    } else if ((e.getModifiers() & MouseEvent.SHIFT_MASK) != 0) {
+	    	sel = path;
+	    	sel.addAll(oldSelection);
+	    } else
+	    	sel = path;
+	    Main.ds.setSelected(sel);
+    }
+
+	/**
+	 * Continuous selection.
+	 * 
+	 * Does a continuous selection.
+	 * 
+	 * @param e
+	 */
+	private void continuousSelection(MouseEvent e) {
+		// Retrieve the initial segment
+		Segment initialSegment = Main.map.mapView.getNearestSegment(e.getPoint());
+		if (initialSegment != null) {
+			// Initialise the path
+			Collection<OsmPrimitive> path = new HashSet<OsmPrimitive>();
+			path.add(initialSegment);
+			// Fill the path with continuous segments
+			calculateContinuousPath(path, initialSegment);
+			// Update the selection with the path 
+			updateSelection(e, path);
+		}
+	}
+	
 	@Override public void mousePressed(MouseEvent e) {
 		straightStart = Main.map.mapView.getNearestNode(e.getPoint());
 		lastEnd = null;
 		oldSelection = Main.ds.getSelected();
+		
+		if (mode == Mode.continuous) continuousSelection(e);
 	}
 
 	@Override public void mouseReleased(MouseEvent e) {
@@ -234,4 +278,51 @@ public class SelectionAction extends MapMode implements SelectionEnded {
 		}
 		return null;
 	}
+
+	/**
+	 * Get the continuous path.
+	 * 
+	 * @param path Path to fill in
+	 * @param initial initial segment
+	 */
+	private void calculateContinuousPath(Collection<OsmPrimitive> path, Segment initial) {
+		Segment current = null;
+		Segment next = initial;
+		Collection<Segment> c = null;
+		// Reverse order
+		while ((current = next) != null && (c = reverseSegmentMap.get(current.from)) != null && c.size() == 2) {
+			for (Segment s : c) {
+				if (s != current) {
+					// s is the other segment connected to current node
+					if (s != initial && s.to == current.from) {
+						// s is the preceding segment
+						path.add(s);
+						next = s;
+					} else
+						// s and next are connected to the same node, but in opposite direction.
+						// Stop here.
+						next = null;
+				}
+			}
+		}
+			
+		next = initial;
+		// Correct order
+		while ((current = next) != null && (c = reverseSegmentMap.get(current.to)) != null && c.size() == 2) {
+			for (Segment s : c) {
+				if (s != current) {
+					// s is the other segment connected to current node
+					if (s != initial && s.from == current.to) {
+						// s is the preceding segment
+						path.add(s);
+						next = s;
+					} else
+						// s and next are connected to the same node, but in opposite direction.
+						// Stop here.
+						next = null;
+				}
+			}
+		}
+		
+	}
 }
