Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5228)
@@ -41,4 +41,5 @@
      */
     protected String pref = null;
+    protected Bounds bounds;
 
     protected static class Param {
@@ -63,8 +64,15 @@
         public final static Param lat_1 = new Param("lat_1", true);
         public final static Param lat_2 = new Param("lat_2", true);
+        public final static Param wktext = new Param("wktext", false);  // ignored
+        public final static Param units = new Param("units", true);     // ignored
+        public final static Param no_defs = new Param("no_defs", false);
+        public final static Param init = new Param("init", true);
+        // JOSM extension, not present in PROJ.4
+        public final static Param bounds = new Param("bounds", true);
 
         public final static Set<Param> params = new HashSet<Param>(Arrays.asList(
                 x_0, y_0, lon_0, k_0, ellps, a, es, rf, f, b, datum, towgs84,
-                nadgrids, proj, lat_0, lat_1, lat_2
+                nadgrids, proj, lat_0, lat_1, lat_2, wktext, units, no_defs, 
+                init, bounds
         ));
 
@@ -83,36 +91,14 @@
 
     public void update(String pref) throws ProjectionConfigurationException {
+        this.pref = pref;
         if (pref == null) {
-            this.pref = null;
             ellps = Ellipsoid.WGS84;
             datum = WGS84Datum.INSTANCE;
             proj = new org.openstreetmap.josm.data.projection.proj.Mercator();
+            bounds = new Bounds(
+                    new LatLon(-85.05112877980659, -180.0),
+                    new LatLon(85.05112877980659, 180.0));
         } else {
-            Map<String, String> parameters = new HashMap<String, String>();
-            String[] parts = pref.trim().split("\\s+");
-            if (pref.trim().isEmpty()) {
-                parts = new String[0];
-            }
-            for (int i = 0; i < parts.length; i++) {
-                String part = parts[i];
-                if (part.isEmpty() || part.charAt(0) != '+')
-                    throw new ProjectionConfigurationException(tr("Parameter must begin with a ''+'' sign (found ''{0}'')", part));
-                Matcher m = Pattern.compile("\\+([a-zA-Z0-9_]+)(=(.*))?").matcher(part);
-                if (m.matches()) {
-                    String key = m.group(1);
-                    String value = null;
-                    if (m.groupCount() >= 3) {
-                        value = m.group(3);
-                    }
-                    if (!Param.paramsByKey.containsKey(key))
-                        throw new ProjectionConfigurationException(tr("Unkown parameter: ''{0}''.", key));
-                    if (Param.paramsByKey.get(key).hasValue && value == null)
-                        throw new ProjectionConfigurationException(tr("Value expected for parameter ''{0}''.", key));
-                    if (!Param.paramsByKey.get(key).hasValue && value != null)
-                        throw new ProjectionConfigurationException(tr("No value expected for parameter ''{0}''.", key));
-                    parameters.put(key, value);
-                } else
-                    throw new ProjectionConfigurationException(tr("Unexpected parameter format (''{0}'')", part));
-            }
+            Map<String, String> parameters = parseParameterList(pref);
             ellps = parseEllipsoid(parameters);
             datum = parseDatum(parameters, ellps);
@@ -124,16 +110,76 @@
             s = parameters.get(Param.y_0.key);
             if (s != null) {
-                this.y_0 = parseDouble(s, Param.x_0.key);
+                this.y_0 = parseDouble(s, Param.y_0.key);
             }
             s = parameters.get(Param.lon_0.key);
             if (s != null) {
-                this.lon_0 = parseAngle(s, Param.x_0.key);
+                this.lon_0 = parseAngle(s, Param.lon_0.key);
             }
             s = parameters.get(Param.k_0.key);
             if (s != null) {
-                this.k_0 = parseDouble(s, Param.x_0.key);
-            }
-            this.pref = pref;
-        }
+                this.k_0 = parseDouble(s, Param.k_0.key);
+            }
+            s = parameters.get(Param.bounds.key);
+            if (s != null) {
+                this.bounds = parseBounds(s);
+            }
+        }
+    }
+
+    private Map<String, String> parseParameterList(String pref) throws ProjectionConfigurationException {
+        Map<String, String> parameters = new HashMap<String, String>();
+        String[] parts = pref.trim().split("\\s+");
+        if (pref.trim().isEmpty()) {
+            parts = new String[0];
+        }
+        for (int i = 0; i < parts.length; i++) {
+            String part = parts[i];
+            if (part.isEmpty() || part.charAt(0) != '+')
+                throw new ProjectionConfigurationException(tr("Parameter must begin with a ''+'' character (found ''{0}'')", part));
+            Matcher m = Pattern.compile("\\+([a-zA-Z0-9_]+)(=(.*))?").matcher(part);
+            if (m.matches()) {
+                String key = m.group(1);
+                // alias
+                if (key.equals("k")) {
+                    key = Param.k_0.key;
+                }
+                String value = null;
+                if (m.groupCount() >= 3) {
+                    value = m.group(3);
+                    // same aliases
+                    if (key.equals(Param.proj.key)) {
+                        if (value.equals("longlat") || value.equals("latlon") || value.equals("latlong")) {
+                            value = "lonlat";
+                        }
+                    }
+                }
+                if (!Param.paramsByKey.containsKey(key))
+                    throw new ProjectionConfigurationException(tr("Unkown parameter: ''{0}''.", key));
+                if (Param.paramsByKey.get(key).hasValue && value == null)
+                    throw new ProjectionConfigurationException(tr("Value expected for parameter ''{0}''.", key));
+                if (!Param.paramsByKey.get(key).hasValue && value != null)
+                    throw new ProjectionConfigurationException(tr("No value expected for parameter ''{0}''.", key));
+                parameters.put(key, value);
+            } else
+                throw new ProjectionConfigurationException(tr("Unexpected parameter format (''{0}'')", part));
+        }
+        // recursive resolution of +init includes
+        String initKey = parameters.get(Param.init.key);
+        if (initKey != null) {
+            String init = Projections.getInit(initKey);
+            if (init == null)
+                throw new ProjectionConfigurationException(tr("Value ''{0}'' for option +init not supported.", initKey));
+            Map<String, String> initp = null;
+            try {
+                initp = parseParameterList(init);
+            } catch (ProjectionConfigurationException ex) {
+                throw new ProjectionConfigurationException(tr(initKey+": "+ex.getMessage()));
+            }
+            for (Map.Entry<String, String> e : parameters.entrySet()) {
+                initp.put(e.getKey(), e.getValue());
+            }
+            return initp;
+        }
+        return parameters;
     }
 
@@ -174,4 +220,6 @@
                 parameters.containsKey(Param.b.key))
             throw new ProjectionConfigurationException(tr("Combination of ellipsoid parameters is not supported."));
+        if (parameters.containsKey(Param.no_defs.key))
+            throw new ProjectionConfigurationException(tr("Ellipsoid required (+ellps=* or +a=*, +b=*)"));
         // nothing specified, use WGS84 as default
         return Ellipsoid.WGS84;
@@ -181,5 +229,5 @@
         String nadgridsId = parameters.get(Param.nadgrids.key);
         if (nadgridsId != null) {
-            NTV2GridShiftFileWrapper nadgrids = Projections.getNadgrids(nadgridsId);
+            NTV2GridShiftFileWrapper nadgrids = Projections.getNTV2Grid(nadgridsId);
             if (nadgrids == null)
                 throw new ProjectionConfigurationException(tr("Grid shift file ''{0}'' for option +nadgrids not supported.", nadgridsId));
@@ -196,6 +244,8 @@
             if (datum == null) throw new ProjectionConfigurationException(tr("Unkown datum identifier: ''{0}''", datumId));
             return datum;
-        } else
-            return new CentricDatum(null, null, ellps);
+        }
+        if (parameters.containsKey(Param.no_defs.key))
+            throw new ProjectionConfigurationException(tr("Datum required (+datum=*, +towgs84=* or +nadgirds=*)"));
+        return new CentricDatum(null, null, ellps);
     }
 
@@ -267,5 +317,14 @@
         proj.initialize(projParams);
         return proj;
-
+    }
+
+    public Bounds parseBounds(String boundsStr) throws ProjectionConfigurationException {
+        String[] numStr = boundsStr.split(",");
+        if (numStr.length != 4)
+            throw new ProjectionConfigurationException(tr("Unexpected number of arguments for parameter ''+bounds'' (must be 4)"));
+        return new Bounds(parseAngle(numStr[1], "minlat (+bounds)"),
+                parseAngle(numStr[0], "minlon (+bounds)"),
+                parseAngle(numStr[3], "maxlat (+bounds)"),
+                parseAngle(numStr[2], "maxlon (+bounds)"));
     }
 
@@ -374,7 +433,8 @@
     @Override
     public Bounds getWorldBoundsLatLon() {
+        if (bounds != null) return bounds;
         return new Bounds(
-            new LatLon(-85.05112877980659, -180.0),
-            new LatLon(85.05112877980659, 180.0)); // FIXME
+            new LatLon(-90.0, -180.0),
+            new LatLon(90.0, 180.0));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5228)
@@ -10,6 +10,8 @@
 import java.awt.event.ActionListener;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -20,8 +22,12 @@
 import javax.swing.JPanel;
 import javax.swing.JTextField;
-
+import javax.swing.plaf.basic.BasicComboBoxEditor;
+
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.preferences.projection.SubPrefsOptions;
+import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem;
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.tools.GBC;
@@ -33,5 +39,5 @@
     @Override
     public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JPanel inner = new PreferencePanel();
+        JPanel inner = new PreferencePanel(listener);
         p.setLayout(new GridBagLayout());
         p.add(inner, GBC.std().fill(GBC.HORIZONTAL));
@@ -40,12 +46,29 @@
     private class PreferencePanel extends JPanel {
 
-        public final JTextField input;
-
-        public PreferencePanel() {
-            input = new JTextField(pref == null ? "" : pref, 30);
-            build();
-        }
-
-        private void build() {
+        public JTextField input;
+        private HistoryComboBox cbInput;
+
+        public PreferencePanel(ActionListener listener) {
+            build(listener);
+        }
+
+        private void build(final ActionListener listener) {
+            input = new JTextField(30);
+            cbInput = new HistoryComboBox();
+            cbInput.setPrototypeDisplayValue(new AutoCompletionListItem("xxxx"));
+            cbInput.setEditor(new BasicComboBoxEditor() {
+                @Override
+                protected JTextField createEditorComponent() {
+                    return input;
+                }
+            });
+            Collection<String> samples = Arrays.asList(
+                    "+proj=lonlat +ellps=WGS84 +datum=WGS84 +bounds=-180,-90,180,90",
+                    "+proj=tmerc +lat_0=0 +lon_0=9 +k_0=1 +x_0=3500000 +y_0=0 +ellps=bessel +nadgrids=BETA2007.gsb");
+            List<String> inputHistory = new LinkedList<String>(Main.pref.getCollection("projection.custom.value.history", samples));
+            Collections.reverse(inputHistory);
+            cbInput.setPossibleItems(inputHistory);
+            cbInput.setText(pref == null ? "" : pref);
+
             final HtmlPanel errorsPanel = new HtmlPanel();
             errorsPanel.setVisible(false);
@@ -64,4 +87,5 @@
                         feedbackValid(tr("Projection configuration is valid."));
                     }
+                    listener.actionPerformed(null);
                 }
 
@@ -109,5 +133,5 @@
             this.setLayout(new GridBagLayout());
             JPanel p2 = new JPanel(new GridBagLayout());
-            p2.add(input, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5));
+            p2.add(cbInput, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5));
             p2.add(btnCheck, GBC.eol().insets(0, 20, 0, 5));
             this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
@@ -120,4 +144,9 @@
             p2.add(GBC.glue(1, 0), GBC.eol().fill(GBC.HORIZONTAL));
             this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+        }
+
+        public void rememberHistory() {
+            cbInput.addCurrentItemToHistory();
+            Main.pref.putCollection("projection.custom.value.history", cbInput.getHistory());
         }
     }
@@ -150,4 +179,5 @@
             s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Build-in:")+" ");
             s.append(listKeys(Projections.nadgrids)+"<br>");
+            s.append("<b>+bounds=</b>minlon,minlat,maxlon,maxlat - <i>"+tr("Projection bounds (in degrees)")+"</i><br>");
 
             HtmlPanel info = new HtmlPanel(s.toString());
@@ -166,4 +196,5 @@
         PreferencePanel prefPanel = (PreferencePanel) p.getComponent(0);
         String pref = prefPanel.input.getText();
+        prefPanel.rememberHistory();
         return Collections.singleton(pref);
     }
Index: trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5228)
@@ -2,8 +2,14 @@
 package org.openstreetmap.josm.data.projection;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.openstreetmap.josm.Main;
@@ -20,4 +26,5 @@
 import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
+import org.openstreetmap.josm.io.MirroredInputStream;
 
 /**
@@ -80,12 +87,13 @@
      * should be compatible to PROJ.4
      */
+    public static Map<String, ProjFactory> projs = new HashMap<String, ProjFactory>();
     public static Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>();
-    public static Map<String, ProjFactory> projs = new HashMap<String, ProjFactory>();
     public static Map<String, Datum> datums = new HashMap<String, Datum>();
     public static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
+    public static Map<String, String> inits = new HashMap<String, String>();
 
     static {
         registerBaseProjection("lonlat", LonLat.class, "core");
-        registerBaseProjection("merc", org.openstreetmap.josm.data.projection.proj.Mercator.class, "core");
+        registerBaseProjection("josm:smerc", org.openstreetmap.josm.data.projection.proj.Mercator.class, "core");
         registerBaseProjection("lcc", LambertConformalConic.class, "core");
         registerBaseProjection("somerc", SwissObliqueMercator.class, "core");
@@ -101,4 +109,6 @@
         nadgrids.put("BETA2007.gsb", NTV2GridShiftFileWrapper.BETA2007);
         nadgrids.put("ntf_r93_b.gsb", NTV2GridShiftFileWrapper.ntf_rgf93);
+
+        loadInits();
     }
 
@@ -134,6 +144,35 @@
     }
 
-    public static NTV2GridShiftFileWrapper getNadgrids(String id) {
+    public static NTV2GridShiftFileWrapper getNTV2Grid(String id) {
         return nadgrids.get(id);
     }
+
+    public static String getInit(String id) {
+        return inits.get(id);
+    }
+
+    /**
+     * Load +init "presets" from file
+     */
+    private static void loadInits() {
+        Pattern epsgPattern = Pattern.compile("\\A<(\\d+)>(.*)<>\\Z");
+        try {
+            InputStream in = new MirroredInputStream("resource://data/epsg");
+            BufferedReader r = new BufferedReader(new InputStreamReader(in));
+            String line;
+            while ((line = r.readLine()) != null) {
+                line = line.trim();
+                if (!line.startsWith("#")) {
+                    Matcher m = epsgPattern.matcher(line);
+                    if (m.matches()) {
+                        inits.put("epsg:" + m.group(1), m.group(2).trim());
+                    } else {
+                        System.err.println("Warning: failed to parse line from the epsg projection definition: "+line);
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/proj/Mercator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/proj/Mercator.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/proj/Mercator.java	(revision 5228)
@@ -20,5 +20,5 @@
     @Override
     public String getProj4Id() {
-        return "merc";
+        return null; // "merc" is ellipsoidal Mercator projection in PROJ.4
     }
 
