Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 5226)
@@ -69,6 +69,6 @@
 import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
 import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
-import org.openstreetmap.josm.gui.preferences.map.ProjectionPreference;
 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitorExecutor;
Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5226)
@@ -4,9 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -14,6 +10,4 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -21,6 +15,9 @@
 import org.openstreetmap.josm.data.projection.datum.CentricDatum;
 import org.openstreetmap.josm.data.projection.datum.Datum;
+import org.openstreetmap.josm.data.projection.datum.NTV2Datum;
+import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.datum.SevenParameterDatum;
 import org.openstreetmap.josm.data.projection.datum.ThreeParameterDatum;
+import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
 import org.openstreetmap.josm.data.projection.proj.Proj;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
@@ -32,15 +29,25 @@
  * Inspired by PROJ.4 and Proj4J.
  */
-public class CustomProjection extends AbstractProjection implements ProjectionSubPrefs {
-
-    private String pref = "";
-
-    public void update(String pref) {
-        try {
+public class CustomProjection extends AbstractProjection {
+
+    /**
+     * pref String that defines the projection
+     *
+     * null means fall back mode (Mercator)
+     */
+    protected String pref = null;
+
+    public void update(String pref) throws ProjectionConfigurationException {
+        if (pref == null) {
+            this.pref = null;
+            ellps = Ellipsoid.WGS84;
+            datum = WGS84Datum.INSTANCE;
+            proj = new org.openstreetmap.josm.data.projection.proj.Mercator();
+        } else {
             Map<String, String> parameters = new HashMap<String, String>();
             String[] parts = pref.trim().split("\\s+");
             for (int i = 0; i < parts.length; i++) {
                 String part = parts[i];
-                if (part.charAt(0) != '+')
+                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);
@@ -74,8 +81,6 @@
                 this.k_0 = parseDouble(s, "k_0");
             }
-        } catch (ProjectionConfigurationException e) {
-            System.err.println(e.toString()); // FIXME
-        }
-        this.pref = pref;
+            this.pref = pref;
+        }
     }
 
@@ -121,4 +126,12 @@
 
     public Datum parseDatum(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
+        String nadgridsId = parameters.get("nadgrids");
+        if (nadgridsId != null) {
+            NTV2GridShiftFileWrapper nadgrids = Projections.getNadgrids(nadgridsId);
+            if (nadgrids == null)
+                throw new ProjectionConfigurationException(tr("Grid shift file ''{0}'' for option +nadgrids not supported.", nadgridsId));
+            return new NTV2Datum(nadgridsId, null, ellps, nadgrids);
+        }
+
         String towgs84 = parameters.get("towgs84");
         if (towgs84 != null)
@@ -298,10 +311,10 @@
     @Override
     public String toCode() {
-        return Utils.md5Hex(pref).substring(0, 10);
+        return "proj:" + (pref == null ? "ERROR" : pref);
     }
 
     @Override
     public String getCacheDirectoryName() {
-        return toCode();
+        return "proj-"+Utils.md5Hex(pref == null ? "" : pref).substring(0, 4);
     }
 
@@ -317,35 +330,3 @@
         return tr("Custom Projection (from PROJ.4 string)");
     }
-
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JTextField input = new JTextField(pref, 50);
-        p.setLayout(new GridBagLayout());
-        p.add(input);
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefTf = p.getComponent(0);
-        if (!(prefTf instanceof JTextField))
-            return null;
-        String pref = ((JTextField) prefTf).getText();
-        return Collections.singleton(pref);
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        update(args.iterator().next());
-    }
-
-    @Override
-    public String[] allCodes() {
-        return new String[0];
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-        return null;
-    }
-
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5226)
@@ -0,0 +1,128 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.gui.preferences.projection.SubPrefsOptions;
+import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class CustomProjectionPrefGui extends CustomProjection implements ProjectionSubPrefs, SubPrefsOptions {
+
+    @Override
+    public void setupPreferencePanel(JPanel p, ActionListener listener) {
+        final JTextField input = new JTextField(pref == null ? "" : pref, 30);
+        final HtmlPanel errorsPanel = new HtmlPanel();
+        errorsPanel.setVisible(false);
+        final JLabel valStatus = new JLabel();
+        valStatus.setVisible(false);
+
+        final AbstractTextComponentValidator val = new AbstractTextComponentValidator(input, false, false, false) {
+
+            private String error;
+
+            @Override
+            public void validate() {
+                if (!isValid()) {
+                    feedbackInvalid(tr("Invalid projection configuration: {0}",error));
+                } else {
+                    feedbackValid(tr("Projection configuration is valid."));
+                }
+            }
+
+            @Override
+            public boolean isValid() {
+                try {
+                    CustomProjection test = new CustomProjection();
+                    test.update(input.getText());
+                } catch (ProjectionConfigurationException ex) {
+                    error = ex.getMessage();
+                    valStatus.setIcon(ImageProvider.get("data", "error.png"));
+                    valStatus.setVisible(true);
+                    errorsPanel.setText(error);
+                    errorsPanel.setVisible(true);
+                    return false;
+                }
+                errorsPanel.setVisible(false);
+                valStatus.setIcon(ImageProvider.get("misc", "green_check.png"));
+                valStatus.setVisible(true);
+                return true;
+            }
+
+        };
+
+        JButton btnCheck = new JButton(tr("Validate"));
+        btnCheck.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                val.validate();
+            }
+        });
+        btnCheck.setLayout(new BorderLayout());
+        btnCheck.setMargin(new Insets(-1,0,-1,0));
+
+        p.setLayout(new GridBagLayout());
+        p.add(input, GBC.std().fill(GBC.HORIZONTAL).insets(0, 0, 5, 5));
+        p.add(btnCheck, GBC.eol());
+        JPanel p2 = new JPanel(new GridBagLayout());
+        p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0));
+        p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL));
+        p.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+    }
+
+    @Override
+    public Collection<String> getPreferences(JPanel p) {
+        Object prefTf = p.getComponent(0);
+        if (!(prefTf instanceof JTextField))
+            return null;
+        String pref = ((JTextField) prefTf).getText();
+        return Collections.singleton(pref);
+    }
+
+    @Override
+    public void setPreferences(Collection<String> args) {
+        try {
+            update(args.iterator().next());
+        } catch (ProjectionConfigurationException ex) {
+            System.err.println("Error: Parsing of custom projection failed, falling back to Mercator. Error message is: "+ex.getMessage());
+            try {
+                update(null);
+            } catch (ProjectionConfigurationException ex1) {
+                throw new RuntimeException(ex1);
+            }
+        }
+    }
+
+    @Override
+    public String[] allCodes() {
+        return new String[0];
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        return null;
+    }
+
+    @Override
+    public boolean showProjectionCode() {
+        return false;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/GaussKrueger.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/GaussKrueger.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/data/projection/GaussKrueger.java	(revision 5226)
@@ -6,5 +6,4 @@
 import java.awt.GridBagLayout;
 import java.awt.event.ActionListener;
-import java.io.InputStream;
 import java.util.Collection;
 import java.util.Collections;
@@ -14,9 +13,8 @@
 import javax.swing.JPanel;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.projection.datum.NTV2Datum;
-import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFile;
+import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
@@ -35,6 +33,4 @@
     };
 
-    private static NTV2GridShiftFile BETA2007 = null;
-
     private static String[] zones = { "2", "3", "4", "5" };
 
@@ -44,16 +40,4 @@
 
     public GaussKrueger(int zone) {
-        if (BETA2007 == null) {
-            try {
-                String gridFileName = "BETA2007.gsb";
-                InputStream is = Main.class.getResourceAsStream("/data/"+gridFileName);
-                if (is == null)
-                    throw new RuntimeException(tr("Error: failed to open input stream for resource ''/data/{0}''.", gridFileName));
-                BETA2007 = new NTV2GridShiftFile();
-                BETA2007.loadGridShiftFile(is, false);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
         updateParameters(zone);
     }
@@ -62,5 +46,5 @@
         this.zone = zone;
         ellps = Ellipsoid.Bessel1841;
-        datum = new NTV2Datum("BETA2007", null, ellps, BETA2007);
+        datum = new NTV2Datum("BETA2007", null, ellps, NTV2GridShiftFileWrapper.BETA2007);
         ////less acurrate datum (errors up to 3m):
         //datum = new SevenParameterDatum(
Index: trunk/src/org/openstreetmap/josm/data/projection/Lambert.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 5226)
@@ -19,4 +19,5 @@
 import org.openstreetmap.josm.data.projection.datum.NTV2Datum;
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFile;
+import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
@@ -77,24 +78,5 @@
     private int layoutZone;
 
-    private static NTV2GridShiftFile ntf_rgf93Grid = null;
-
-    public static NTV2GridShiftFile getNtf_rgf93Grid() {
-        return ntf_rgf93Grid;
-    }
-
     public Lambert() {
-        if (ntf_rgf93Grid == null) {
-            try {
-                String gridFileName = "ntf_r93_b.gsb";
-                InputStream is = Main.class.getResourceAsStream("/data/"+gridFileName);
-                if (is == null) {
-                    throw new RuntimeException(tr("Error: failed to open input stream for resource ''/data/{0}''. Cannot load NTF<->RGF93 grid", gridFileName));
-                }
-                ntf_rgf93Grid = new NTV2GridShiftFile();
-                ntf_rgf93Grid.loadGridShiftFile(is, false);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
         updateParameters(DEFAULT_ZONE);
     }
@@ -103,5 +85,5 @@
         this.layoutZone = layoutZone;
         ellps = Ellipsoid.clarkeIGN;
-        datum = new NTV2Datum("ntf_rgf93Grid", null, ellps, ntf_rgf93Grid);
+        datum = new NTV2Datum("ntf_rgf93Grid", null, ellps, NTV2GridShiftFileWrapper.ntf_rgf93);
         x_0 = x_0s[layoutZone];
         lon_0 = 2.0 + 20.0 / 60 + 14.025 / 3600; // 0 grade Paris
Index: trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5226)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.projection.datum.Datum;
+import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
@@ -16,5 +17,4 @@
 import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
 
 /**
@@ -49,5 +49,5 @@
     static {
         if (Main.pref.getBoolean("customprojection")) {
-            addProjection(new CustomProjection());
+            addProjection(new CustomProjectionPrefGui());
         }
     }
@@ -86,4 +86,5 @@
     private static Map<String, Class<? extends Proj>> projs = new HashMap<String, Class<? extends Proj>>();
     private static Map<String, Datum> datums = new HashMap<String, Datum>();
+    private static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
 
     static {
@@ -99,4 +100,7 @@
 
         datums.put("WGS84", WGS84Datum.INSTANCE);
+
+        nadgrids.put("BETA2007.gsb", NTV2GridShiftFileWrapper.BETA2007);
+        nadgrids.put("ntf_r93_b.gsb", NTV2GridShiftFileWrapper.ntf_rgf93);
     }
 
@@ -122,3 +126,7 @@
         return datums.get(id);
     }
+
+    public static NTV2GridShiftFileWrapper getNadgrids(String id) {
+        return nadgrids.get(id);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2Datum.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2Datum.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2Datum.java	(revision 5226)
@@ -10,7 +10,7 @@
 public class NTV2Datum extends AbstractDatum {
 
-    protected NTV2GridShiftFile nadgrids;
+    protected NTV2GridShiftFileWrapper nadgrids;
 
-    public NTV2Datum(String name, String proj4Id, Ellipsoid ellps, NTV2GridShiftFile nadgrids) {
+    public NTV2Datum(String name, String proj4Id, Ellipsoid ellps, NTV2GridShiftFileWrapper nadgrids) {
         super(name, proj4Id, ellps);
         this.nadgrids = nadgrids;
@@ -20,5 +20,5 @@
     public LatLon toWGS84(LatLon ll) {
         NTV2GridShift gs = new NTV2GridShift(ll);
-        nadgrids.gridShiftForward(gs);
+        nadgrids.getShiftFile().gridShiftForward(gs);
         return new LatLon(ll.lat() + gs.getLatShiftDegrees(), ll.lon() + gs.getLonShiftPositiveEastDegrees());
     }
@@ -27,5 +27,5 @@
     public LatLon fromWGS84(LatLon ll) {
         NTV2GridShift gs = new NTV2GridShift(ll);
-        nadgrids.gridShiftReverse(gs);
+        nadgrids.getShiftFile().gridShiftReverse(gs);
         return new LatLon(ll.lat() + gs.getLatShiftDegrees(), ll.lon() + gs.getLonShiftPositiveEastDegrees());
     }
Index: trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2GridShiftFileWrapper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2GridShiftFileWrapper.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2GridShiftFileWrapper.java	(revision 5226)
@@ -0,0 +1,44 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection.datum;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.io.MirroredInputStream;
+
+/**
+ * Wrapper for NTV2GridShiftFile.
+ *
+ * Loads the shift file from disk, when it is first accessed.
+ */
+public class NTV2GridShiftFileWrapper {
+
+    public final static NTV2GridShiftFileWrapper BETA2007 = new NTV2GridShiftFileWrapper("resource://data/BETA2007.gsb");
+    public final static NTV2GridShiftFileWrapper ntf_rgf93 = new NTV2GridShiftFileWrapper("resource://data/ntf_r93_b.gsb");
+    
+
+    private NTV2GridShiftFile instance = null;
+    private String gridFileName;
+
+    public NTV2GridShiftFileWrapper(String filename) {
+        this.gridFileName = filename;
+    }
+
+    public NTV2GridShiftFile getShiftFile() {
+        if (instance == null) {
+            try {
+                InputStream is = new MirroredInputStream(gridFileName);
+                if (is == null)
+                    throw new RuntimeException(tr("Error: failed to open input stream for resource ''/data/{0}''.", gridFileName));
+                instance = new NTV2GridShiftFile();
+                instance.loadGridShiftFile(is, false);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return instance;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 5226)
@@ -43,5 +43,5 @@
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.help.Helpful;
-import org.openstreetmap.josm.gui.preferences.map.ProjectionPreference;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.tools.Predicate;
 
Index: trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java	(revision 5226)
@@ -39,6 +39,6 @@
 import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
 import org.openstreetmap.josm.gui.preferences.map.MapPreference;
-import org.openstreetmap.josm.gui.preferences.map.ProjectionPreference;
 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.gui.preferences.shortcut.ShortcutPreference;
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/map/ProjectionPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/map/ProjectionPreference.java	(revision 5225)
+++ 	(revision )
@@ -1,305 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.gui.preferences.map;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.Collection;
-
-import javax.swing.BorderFactory;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JSeparator;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.CoordinateFormat;
-import org.openstreetmap.josm.data.preferences.CollectionProperty;
-import org.openstreetmap.josm.data.preferences.ParametrizedCollectionProperty;
-import org.openstreetmap.josm.data.preferences.StringProperty;
-import org.openstreetmap.josm.data.projection.Mercator;
-import org.openstreetmap.josm.data.projection.Projection;
-import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
-import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
-import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
-import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
-import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
-import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
-import org.openstreetmap.josm.plugins.PluginHandler;
-import org.openstreetmap.josm.tools.GBC;
-
-public class ProjectionPreference implements SubPreferenceSetting {
-
-    public static class Factory implements PreferenceSettingFactory {
-        public PreferenceSetting createPreferenceSetting() {
-            return new ProjectionPreference();
-        }
-    }
-
-    private static final StringProperty PROP_PROJECTION = new StringProperty("projection", Mercator.class.getName());
-    private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
-    private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
-    private static final ParametrizedCollectionProperty PROP_PROJECTION_SUBPROJECTION = new ParametrizedCollectionProperty(null) {
-        @Override
-        protected String getKey(String... params) {
-            String name = params[0];
-            String sname = name.substring(name.lastIndexOf(".")+1);
-            return "projection.sub."+sname;
-        }
-    };
-    public static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric");
-    private static final String[] unitsValues = (new ArrayList<String>(NavigatableComponent.SYSTEMS_OF_MEASUREMENT.keySet())).toArray(new String[0]);
-    private static final String[] unitsValuesTr = new String[unitsValues.length];
-    static {
-        for (int i=0; i<unitsValues.length; ++i) {
-            unitsValuesTr[i] = tr(unitsValues[i]);
-        }
-    }
-
-    /**
-     * Combobox with all projections available
-     */
-    private JComboBox projectionCombo = new JComboBox(Projections.getProjections().toArray());
-
-    /**
-     * Combobox with all coordinate display possibilities
-     */
-    private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values());
-
-    private JComboBox unitsCombo = new JComboBox(unitsValuesTr);
-
-    /**
-     * This variable holds the JPanel with the projection's preferences. If the
-     * selected projection does not implement this, it will be set to an empty
-     * Panel.
-     */
-    private JPanel projSubPrefPanel;
-    private JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout());
-
-    private JLabel projectionCode = new JLabel();
-    private JLabel bounds = new JLabel();
-
-    /**
-     * This is the panel holding all projection preferences
-     */
-    private JPanel projPanel = new JPanel(new GridBagLayout());
-
-    /**
-     * The GridBagConstraints for the Panel containing the ProjectionSubPrefs.
-     * This is required twice in the code, creating it here keeps both occurrences
-     * in sync
-     */
-    static private GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0);
-
-    public void addGui(PreferenceTabbedPane gui) {
-        setupProjectionCombo();
-
-        for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
-            if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(PROP_COORDINATES.get())) {
-                coordinatesCombo.setSelectedIndex(i);
-                break;
-            }
-        }
-
-        for (int i = 0; i < unitsValues.length; ++i) {
-            if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) {
-                unitsCombo.setSelectedIndex(i);
-                break;
-            }
-        }
-
-        projPanel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
-        projPanel.setLayout(new GridBagLayout());
-        projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
-        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
-        projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
-        projPanel.add(new JLabel(tr("Projection code")), GBC.std().insets(25,5,0,5));
-        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
-        projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
-        projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25,5,0,5));
-        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
-        projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
-        projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
-        projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20,5,5,5));
-
-        projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0,5,0,10));
-        projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
-        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
-        projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
-        projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5,5,0,5));
-        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
-        projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
-        projPanel.add(GBC.glue(1,1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0));
-
-        JScrollPane scrollpane = new JScrollPane(projPanel);
-        gui.getMapPreference().mapcontent.addTab(tr("Map Projection"), scrollpane);
-
-        updateMeta(Main.getProjection());
-    }
-
-    private void updateMeta(Projection proj)
-    {
-        projectionCode.setText(proj.toCode());
-        Bounds b = proj.getWorldBoundsLatLon();
-        CoordinateFormat cf = CoordinateFormat.getDefaultFormat();
-        bounds.setText(b.getMin().latToString(cf)+"; "+b.getMin().lonToString(cf)+" : "+b.getMax().latToString(cf)+"; "+b.getMax().lonToString(cf));
-    }
-
-    public boolean ok() {
-        Projection proj = (Projection) projectionCombo.getSelectedItem();
-
-        String projname = proj.getClass().getName();
-        Collection<String> prefs = null;
-        if(proj instanceof ProjectionSubPrefs) {
-            prefs = ((ProjectionSubPrefs) proj).getPreferences(projSubPrefPanel);
-        }
-
-        PROP_PROJECTION.put(projname);
-        setProjection(projname, prefs);
-
-        if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) {
-            CoordinateFormat.setCoordinateFormat((CoordinateFormat)coordinatesCombo.getSelectedItem());
-        }
-
-        int i = unitsCombo.getSelectedIndex();
-        PROP_SYSTEM_OF_MEASUREMENT.put(unitsValues[i]);
-
-        return false;
-    }
-
-    static public void setProjection()
-    {
-        setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get());
-    }
-
-    static public void setProjection(String name, Collection<String> coll)
-    {
-        Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
-
-        Projection proj = null;
-        for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
-            try {
-                proj = (Projection) Class.forName(name, true, cl).newInstance();
-            } catch (final Exception e) {
-            }
-            if (proj != null) {
-                break;
-            }
-        }
-        if (proj == null) {
-            JOptionPane.showMessageDialog(
-                    Main.parent,
-                    tr("The projection {0} could not be activated. Using Mercator", name),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-            coll = null;
-            proj = new Mercator();
-            name = proj.getClass().getName();
-            PROP_PROJECTION.put(name);
-        }
-        PROP_SUB_PROJECTION.put(coll);
-        PROP_PROJECTION_SUBPROJECTION.put(coll, name);
-        if (proj instanceof ProjectionSubPrefs) {
-            ((ProjectionSubPrefs) proj).setPreferences(coll);
-        }
-        Projection oldProj = Main.getProjection();
-        Main.setProjection(proj);
-        if (b != null && (!proj.getClass().getName().equals(oldProj.getClass().getName()) || proj.hashCode() != oldProj.hashCode()))
-        {
-            Main.map.mapView.zoomTo(b);
-            /* TODO - remove layers with fixed projection */
-        }
-    }
-
-    private class SBPanel extends JPanel implements ActionListener
-    {
-        private ProjectionSubPrefs p;
-        public SBPanel(ProjectionSubPrefs pr)
-        {
-            super();
-            p = pr;
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            p.setPreferences(p.getPreferences(this));
-            updateMeta(p);
-        }
-    }
-
-    /**
-     * Handles all the work related to update the projection-specific
-     * preferences
-     * @param proj
-     */
-    private void selectedProjectionChanged(Projection proj) {
-        if(!(proj instanceof ProjectionSubPrefs)) {
-            projSubPrefPanel = new JPanel();
-        } else {
-            ProjectionSubPrefs projPref = (ProjectionSubPrefs) proj;
-            projSubPrefPanel = new SBPanel(projPref);
-            projPref.setupPreferencePanel(projSubPrefPanel, (SBPanel)projSubPrefPanel);
-        }
-
-        // Don't try to update if we're still starting up
-        int size = projPanel.getComponentCount();
-        if(size < 1)
-            return;
-
-        // Replace old panel with new one
-        projSubPrefPanelWrapper.removeAll();
-        projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
-        projPanel.revalidate();
-        projSubPrefPanel.repaint();
-        updateMeta(proj);
-    }
-
-    /**
-     * Sets up projection combobox with default values and action listener
-     */
-    private void setupProjectionCombo() {
-        boolean found = false;
-        for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
-            Projection proj = (Projection)projectionCombo.getItemAt(i);
-            String name = proj.getClass().getName();
-            if(proj instanceof ProjectionSubPrefs) {
-                ((ProjectionSubPrefs) proj).setPreferences(PROP_PROJECTION_SUBPROJECTION.get(name));
-            }
-            if (name.equals(PROP_PROJECTION.get())) {
-                projectionCombo.setSelectedIndex(i);
-                selectedProjectionChanged(proj);
-                found = true;
-                break;
-            }
-        }
-        if (!found)
-            throw new RuntimeException("Couldn't find the current projection in the list of available projections!");
-
-        projectionCombo.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                JComboBox cb = (JComboBox)e.getSource();
-                Projection proj = (Projection)cb.getSelectedItem();
-                selectedProjectionChanged(proj);
-            }
-        });
-    }
-
-    @Override
-    public boolean isExpert() {
-        return false;
-    }
-
-    @Override
-    public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
-        return gui.getMapPreference();
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 5226)
@@ -0,0 +1,312 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
+import org.openstreetmap.josm.data.preferences.CollectionProperty;
+import org.openstreetmap.josm.data.preferences.ParametrizedCollectionProperty;
+import org.openstreetmap.josm.data.preferences.StringProperty;
+import org.openstreetmap.josm.data.projection.Mercator;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
+import org.openstreetmap.josm.data.projection.Projections;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
+import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
+import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
+import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.tools.GBC;
+
+public class ProjectionPreference implements SubPreferenceSetting {
+
+    public static class Factory implements PreferenceSettingFactory {
+        public PreferenceSetting createPreferenceSetting() {
+            return new ProjectionPreference();
+        }
+    }
+
+    private static final StringProperty PROP_PROJECTION = new StringProperty("projection", Mercator.class.getName());
+    private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
+    private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
+    private static final ParametrizedCollectionProperty PROP_PROJECTION_SUBPROJECTION = new ParametrizedCollectionProperty(null) {
+        @Override
+        protected String getKey(String... params) {
+            String name = params[0];
+            String sname = name.substring(name.lastIndexOf(".")+1);
+            return "projection.sub."+sname;
+        }
+    };
+    public static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric");
+    private static final String[] unitsValues = (new ArrayList<String>(NavigatableComponent.SYSTEMS_OF_MEASUREMENT.keySet())).toArray(new String[0]);
+    private static final String[] unitsValuesTr = new String[unitsValues.length];
+    static {
+        for (int i=0; i<unitsValues.length; ++i) {
+            unitsValuesTr[i] = tr(unitsValues[i]);
+        }
+    }
+
+    /**
+     * Combobox with all projections available
+     */
+    private JComboBox projectionCombo = new JComboBox(Projections.getProjections().toArray());
+
+    /**
+     * Combobox with all coordinate display possibilities
+     */
+    private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values());
+
+    private JComboBox unitsCombo = new JComboBox(unitsValuesTr);
+
+    /**
+     * This variable holds the JPanel with the projection's preferences. If the
+     * selected projection does not implement this, it will be set to an empty
+     * Panel.
+     */
+    private JPanel projSubPrefPanel;
+    private JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout());
+
+    private JLabel projectionCodeLabel;
+    private Component projectionCodeGlue;
+    private JLabel projectionCode = new JLabel();
+    private JLabel bounds = new JLabel();
+
+    /**
+     * This is the panel holding all projection preferences
+     */
+    private JPanel projPanel = new JPanel(new GridBagLayout());
+
+    /**
+     * The GridBagConstraints for the Panel containing the ProjectionSubPrefs.
+     * This is required twice in the code, creating it here keeps both occurrences
+     * in sync
+     */
+    static private GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0);
+
+    public void addGui(PreferenceTabbedPane gui) {
+        setupProjectionCombo();
+
+        for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
+            if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(PROP_COORDINATES.get())) {
+                coordinatesCombo.setSelectedIndex(i);
+                break;
+            }
+        }
+
+        for (int i = 0; i < unitsValues.length; ++i) {
+            if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) {
+                unitsCombo.setSelectedIndex(i);
+                break;
+            }
+        }
+
+        projPanel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
+        projPanel.setLayout(new GridBagLayout());
+        projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
+        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+        projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
+        projPanel.add(projectionCodeLabel = new JLabel(tr("Projection code")), GBC.std().insets(25,5,0,5));
+        projPanel.add(projectionCodeGlue = GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+        projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
+        projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25,5,0,5));
+        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+        projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
+        projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
+        projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20,5,5,5));
+
+        projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0,5,0,10));
+        projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
+        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+        projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
+        projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5,5,0,5));
+        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+        projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
+        projPanel.add(GBC.glue(1,1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0));
+
+        JScrollPane scrollpane = new JScrollPane(projPanel);
+        gui.getMapPreference().mapcontent.addTab(tr("Map Projection"), scrollpane);
+
+        updateMeta(Main.getProjection());
+    }
+
+    private void updateMeta(Projection proj)
+    {
+        projectionCode.setText(proj.toCode());
+        Bounds b = proj.getWorldBoundsLatLon();
+        CoordinateFormat cf = CoordinateFormat.getDefaultFormat();
+        bounds.setText(b.getMin().latToString(cf)+"; "+b.getMin().lonToString(cf)+" : "+b.getMax().latToString(cf)+"; "+b.getMax().lonToString(cf));
+        boolean hideCode = proj instanceof SubPrefsOptions && !((SubPrefsOptions) proj).showProjectionCode();
+        projectionCodeLabel.setVisible(!hideCode);
+        projectionCodeGlue.setVisible(!hideCode);
+        projectionCode.setVisible(!hideCode);
+    }
+
+    public boolean ok() {
+        Projection proj = (Projection) projectionCombo.getSelectedItem();
+
+        String projname = proj.getClass().getName();
+        Collection<String> prefs = null;
+        if(proj instanceof ProjectionSubPrefs) {
+            prefs = ((ProjectionSubPrefs) proj).getPreferences(projSubPrefPanel);
+        }
+
+        PROP_PROJECTION.put(projname);
+        setProjection(projname, prefs);
+
+        if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) {
+            CoordinateFormat.setCoordinateFormat((CoordinateFormat)coordinatesCombo.getSelectedItem());
+        }
+
+        int i = unitsCombo.getSelectedIndex();
+        PROP_SYSTEM_OF_MEASUREMENT.put(unitsValues[i]);
+
+        return false;
+    }
+
+    static public void setProjection()
+    {
+        setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get());
+    }
+
+    static public void setProjection(String name, Collection<String> coll)
+    {
+        Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
+
+        Projection proj = null;
+        for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
+            try {
+                proj = (Projection) Class.forName(name, true, cl).newInstance();
+            } catch (final Exception e) {
+            }
+            if (proj != null) {
+                break;
+            }
+        }
+        if (proj == null) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("The projection {0} could not be activated. Using Mercator", name),
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE
+            );
+            coll = null;
+            proj = new Mercator();
+            name = proj.getClass().getName();
+            PROP_PROJECTION.put(name);
+        }
+        PROP_SUB_PROJECTION.put(coll);
+        PROP_PROJECTION_SUBPROJECTION.put(coll, name);
+        if (proj instanceof ProjectionSubPrefs) {
+            ((ProjectionSubPrefs) proj).setPreferences(coll);
+        }
+        Projection oldProj = Main.getProjection();
+        Main.setProjection(proj);
+        if (b != null && (!proj.getClass().getName().equals(oldProj.getClass().getName()) || proj.hashCode() != oldProj.hashCode()))
+        {
+            Main.map.mapView.zoomTo(b);
+            /* TODO - remove layers with fixed projection */
+        }
+    }
+
+    private class SBPanel extends JPanel implements ActionListener
+    {
+        private ProjectionSubPrefs p;
+        public SBPanel(ProjectionSubPrefs pr)
+        {
+            super();
+            p = pr;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            p.setPreferences(p.getPreferences(this));
+            updateMeta(p);
+        }
+    }
+
+    /**
+     * Handles all the work related to update the projection-specific
+     * preferences
+     * @param proj
+     */
+    private void selectedProjectionChanged(Projection proj) {
+        if(!(proj instanceof ProjectionSubPrefs)) {
+            projSubPrefPanel = new JPanel();
+        } else {
+            ProjectionSubPrefs projPref = (ProjectionSubPrefs) proj;
+            projSubPrefPanel = new SBPanel(projPref);
+            projPref.setupPreferencePanel(projSubPrefPanel, (SBPanel)projSubPrefPanel);
+        }
+
+        // Don't try to update if we're still starting up
+        int size = projPanel.getComponentCount();
+        if(size < 1)
+            return;
+
+        // Replace old panel with new one
+        projSubPrefPanelWrapper.removeAll();
+        projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
+        projPanel.revalidate();
+        projSubPrefPanel.repaint();
+        updateMeta(proj);
+    }
+
+    /**
+     * Sets up projection combobox with default values and action listener
+     */
+    private void setupProjectionCombo() {
+        boolean found = false;
+        for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
+            Projection proj = (Projection)projectionCombo.getItemAt(i);
+            String name = proj.getClass().getName();
+            if(proj instanceof ProjectionSubPrefs) {
+                ((ProjectionSubPrefs) proj).setPreferences(PROP_PROJECTION_SUBPROJECTION.get(name));
+            }
+            if (name.equals(PROP_PROJECTION.get())) {
+                projectionCombo.setSelectedIndex(i);
+                selectedProjectionChanged(proj);
+                found = true;
+                break;
+            }
+        }
+        if (!found)
+            throw new RuntimeException("Couldn't find the current projection in the list of available projections!");
+
+        projectionCombo.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                JComboBox cb = (JComboBox)e.getSource();
+                Projection proj = (Projection)cb.getSelectedItem();
+                selectedProjectionChanged(proj);
+            }
+        });
+    }
+
+    @Override
+    public boolean isExpert() {
+        return false;
+    }
+
+    @Override
+    public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
+        return gui.getMapPreference();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java	(revision 5226)
@@ -0,0 +1,13 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+/**
+ * ProjectionSubPrefs can implement this interface to set some additional options
+ */
+public interface SubPrefsOptions {
+
+    /**
+     * @return true, if the projection code should be displayed in the top panel
+     */
+    boolean showProjectionCode();
+}
Index: trunk/src/org/openstreetmap/josm/gui/widgets/AbstractTextComponentValidator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/AbstractTextComponentValidator.java	(revision 5225)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/AbstractTextComponentValidator.java	(revision 5226)
@@ -19,4 +19,5 @@
 
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -41,7 +42,9 @@
      */
     private Boolean valid = null;
+    // remember the message
+    private String msg;
 
     protected void feedbackInvalid(String msg) {
-        if (valid == null || valid) {
+        if (valid == null || valid || !Utils.equal(msg, this.msg)) {
             // only provide feedback if the validity has changed. This avoids
             // unnecessary UI updates.
@@ -50,4 +53,5 @@
             tc.setToolTipText(msg);
             valid = false;
+            this.msg = msg;
         }
     }
@@ -58,5 +62,5 @@
 
     protected void feedbackValid(String msg) {
-        if (valid == null || !valid) {
+        if (valid == null || !valid || !Utils.equal(msg, this.msg)) {
             // only provide feedback if the validity has changed. This avoids
             // unnecessary UI updates.
@@ -65,4 +69,5 @@
             tc.setToolTipText(msg == null ? "" : msg);
             valid = true;
+            this.msg = msg;
         }
     }
@@ -92,8 +97,16 @@
      */
     public AbstractTextComponentValidator(JTextComponent tc, boolean addActionListener) throws IllegalArgumentException {
+        this(tc, true, true, addActionListener);
+    }
+
+    public AbstractTextComponentValidator(JTextComponent tc, boolean addFocusListener, boolean addDocumentListener, boolean addActionListener) throws IllegalArgumentException {
         CheckParameterUtil.ensureParameterNotNull(tc, "tc");
         this.tc = tc;
-        tc.addFocusListener(this);
-        tc.getDocument().addDocumentListener(this);
+        if (addFocusListener) {
+            tc.addFocusListener(this);
+        }
+        if (addDocumentListener) {
+            tc.getDocument().addDocumentListener(this);
+        }
         if (addActionListener) {
             if (tc instanceof JTextField) {
