Index: /trunk/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java	(revision 8495)
+++ /trunk/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java	(revision 8496)
@@ -5,4 +5,5 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.MatchResult;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -15,5 +16,7 @@
     private final OsmPrimitiveType type;
 
-    public static final Pattern ID_PATTERN = Pattern.compile("((n(ode)?|w(ay)?|r(el(ation)?)?)[ /]?)(\\d+)");
+    public static final Pattern ID_PATTERN = Pattern.compile("(n|node|w|way|r|rel|relation)[ /]?(\\d+)");
+
+    public static final Pattern MULTIPLE_IDS_PATTERN = Pattern.compile(ID_PATTERN.pattern() + "(-(\\d+))?");
 
     public SimplePrimitiveId(long id, OsmPrimitiveType type) {
@@ -80,10 +83,5 @@
         final Matcher m = ID_PATTERN.matcher(s);
         if (m.matches()) {
-            return new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())),
-                    s.charAt(0) == 'n'
-                            ? OsmPrimitiveType.NODE
-                            : s.charAt(0) == 'w'
-                            ? OsmPrimitiveType.WAY
-                            : OsmPrimitiveType.RELATION);
+            return new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())), getOsmPrimitiveType(s.charAt(0)));
         } else {
             throw new IllegalArgumentException("The string " + s + " does not match the pattern " + ID_PATTERN);
@@ -92,21 +90,56 @@
 
     /**
+     * Parses a range {@code SimplePrimitiveId} from the string {@code s}.
+     * @param s the string to be parsed, e.g., {@code node1}, {@code node1-7}, {@code node70-7}.
+     * @return the parsed {@code SimplePrimitiveId}s
+     * @throws IllegalArgumentException if the string does not match the pattern
+     */
+    public static List<SimplePrimitiveId> multipleFromString(String s) {
+        final Matcher m = MULTIPLE_IDS_PATTERN.matcher(s);
+        if (m.matches()) {
+            return extractIdsInto(m, new ArrayList<SimplePrimitiveId>());
+        } else {
+            throw new IllegalArgumentException("The string " + s + " does not match the pattern " + MULTIPLE_IDS_PATTERN);
+        }
+    }
+
+    /**
      * Attempts to parse extract any primitive id from the string {@code s}.
-     * @param s the string to be parsed, e.g., {@code n1, w1}, {@code node1 and rel2}.
+     * @param s the string to be parsed, e.g., {@code "n1, w1"}, {@code "node1 and rel2"}, {@code "node 123-29"}.
      * @return the parsed list of {@code OsmPrimitiveType}s.
      */
     public static List<SimplePrimitiveId> fuzzyParse(String s) {
         final List<SimplePrimitiveId> ids = new ArrayList<>();
-        final Matcher m = ID_PATTERN.matcher(s);
+        final Matcher m = MULTIPLE_IDS_PATTERN.matcher(s);
         while (m.find()) {
-            final char firstChar = s.charAt(m.start());
-            ids.add(new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())),
-                    firstChar == 'n'
-                            ? OsmPrimitiveType.NODE
-                            : firstChar == 'w'
-                            ? OsmPrimitiveType.WAY
-                            : OsmPrimitiveType.RELATION));
+            extractIdsInto(m, ids);
         }
         return ids;
     }
+
+    private static List<SimplePrimitiveId> extractIdsInto(MatchResult m, List<SimplePrimitiveId> ids) {
+        final OsmPrimitiveType type = getOsmPrimitiveType(m.group(1).charAt(0));
+        final String firstId = m.group(2);
+        final String lastId = m.group(4);
+        if (lastId != null) {
+            final long lastIdParsed;
+            if (lastId.length() < firstId.length()) {
+                // parse ranges such as 123-25 or 123-5
+                lastIdParsed = Long.parseLong(firstId.substring(0, firstId.length() - lastId.length()) + lastId);
+            } else {
+                // parse ranges such as 123-125 or 998-1001
+                lastIdParsed = Long.parseLong(lastId);
+            }
+            for (long i = Long.parseLong(firstId); i <= lastIdParsed; i++) {
+                ids.add(new SimplePrimitiveId(i, type));
+            }
+        } else {
+            ids.add(new SimplePrimitiveId(Long.parseLong(firstId), type));
+        }
+        return ids;
+    }
+
+    private static OsmPrimitiveType getOsmPrimitiveType(char firstChar) {
+        return firstChar == 'n' ? OsmPrimitiveType.NODE : firstChar == 'w' ? OsmPrimitiveType.WAY : OsmPrimitiveType.RELATION;
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java	(revision 8495)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java	(revision 8496)
@@ -91,13 +91,15 @@
         tfId.setPreferredSize(new Dimension(400, tfId.getPreferredSize().height));
 
-        HtmlPanel help = new HtmlPanel(/* I18n: {0} and {1} contains example strings not meant for translation. {2}=n, {3}=w, {4}=r. */
-                tr("Object IDs can be separated by comma or space.<br/>"
-                        + "Examples: {0}<br/>"
-                        + "In mixed mode, specify objects like this: {1}<br/>"
-                        + "({2} stands for <i>node</i>, {3} for <i>way</i>, and {4} for <i>relation</i>)",
-                        "<b>" + Utils.joinAsHtmlUnorderedList(Arrays.asList("1 2 5", "1,2,5")) + "</b>",
-                        "<b>w123, n110, w12, r15</b>",
-                        "<b>n</b>", "<b>w</b>", "<b>r</b>"
-                ));
+        final String help1 = /* I18n: {0} and contains example strings not meant for translation. */
+                tr("Object IDs can be separated by comma or space, for instance: {0}",
+                        "<b>" + Utils.joinAsHtmlUnorderedList(Arrays.asList("1 2 5", "1,2,5")) + "</b>");
+        final String help2 = /* I18n: {0} and contains example strings not meant for translation. {1}=n, {2}=w, {3}=r. */
+                tr("In mixed mode, specify objects like this: {0}<br/>"
+                                + "({1} stands for <i>node</i>, {2} for <i>way</i>, and {3} for <i>relation</i>)",
+                        "<b>w123, n110, w12, r15</b>", "<b>n</b>", "<b>w</b>", "<b>r</b>");
+        final String help3 = /* I18n: {0} and contains example strings not meant for translation. */
+                tr("Ranges of object IDs are specified with a hyphen, for instance: {0}",
+                        "<b>" + Utils.joinAsHtmlUnorderedList(Arrays.asList("w1-5", "n30-37", "r501-5")) + "</b>");
+        HtmlPanel help = new HtmlPanel(help1 + "<br/>" + help2 + "<br/><br/>" + help3);
         help.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
 
Index: /trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java	(revision 8495)
+++ /trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java	(revision 8496)
@@ -107,5 +107,5 @@
                 } else {
                     try {
-                        ids.add(SimplePrimitiveId.fromString(s));
+                        ids.addAll(SimplePrimitiveId.multipleFromString(s));
                     } catch (IllegalArgumentException ex) {
                         try {
Index: /trunk/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy	(revision 8495)
+++ /trunk/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy	(revision 8496)
@@ -24,7 +24,12 @@
     }
 
+    void testBad() {
+        shouldFail { SimplePrimitiveId.fromString("foobar") }
+    }
+
     void testFuzzy() {
         assert SimplePrimitiveId.fuzzyParse("foo relation/123 bar").toString() == "[relation 123]"
         assert SimplePrimitiveId.fuzzyParse("foo relation/123 and way/345 but also node/789").toString() == "[relation 123, way 345, node 789]"
+        assert SimplePrimitiveId.fuzzyParse("foo relation/123-24 and way/345-346 but also node/789").toString() == "[relation 123, relation 124, way 345, way 346, node 789]"
     }
 
@@ -34,3 +39,23 @@
         assert SimplePrimitiveId.fromString("relation 123") == new SimplePrimitiveId(123, OsmPrimitiveType.RELATION)
     }
+
+    void testMultipleIDs() {
+        assert SimplePrimitiveId.multipleFromString("node/234").toString() == "[node 234]"
+        assert SimplePrimitiveId.multipleFromString("node/234-234").toString() == "[node 234]"
+        assert SimplePrimitiveId.multipleFromString("node/2-1").toString() == "[]"
+        assert SimplePrimitiveId.multipleFromString("node/123-124").toString() == "[node 123, node 124]"
+        assert SimplePrimitiveId.multipleFromString("n/123-124").toString() == "[node 123, node 124]"
+        assert SimplePrimitiveId.multipleFromString("node123-126").toString() == "[node 123, node 124, node 125, node 126]"
+        assert SimplePrimitiveId.multipleFromString("way/123-123").toString() == "[way 123]"
+        assert SimplePrimitiveId.multipleFromString("w/123-127").toString() == "[way 123, way 124, way 125, way 126, way 127]"
+        assert SimplePrimitiveId.multipleFromString("way123-125").toString() == "[way 123, way 124, way 125]"
+        assert SimplePrimitiveId.multipleFromString("relation/123-125").toString() == "[relation 123, relation 124, relation 125]"
+        assert SimplePrimitiveId.multipleFromString("r/123-125").toString() == "[relation 123, relation 124, relation 125]"
+        assert SimplePrimitiveId.multipleFromString("relation123-125").toString() == "[relation 123, relation 124, relation 125]"
+        assert SimplePrimitiveId.multipleFromString("node/234-5").toString() == "[node 234, node 235]"
+        assert SimplePrimitiveId.multipleFromString("node/234-35").toString() == "[node 234, node 235]"
+        assert SimplePrimitiveId.multipleFromString("node/234-235").toString() == "[node 234, node 235]"
+        assert SimplePrimitiveId.multipleFromString("node/998-1001").toString() == "[node 998, node 999, node 1000, node 1001]"
+        shouldFail { SimplePrimitiveId.multipleFromString("foo node123 bar") }
+    }
 }
