Index: /trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/Main.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/Main.java	(revision 5234)
@@ -816,6 +816,7 @@
         CheckParameterUtil.ensureParameterNotNull(p);
         Projection oldValue = proj;
+        Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
         proj = p;
-        fireProjectionChanged(oldValue, proj);
+        fireProjectionChanged(oldValue, proj, b);
     }
 
@@ -827,5 +828,5 @@
     private static final ArrayList<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<WeakReference<ProjectionChangeListener>>();
 
-    private static void fireProjectionChanged(Projection oldValue, Projection newValue) {
+    private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
         if (newValue == null ^ oldValue == null
                 || (newValue != null && oldValue != null && !Utils.equal(newValue.toCode(), oldValue.toCode()))) {
@@ -843,9 +844,6 @@
                 }
             }
-            if (newValue != null) {
-                Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
-                if (b != null){
-                    Main.map.mapView.zoomTo(b);
-                }
+            if (newValue != null && oldBounds != null) {
+                Main.map.mapView.zoomTo(oldBounds);
             }
             /* TODO - remove layers with fixed projection */
Index: /trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5234)
@@ -40,5 +40,5 @@
      * null means fall back mode (Mercator)
      */
-    protected String pref = null;
+    protected String pref;
     protected Bounds bounds;
 
@@ -87,4 +87,21 @@
             this.key = key;
             this.hasValue = hasValue;
+        }
+    }
+
+    public CustomProjection() {
+        this.pref = null;
+    }
+
+    public CustomProjection(String pref) {
+        try {
+            this.pref = pref;
+            update(pref);
+        } catch (ProjectionConfigurationException ex) {
+            try {
+                update(null);
+            } catch (ProjectionConfigurationException ex1) {
+                throw new RuntimeException();
+            }
         }
     }
Index: unk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5233)
+++ 	(revision )
@@ -1,232 +1,0 @@
-// 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.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;
-
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-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;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Utils;
-
-public class CustomProjectionPrefGui extends CustomProjection implements ProjectionSubPrefs, SubPrefsOptions {
-
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JPanel inner = new PreferencePanel(listener);
-        p.setLayout(new GridBagLayout());
-        p.add(inner, GBC.std().fill(GBC.HORIZONTAL));
-    }
-
-    private class PreferencePanel extends JPanel {
-
-        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);
-            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."));
-                    }
-                    listener.actionPerformed(null);
-                }
-
-                @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));
-
-            JButton btnInfo = new JButton(tr("Parameter information..."));
-            btnInfo.addActionListener(new ActionListener() {
-                @Override
-                public void actionPerformed(ActionEvent e) {
-                    ParameterInfoDialog dlg = new ParameterInfoDialog();
-                    dlg.showDialog();
-                    dlg.toFront();
-                }
-            });
-
-            this.setLayout(new GridBagLayout());
-            JPanel p2 = new JPanel(new GridBagLayout());
-            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));
-            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));
-            this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
-            p2 = new JPanel(new GridBagLayout());
-            p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0));
-            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());
-        }
-    }
-
-    public class ParameterInfoDialog extends ExtendedDialog {
-
-        public ParameterInfoDialog() {
-            super(null, tr("Parameter information"), new String[] { tr("Close") }, false);
-            setContent(build());
-        }
-
-        private JComponent build() {
-            StringBuilder s = new StringBuilder();
-            s.append("<b>+proj=...</b> - <i>"+tr("Projection name")+"</i><br>");
-            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
-            s.append(listKeys(Projections.projs)+"<br>");
-            s.append("<b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>"+tr("Projection parameters")+"</i><br>");
-            s.append("<b>+x_0=..., +y_0=...</b> - <i>"+tr("False easting and false northing")+"</i><br>");
-            s.append("<b>+lon_0=...</b> - <i>"+tr("Central meridian")+"</i><br>");
-            s.append("<b>+k_0=...</b> - <i>"+tr("Scaling factor")+"</i><br>");
-            s.append("<b>+ellps=...</b> - <i>"+tr("Ellipsoid name")+"</i><br>");
-            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
-            s.append(listKeys(Projections.ellipsoids)+"<br>");
-            s.append("<b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>"+tr("Ellipsoid parameters")+"</i><br>");
-            s.append("<b>+datum=...</b> - <i>"+tr("Datum name")+"</i><br>");
-            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
-            s.append(listKeys(Projections.datums)+"<br>");
-            s.append("<b>+towgs84=...</b> - <i>"+tr("3 or 7 term datum transform parameters")+"</i><br>");
-            s.append("<b>+nadgrids=...</b> - <i>"+tr("NTv2 grid file")+"</i><br>");
-            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());
-            return info;
-        }
-
-        private String listKeys(Map<String, ?> map) {
-            List<String> keys = new ArrayList<String>(map.keySet());
-            Collections.sort(keys);
-            return Utils.join(", ", keys);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        PreferencePanel prefPanel = (PreferencePanel) p.getComponent(0);
-        String pref = prefPanel.input.getText();
-        prefPanel.rememberHistory();
-        return Collections.singleton(pref);
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        try {
-            if (args == null || args.isEmpty()) throw new ProjectionConfigurationException();
-            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 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/GaussKrueger.java	(revision 5234)
@@ -3,13 +3,4 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -19,10 +10,9 @@
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
-import org.openstreetmap.josm.tools.GBC;
 
-public class GaussKrueger extends AbstractProjection implements ProjectionSubPrefs {
+public class GaussKrueger extends AbstractProjection {
 
     public static final int DEFAULT_ZONE = 2;
-    private int zone;
+    private final int zone;
 
     private static Bounds[] bounds = {
@@ -33,6 +23,4 @@
     };
 
-    private static String[] zones = { "2", "3", "4", "5" };
-
     public GaussKrueger() {
         this(DEFAULT_ZONE);
@@ -40,8 +28,6 @@
 
     public GaussKrueger(int zone) {
-        updateParameters(zone);
-    }
-
-    private void updateParameters(int zone) {
+        if (zone < 2 || zone > 5)
+            throw new IllegalArgumentException();
         this.zone = zone;
         ellps = Ellipsoid.Bessel1841;
@@ -63,5 +49,5 @@
     @Override
     public String toString() {
-        return tr("Gau\u00DF-Kr\u00FCger");
+        return tr("Gau\u00DF-Kr\u00FCger Zone {0}", zone);
     }
 
@@ -81,68 +67,3 @@
     }
 
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JComboBox prefcb = new JComboBox(zones);
-
-        prefcb.setSelectedIndex(zone-2);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("GK Zone")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        if (listener != null) {
-            prefcb.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefcb = p.getComponent(2);
-        if(!(prefcb instanceof JComboBox))
-            return null;
-        int zone = ((JComboBox)prefcb).getSelectedIndex();
-        return Collections.singleton(Integer.toString(zone+2));
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int zone = DEFAULT_ZONE;
-        if (args != null) {
-            try {
-                for(String s : args)
-                {
-                    zone = Integer.parseInt(s);
-                    if(zone < 2 || zone > 5) {
-                        zone = DEFAULT_ZONE;
-                    }
-                    break;
-                }
-            } catch(NumberFormatException e) {}
-        }
-        updateParameters(zone);
-    }
-
-    @Override
-    public String[] allCodes() {
-        String[] zones = new String[4];
-        for (int zone = 2; zone <= 5; zone++) {
-            zones[zone-2] = "EPSG:" + (31464 + zone);
-        }
-        return zones;
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code)
-    {
-        //zone 2 = EPSG:31466 up to zone 5 = EPSG:31469
-        for (int zone = 2; zone <= 5; zone++) {
-            String epsg = "EPSG:" + (31464 + zone);
-            if (epsg.equals(code))
-                return Collections.singleton(String.valueOf(zone));
-        }
-        return null;
-    }
-
 }
Index: /trunk/src/org/openstreetmap/josm/data/projection/Lambert.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 5234)
@@ -30,5 +30,5 @@
  * @author Pieren
  */
-public class Lambert extends AbstractProjection implements ProjectionSubPrefs {
+public class Lambert extends AbstractProjection {
 
     /**
@@ -92,8 +92,8 @@
 
     public Lambert() {
-        updateParameters(DEFAULT_ZONE);
+        this(DEFAULT_ZONE);
     }
 
-    private void updateParameters(final int layoutZone) {
+    public Lambert(final int layoutZone) {
         this.layoutZone = layoutZone;
         ellps = Ellipsoid.clarkeIGN;
@@ -151,77 +151,3 @@
     }
 
-    public static String[] lambert4zones = {
-        tr("{0} ({1} to {2} degrees)", 1,"51.30","48.15"),
-        tr("{0} ({1} to {2} degrees)", 2,"48.15","45.45"),
-        tr("{0} ({1} to {2} degrees)", 3,"45.45","42.76"),
-        tr("{0} (Corsica)", 4)
-    };
-
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JComboBox prefcb = new JComboBox(lambert4zones);
-
-        prefcb.setSelectedIndex(layoutZone);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("Lambert CC Zone")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(new JLabel(ImageProvider.get("data/projection", "Departements_Lambert4Zones.png")), GBC.eol().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        if (listener != null) {
-            prefcb.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefcb = p.getComponent(2);
-        if(!(prefcb instanceof JComboBox))
-            return null;
-        layoutZone = ((JComboBox)prefcb).getSelectedIndex();
-        return Collections.singleton(Integer.toString(layoutZone+1));
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int layoutZone = DEFAULT_ZONE;
-        if (args != null) {
-            try {
-                for(String s : args)
-                {
-                    layoutZone = Integer.parseInt(s)-1;
-                    if(layoutZone < 0 || layoutZone > 3) {
-                        layoutZone = DEFAULT_ZONE;
-                    }
-                    break;
-                }
-            } catch(NumberFormatException e) {}
-        }
-        updateParameters(layoutZone);
-    }
-
-    @Override
-    public String[] allCodes() {
-        String[] zones = new String[4];
-        for (int zone = 0; zone < 4; zone++) {
-            zones[zone] = "EPSG:"+(27561+zone);
-        }
-        return zones;
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-        if (code.startsWith("EPSG:2756") && code.length() == 10) {
-            try {
-                String zonestring = code.substring(9);
-                int zoneval = Integer.parseInt(zonestring);
-                if(zoneval >= 1 && zoneval <= 4)
-                    return Collections.singleton(zonestring);
-            } catch(NumberFormatException e) {}
-        }
-        return null;
-    }
-
 }
Index: /trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java	(revision 5234)
@@ -3,13 +3,4 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -18,6 +9,4 @@
 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
@@ -27,5 +16,5 @@
  *
  */
-public class LambertCC9Zones extends AbstractProjection implements ProjectionSubPrefs {
+public class LambertCC9Zones extends AbstractProjection {
 
     /**
@@ -40,5 +29,5 @@
     public static final int DEFAULT_ZONE = 0;
 
-    private int layoutZone = DEFAULT_ZONE;
+    private final int layoutZone;
 
     public LambertCC9Zones() {
@@ -46,9 +35,5 @@
     }
 
-    public LambertCC9Zones(int layoutZone) {
-        updateParameters(layoutZone);
-    }
-
-    public void updateParameters(final int layoutZone) {
+    public LambertCC9Zones(final int layoutZone) {
         ellps = Ellipsoid.GRS80;
         datum = GRS80Datum.INSTANCE;
@@ -112,83 +97,3 @@
     }
 
-    private static String[] lambert9zones = {
-        tr("{0} ({1} to {2} degrees)", 1,41,43),
-        tr("{0} ({1} to {2} degrees)", 2,42,44),
-        tr("{0} ({1} to {2} degrees)", 3,43,45),
-        tr("{0} ({1} to {2} degrees)", 4,44,46),
-        tr("{0} ({1} to {2} degrees)", 5,45,47),
-        tr("{0} ({1} to {2} degrees)", 6,46,48),
-        tr("{0} ({1} to {2} degrees)", 7,47,49),
-        tr("{0} ({1} to {2} degrees)", 8,48,50),
-        tr("{0} ({1} to {2} degrees)", 9,49,51)
-    };
-
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JComboBox prefcb = new JComboBox(lambert9zones);
-
-        prefcb.setSelectedIndex(layoutZone);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("Lambert CC Zone")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(new JLabel(ImageProvider.get("data/projection", "LambertCC9Zones.png")), GBC.eol().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        if (listener != null) {
-            prefcb.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefcb = p.getComponent(2);
-        if(!(prefcb instanceof JComboBox))
-            return null;
-        int layoutZone = ((JComboBox)prefcb).getSelectedIndex();
-        return Collections.singleton(Integer.toString(layoutZone+1));
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int layoutZone = DEFAULT_ZONE;
-        if (args != null) {
-            try {
-                for(String s : args)
-                {
-                    layoutZone = Integer.parseInt(s)-1;
-                    if(layoutZone < 0 || layoutZone > 8) {
-                        layoutZone = DEFAULT_ZONE;
-                    }
-                    break;
-                }
-            } catch(NumberFormatException e) {}
-        }
-        updateParameters(layoutZone);
-    }
-
-    @Override
-    public String[] allCodes() {
-        String[] zones = new String[9];
-        for (int zone = 0; zone < 9; zone++) {
-            zones[zone] = "EPSG:" + (3942 + zone);
-        }
-        return zones;
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code)
-    {
-        //zone 1=CC42=EPSG:3942 up to zone 9=CC50=EPSG:3950
-        if (code.startsWith("EPSG:39") && code.length() == 9) {
-            try {
-                String zonestring = code.substring(5,9);
-                int zoneval = Integer.parseInt(zonestring)-3942;
-                if(zoneval >= 0 && zoneval <= 8)
-                    return Collections.singleton(String.valueOf(zoneval+1));
-            } catch(NumberFormatException ex) {}
-        }
-        return null;
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/projection/ProjectionInfo.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/ProjectionInfo.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/ProjectionInfo.java	(revision 5234)
@@ -1,32 +1,20 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.projection;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.util.Collection;
 import java.util.HashMap;
 
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionChoice;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
+
 public class ProjectionInfo {
+    private static HashMap<String, ProjectionChoice> allCodesPC;
     private static HashMap<String, Projection> allCodes;
-
-    private static ProjectionSubPrefs recreateProj(ProjectionSubPrefs proj) {
-        try {
-            return proj.getClass().newInstance();
-        } catch (Exception e) {
-            throw new IllegalStateException(
-                    tr("Cannot instantiate projection ''{0}''", proj.getClass().toString()), e);
-        }
-    }
 
     static {
         allCodes = new HashMap<String, Projection>();
-        for (Projection proj : Projections.getProjections()) {
-            if (proj instanceof ProjectionSubPrefs) {
-                ProjectionSubPrefs projSub = recreateProj((ProjectionSubPrefs)proj);
-                for (String code : projSub.allCodes()) {
-                    allCodes.put(code, projSub);
-                }
-            } else {
-                allCodes.put(proj.toCode(), proj);
+        for (ProjectionChoice pc : ProjectionPreference.getProjectionChoices()) {
+            for (String code : pc.allCodes()) {
+                allCodesPC.put(code, pc);
             }
         }
@@ -34,22 +22,13 @@
 
     public static Projection getProjectionByCode(String code) {
-        Projection proj = allCodes.get(code);
-        if (proj == null) return null;
-        if (code.equals(proj.toCode())) return proj;
-        if (!(proj instanceof ProjectionSubPrefs))
-            throw new IllegalStateException(tr(
-                    "Projection code mismatch in ''{0}'': toCode() returns ''{1}'', expected ''{2}''.",
-                    proj.getClass().toString(), proj.toCode(), code));
-        ProjectionSubPrefs projSub = recreateProj((ProjectionSubPrefs)proj);
-        Collection<String> prefs = projSub.getPreferencesFromCode(code);
-        if (prefs != null) {
-            projSub.setPreferences(prefs);
-        }
-        if (!code.equals(projSub.toCode()))
-            throw new IllegalStateException(tr(
-                    "Bad implementation of ''{0}'' projection class: cannot set preferences to match code ''{1}''.",
-                    projSub.getClass().toString(), code));
-        allCodes.put(code, projSub);
-        return projSub;
+        Projection p = allCodes.get(code);
+        if (p != null) return p;
+        ProjectionChoice pc = allCodesPC.get(code);
+        if (pc == null) return null;
+        Collection<String> pref = pc.getPreferencesFromCode(code);
+        pc.setPreferences(pref);
+        p = pc.getProjection();
+        allCodes.put(code, p);
+        return p;
     }
 }
Index: unk/src/org/openstreetmap/josm/data/projection/ProjectionSubPrefs.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/ProjectionSubPrefs.java	(revision 5233)
+++ 	(revision )
@@ -1,38 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.projection;
-
-import java.awt.event.ActionListener;
-import java.util.Collection;
-
-import javax.swing.JPanel;
-
-public interface ProjectionSubPrefs extends Projection {
-    /**
-     * Generates the GUI for the given preference and packs them in a JPanel
-     * so they may be displayed if the projection is selected.
-     * 
-     * @param listener   listener for any change of preferences
-     */
-    public void setupPreferencePanel(JPanel p, ActionListener listener);
-
-    /**
-     * Will be called if the preference dialog is dismissed.
-     */
-    public Collection<String> getPreferences(JPanel p);
-
-    /**
-     * Return all projection codes supported by this projection class.
-     */
-    public String[] allCodes();
-
-    /**
-     * Return null when code is not part of this projection.
-     */
-    public Collection<String> getPreferencesFromCode(String code);
-
-    /**
-     * Will be called if the preference dialog is dismissed.
-     * argument may be null to reset everything
-     */
-    public void setPreferences(Collection<String> args);
-}
Index: /trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5234)
@@ -6,6 +6,4 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -33,42 +31,4 @@
  */
 public class Projections {
-    /**
-     * List of all available projections.
-     */
-    private static ArrayList<Projection> allProjections =
-        new ArrayList<Projection>(Arrays.asList(new Projection[] {
-                // global projections
-                new Epsg4326(),
-                new Mercator(),
-                new UTM(),
-                // regional - alphabetical order by country code
-                new BelgianLambert1972(),   // BE
-                new BelgianLambert2008(),   // BE
-                new SwissGrid(),            // CH
-                new GaussKrueger(),         // DE
-                new LambertEST(),           // EE
-                new Lambert(),              // FR
-                new Lambert93(),            // FR
-                new LambertCC9Zones(),      // FR
-                new UTM_France_DOM(),       // FR
-                new TransverseMercatorLV(), // LV
-                new Puwg(),                 // PL
-                new Epsg3008(),             // SE
-                new CustomProjectionPrefGui()
-        }));
-
-    public static ArrayList<Projection> getProjections() {
-        return allProjections;
-    }
-
-    /**
-     * Adds a new projection to the list of known projections.
-     *
-     * For Plugins authors: make sure your plugin is an early plugin, i.e. put
-     * Plugin-Early=true in your Manifest.
-     */
-    public static void addProjection(Projection proj) {
-        allProjections.add(proj);
-    }
 
     public static EastNorth project(LatLon ll) {
Index: /trunk/src/org/openstreetmap/josm/data/projection/Puwg.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/Puwg.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/Puwg.java	(revision 5234)
@@ -4,13 +4,4 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -18,5 +9,4 @@
 import org.openstreetmap.josm.data.projection.datum.GRS80Datum;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
-import org.openstreetmap.josm.tools.GBC;
 
 /**
@@ -26,11 +16,11 @@
  * @author steelman
  */
-public class Puwg extends AbstractProjection implements ProjectionSubPrefs {
+public class Puwg extends AbstractProjection {
 
     public static final int DEFAULT_ZONE = 0;
 
-    private int zone;
-
-    static PuwgData[] Zones = new PuwgData[] {
+    private final int zone;
+
+    static public PuwgData[] Zones = new PuwgData[] {
         new Epsg2180(),
         new Epsg2176(),
@@ -53,8 +43,4 @@
         }
         datum = GRS80Datum.INSTANCE;
-        updateParameters(zone);
-    }
-
-    public void updateParameters(int zone) {
         this.zone = zone;
         PuwgData z = Zones[zone];
@@ -90,194 +76,132 @@
     }
 
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JComboBox prefcb = new JComboBox(Puwg.Zones);
-
-        prefcb.setSelectedIndex(zone);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("PUWG Zone")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        if (listener != null) {
-            prefcb.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefcb = p.getComponent(2);
-        if(!(prefcb instanceof JComboBox))
-            return null;
-        int zone = ((JComboBox)prefcb).getSelectedIndex();
-        return Collections.singleton((Puwg.Zones[zone]).toCode());
-    }
-
-    @Override
-    public String[] allCodes() {
-        String[] zones = new String[Zones.length];
-        for (int zone = 0; zone < Zones.length; zone++) {
-            zones[zone] = Zones[zone].toCode();
-        }
-        return zones;
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-        for (PuwgData p : Puwg.Zones) {
-            if (code.equals(p.toCode()))
-                return Collections.singleton(code);
-        }
-        return null;
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int z = DEFAULT_ZONE;
-        if (args != null) {
-            try {
-                for (String s : args) {
-                    for (int i=0; i < Zones.length; ++i)
-                        if (s.equals(Zones[i].toCode())) {
-                            z = i;
-                            break;
-                        }
-                    break;
-                }
-            } catch (NullPointerException e) {}
-        }
-        updateParameters(z);
+    public interface PuwgData {
+        double getPuwgCentralMeridianDeg();
+        double getPuwgCentralMeridian();
+        double getPuwgFalseEasting();
+        double getPuwgFalseNorthing();
+        double getPuwgScaleFactor();
+
+        // Projection methods
+        Integer getEpsgCode();
+        String toCode();
+        String getCacheDirectoryName();
+        Bounds getWorldBoundsLatLon();
+    }
+
+    public static class Epsg2180 implements PuwgData {
+
+        private static final double Epsg2180FalseEasting = 500000.0; /* y */
+        private static final double Epsg2180FalseNorthing = -5300000.0; /* x */
+        private static final double Epsg2180ScaleFactor = 0.9993;
+        private static final double Epsg2180CentralMeridian = 19.0;
+
+        @Override public String toString() {
+            return tr("PUWG 1992 (Poland)");
+        }
+
+        @Override
+        public Integer getEpsgCode() {
+            return 2180;
+        }
+
+        @Override
+        public String toCode() {
+            return "EPSG:" + getEpsgCode();
+        }
+
+        @Override
+        public String getCacheDirectoryName() {
+            return "epsg2180";
+        }
+
+        @Override
+        public Bounds getWorldBoundsLatLon()
+        {
+            return new Bounds(
+                    new LatLon(49.00, 14.12),
+                    new LatLon(54.84, 24.15));
+        }
+
+        @Override public double getPuwgCentralMeridianDeg() { return Epsg2180CentralMeridian; }
+        @Override public double getPuwgCentralMeridian() { return Math.toRadians(Epsg2180CentralMeridian); }
+        @Override public double getPuwgFalseEasting() { return Epsg2180FalseEasting; }
+        @Override public double getPuwgFalseNorthing() { return Epsg2180FalseNorthing; }
+        @Override public double getPuwgScaleFactor() { return Epsg2180ScaleFactor; }
+    }
+
+    abstract static class Puwg2000 implements PuwgData {
+
+        private static final double PuwgFalseEasting = 500000.0;
+        private static final double PuwgFalseNorthing = 0;
+        private static final double PuwgScaleFactor = 0.999923;
+        //final private double[] Puwg2000CentralMeridian = {15.0, 18.0, 21.0, 24.0};
+        final private Integer[] Puwg2000Code = { 2176,  2177, 2178, 2179 };
+        final private String[] Puwg2000CDName = { "epsg2176",  "epsg2177", "epsg2178", "epsg2179" };
+
+        @Override public String toString() {
+            return tr("PUWG 2000 Zone {0} (Poland)", Integer.toString(getZone()));
+        }
+
+        @Override
+        public Integer getEpsgCode() {
+            return Puwg2000Code[getZoneIndex()];
+        }
+
+        @Override
+        public String toCode() {
+            return "EPSG:" + getEpsgCode();
+        }
+
+        @Override
+        public String getCacheDirectoryName() {
+            return Puwg2000CDName[getZoneIndex()];
+        }
+
+        @Override
+        public Bounds getWorldBoundsLatLon()
+        {
+            return new Bounds(
+                    new LatLon(49.00, (3 * getZone()) - 1.5),
+                    new LatLon(54.84, (3 * getZone()) + 1.5));
+        }
+
+        @Override public double getPuwgCentralMeridianDeg() { return getZone() * 3.0; }
+        @Override public double getPuwgCentralMeridian() { return Math.toRadians(getZone() * 3.0); }
+        @Override public double getPuwgFalseNorthing() { return PuwgFalseNorthing;}
+        @Override public double getPuwgFalseEasting() { return 1e6 * getZone() + PuwgFalseEasting; }
+        @Override public double getPuwgScaleFactor() { return PuwgScaleFactor; }
+        public abstract int getZone();
+
+        public int getZoneIndex() { return getZone() - 5; }
+
+    }
+
+    public static class Epsg2176 extends Puwg2000 {
+        private static final int PuwgZone = 5;
+
+        @Override
+        public int getZone() { return PuwgZone; }
+    }
+
+    public static class Epsg2177 extends Puwg2000 {
+        private static final int PuwgZone = 6;
+
+        @Override
+        public int getZone() { return PuwgZone; }
+    }
+
+    public static class Epsg2178 extends Puwg2000 {
+        private static final int PuwgZone = 7;
+
+        @Override
+        public int getZone() { return PuwgZone; }
+    }
+
+    public static class Epsg2179 extends Puwg2000 {
+        private static final int PuwgZone = 8;
+
+        @Override
+        public int getZone() { return PuwgZone; }
     }
 }
-
-interface PuwgData {
-    double getPuwgCentralMeridianDeg();
-    double getPuwgCentralMeridian();
-    double getPuwgFalseEasting();
-    double getPuwgFalseNorthing();
-    double getPuwgScaleFactor();
-
-    // Projection methods
-    Integer getEpsgCode();
-    String toCode();
-    String getCacheDirectoryName();
-    Bounds getWorldBoundsLatLon();
-}
-
-class Epsg2180 implements PuwgData {
-
-    private static final double Epsg2180FalseEasting = 500000.0; /* y */
-    private static final double Epsg2180FalseNorthing = -5300000.0; /* x */
-    private static final double Epsg2180ScaleFactor = 0.9993;
-    private static final double Epsg2180CentralMeridian = 19.0;
-
-    @Override public String toString() {
-        return tr("PUWG 1992 (Poland)");
-    }
-
-    @Override
-    public Integer getEpsgCode() {
-        return 2180;
-    }
-
-    @Override
-    public String toCode() {
-        return "EPSG:" + getEpsgCode();
-    }
-
-    @Override
-    public String getCacheDirectoryName() {
-        return "epsg2180";
-    }
-
-    @Override
-    public Bounds getWorldBoundsLatLon()
-    {
-        return new Bounds(
-                new LatLon(49.00, 14.12),
-                new LatLon(54.84, 24.15));
-    }
-
-    @Override public double getPuwgCentralMeridianDeg() { return Epsg2180CentralMeridian; }
-    @Override public double getPuwgCentralMeridian() { return Math.toRadians(Epsg2180CentralMeridian); }
-    @Override public double getPuwgFalseEasting() { return Epsg2180FalseEasting; }
-    @Override public double getPuwgFalseNorthing() { return Epsg2180FalseNorthing; }
-    @Override public double getPuwgScaleFactor() { return Epsg2180ScaleFactor; }
-}
-
-abstract class Puwg2000 implements PuwgData {
-
-    private static final double PuwgFalseEasting = 500000.0;
-    private static final double PuwgFalseNorthing = 0;
-    private static final double PuwgScaleFactor = 0.999923;
-    //final private double[] Puwg2000CentralMeridian = {15.0, 18.0, 21.0, 24.0};
-    final private Integer[] Puwg2000Code = { 2176,  2177, 2178, 2179 };
-    final private String[] Puwg2000CDName = { "epsg2176",  "epsg2177", "epsg2178", "epsg2179" };
-
-    @Override public String toString() {
-        return tr("PUWG 2000 Zone {0} (Poland)", Integer.toString(getZone()));
-    }
-
-    @Override
-    public Integer getEpsgCode() {
-        return Puwg2000Code[getZoneIndex()];
-    }
-
-    @Override
-    public String toCode() {
-        return "EPSG:" + getEpsgCode();
-    }
-
-    @Override
-    public String getCacheDirectoryName() {
-        return Puwg2000CDName[getZoneIndex()];
-    }
-
-    @Override
-    public Bounds getWorldBoundsLatLon()
-    {
-        return new Bounds(
-                new LatLon(49.00, (3 * getZone()) - 1.5),
-                new LatLon(54.84, (3 * getZone()) + 1.5));
-    }
-
-    @Override public double getPuwgCentralMeridianDeg() { return getZone() * 3.0; }
-    @Override public double getPuwgCentralMeridian() { return Math.toRadians(getZone() * 3.0); }
-    @Override public double getPuwgFalseNorthing() { return PuwgFalseNorthing;}
-    @Override public double getPuwgFalseEasting() { return 1e6 * getZone() + PuwgFalseEasting; }
-    @Override public double getPuwgScaleFactor() { return PuwgScaleFactor; }
-    public abstract int getZone();
-
-    public int getZoneIndex() { return getZone() - 5; }
-
-}
-
-class Epsg2176 extends Puwg2000 {
-    private static final int PuwgZone = 5;
-
-    @Override
-    public int getZone() { return PuwgZone; }
-}
-
-class Epsg2177 extends Puwg2000 {
-    private static final int PuwgZone = 6;
-
-    @Override
-    public int getZone() { return PuwgZone; }
-}
-
-class Epsg2178 extends Puwg2000 {
-    private static final int PuwgZone = 7;
-
-    @Override
-    public int getZone() { return PuwgZone; }
-}
-
-class Epsg2179 extends Puwg2000 {
-    private static final int PuwgZone = 8;
-
-    @Override
-    public int getZone() { return PuwgZone; }
-}
Index: /trunk/src/org/openstreetmap/josm/data/projection/SwissGrid.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/SwissGrid.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/SwissGrid.java	(revision 5234)
@@ -3,11 +3,4 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.event.ActionListener;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.swing.Box;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -16,6 +9,4 @@
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
 import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
-import org.openstreetmap.josm.gui.widgets.HtmlPanel;
-import org.openstreetmap.josm.tools.GBC;
 
 /**
@@ -31,5 +22,5 @@
  * ProjectionSubPrefs to show a warning that the grid file correction is not done.
  */
-public class SwissGrid extends AbstractProjection implements ProjectionSubPrefs {
+public class SwissGrid extends AbstractProjection {
 
     public SwissGrid() {
@@ -75,29 +66,3 @@
     }
 
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        p.add(new HtmlPanel(tr("<i>CH1903 / LV03 (without local corrections)</i>")), GBC.eol().fill(GBC.HORIZONTAL));
-        p.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        return Collections.singletonList("CH1903");
-    }
-
-    @Override
-    public String[] allCodes() {
-        return new String[] { "EPSG:21781" };
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-        if ("EPSG:21781".equals(code))
-            return Collections.singletonList("CH1903");
-        return null;
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/projection/UTM.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/UTM.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/UTM.java	(revision 5234)
@@ -4,22 +4,8 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-
-import javax.swing.ButtonGroup;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
-import org.openstreetmap.josm.tools.GBC;
 
 /**
@@ -29,5 +15,5 @@
  *
  */
-public class UTM extends AbstractProjection implements ProjectionSubPrefs {
+public class UTM extends AbstractProjection {
 
     private static final int DEFAULT_ZONE = 30;
@@ -56,8 +42,4 @@
         }
         datum = WGS84Datum.INSTANCE;
-        updateParameters(zone, hemisphere, offset);
-    }
-
-    public void updateParameters(int zone, Hemisphere hemisphere, boolean offset) {
         this.zone = zone;
         this.hemisphere = hemisphere;
@@ -125,154 +107,3 @@
     }
 
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        //Zone
-        JComboBox zonecb = new JComboBox();
-        for(int i = 1; i <= 60; i++) {
-            zonecb.addItem(i);
-        }
-
-        zonecb.setSelectedIndex(zone - 1);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("UTM Zone")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(zonecb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        //Hemisphere
-        JRadioButton north = new JRadioButton();
-        north.setSelected(hemisphere == Hemisphere.North);
-        JRadioButton south = new JRadioButton();
-        south.setSelected(hemisphere == Hemisphere.South);
-
-        ButtonGroup group = new ButtonGroup();
-        group.add(north);
-        group.add(south);
-
-        JPanel bPanel = new JPanel();
-        bPanel.setLayout(new GridBagLayout());
-
-        bPanel.add(new JLabel(tr("North")), GBC.std().insets(5, 5, 0, 5));
-        bPanel.add(north, GBC.std().fill(GBC.HORIZONTAL));
-        bPanel.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        bPanel.add(new JLabel(tr("South")), GBC.std().insets(5, 5, 0, 5));
-        bPanel.add(south, GBC.std().fill(GBC.HORIZONTAL));
-        bPanel.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        p.add(new JLabel(tr("Hemisphere")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        p.add(bPanel, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        //Offset
-        JCheckBox offsetBox = new JCheckBox();
-        offsetBox.setSelected(offset);
-
-        p.add(new JLabel(tr("Offset 3.000.000m east")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        /* Note: we use component position 2 below to find this again */
-        p.add(offsetBox, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-
-        if (listener != null) {
-            north.addActionListener(listener);
-            south.addActionListener(listener);
-            zonecb.addActionListener(listener);
-            offsetBox.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        int zone = DEFAULT_ZONE;
-        Hemisphere hemisphere = DEFAULT_HEMISPHERE;
-        boolean offset = false;
-
-        Object zonecb = p.getComponent(2);
-        if (zonecb instanceof JComboBox) {
-            zone = ((JComboBox)zonecb).getSelectedIndex() + 1;
-        }
-
-        Object bPanel = p.getComponent(6);
-        if (bPanel instanceof JPanel) {
-            Object south = ((JPanel)bPanel).getComponent(4);
-            if (south instanceof JRadioButton) {
-                hemisphere = ((JRadioButton)south).isSelected()?Hemisphere.South:Hemisphere.North;
-            }
-        }
-
-        Object offsetBox = p.getComponent(10);
-        if (offsetBox instanceof JCheckBox) {
-            offset = ((JCheckBox) offsetBox).isSelected();
-        }
-
-        return Arrays.asList(Integer.toString(zone), hemisphere.toString(), (offset?"offset":"standard"));
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int zone = DEFAULT_ZONE;
-        Hemisphere hemisphere = DEFAULT_HEMISPHERE;
-        boolean offset = false;
-
-        if(args != null)
-        {
-            String[] array = args.toArray(new String[0]);
-            try {
-                zone = Integer.parseInt(array[0]);
-                if(zone <= 0 || zone > 60) {
-                    zone = DEFAULT_ZONE;
-                }
-            } catch(NumberFormatException e) {}
-
-            if (array.length > 1) {
-                hemisphere = Hemisphere.valueOf(array[1]);
-            }
-
-            if (array.length > 2) {
-                offset = array[2].equals("offset");
-            }
-        }
-        updateParameters(zone, hemisphere, offset);
-    }
-
-    @Override
-    public String[] allCodes() {
-        ArrayList<String> projections = new ArrayList<String>(60*4);
-        for (int zone = 1;zone <= 60; zone++) {
-            for (boolean offset : new boolean[] { false, true }) {
-                for (Hemisphere hemisphere : Hemisphere.values()) {
-                    projections.add("EPSG:" + ((offset?325800:32600) + zone + (hemisphere == Hemisphere.South?100:0)));
-                }
-            }
-        }
-        return projections.toArray(new String[0]);
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-
-        boolean offset = code.startsWith("EPSG:3258") || code.startsWith("EPSG:3259");
-
-        if(code.startsWith("EPSG:326") || code.startsWith("EPSG:327") || offset)
-        {
-            try {
-                Hemisphere hemisphere;
-                String zonestring;
-                if (offset) {
-                    hemisphere = code.charAt(8)=='8'?Hemisphere.North:Hemisphere.South;
-                    zonestring = code.substring(9);
-                } else {
-                    hemisphere = code.charAt(7)=='6'?Hemisphere.North:Hemisphere.South;
-                    zonestring = code.substring(8);
-                }
-
-                int zoneval = Integer.parseInt(zonestring);
-                if(zoneval > 0 && zoneval <= 60)
-                    return Arrays.asList(zonestring, hemisphere.toString(), (offset?"offset":"standard"));
-            } catch(NumberFormatException e) {}
-        }
-        return null;
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/projection/UTM_France_DOM.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/UTM_France_DOM.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/data/projection/UTM_France_DOM.java	(revision 5234)
@@ -3,13 +3,4 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -20,5 +11,4 @@
 import org.openstreetmap.josm.data.projection.datum.ThreeParameterDatum;
 import org.openstreetmap.josm.data.projection.proj.ProjParameters;
-import org.openstreetmap.josm.tools.GBC;
 
 /**
@@ -27,12 +17,5 @@
  *
  */
-public class UTM_France_DOM extends AbstractProjection implements ProjectionSubPrefs {
-
-    private final static String FortMarigotName = tr("Guadeloupe Fort-Marigot 1949");
-    private final static String SainteAnneName = tr("Guadeloupe Ste-Anne 1948");
-    private final static String MartiniqueName = tr("Martinique Fort Desaix 1952");
-    private final static String Reunion92Name = tr("Reunion RGR92");
-    private final static String Guyane92Name = tr("Guyane RGFG95");
-    private final static String[] utmGeodesicsNames = { FortMarigotName, SainteAnneName, MartiniqueName, Reunion92Name, Guyane92Name};
+public class UTM_France_DOM extends AbstractProjection {
 
     private final static Bounds FortMarigotBounds = new Bounds( new LatLon(17.6,-63.25), new LatLon(18.5,-62.5));
@@ -48,5 +31,5 @@
     private final static Integer ReunionEPSG = 2975;
     private final static Integer GuyaneEPSG = 2972;
-    private final static Integer[] utmEPSGs = { FortMarigotEPSG, SainteAnneEPSG, MartiniqueEPSG, ReunionEPSG, GuyaneEPSG };
+    public final static Integer[] utmEPSGs = { FortMarigotEPSG, SainteAnneEPSG, MartiniqueEPSG, ReunionEPSG, GuyaneEPSG };
 
     private final static Datum FortMarigotDatum = new ThreeParameterDatum("FortMarigot Datum", null, Ellipsoid.hayford, 136.596, 248.148, -429.789);
@@ -74,8 +57,8 @@
 
     public UTM_France_DOM() {
-        updateParameters(DEFAULT_GEODESIC);
+        this(DEFAULT_GEODESIC);
     }
 
-    public void updateParameters(int currentGeodesic) {
+    public UTM_France_DOM(int currentGeodesic) {
         this.currentGeodesic = currentGeodesic;
         datum = utmDatums[currentGeodesic];
@@ -124,61 +107,3 @@
     }
 
-    @Override
-    public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        JComboBox prefcb = new JComboBox(utmGeodesicsNames);
-
-        prefcb.setSelectedIndex(currentGeodesic);
-        p.setLayout(new GridBagLayout());
-        p.add(new JLabel(tr("UTM Geodesic system")), GBC.std().insets(5,5,0,5));
-        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
-        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
-        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
-        if (listener != null) {
-            prefcb.addActionListener(listener);
-        }
-    }
-
-    @Override
-    public Collection<String> getPreferences(JPanel p) {
-        Object prefcb = p.getComponent(2);
-        if(!(prefcb instanceof JComboBox))
-            return null;
-        currentGeodesic = ((JComboBox)prefcb).getSelectedIndex();
-        return Collections.singleton(Integer.toString(currentGeodesic+1));
-    }
-
-    @Override
-    public String[] allCodes() {
-        String[] res = new String[utmEPSGs.length];
-        for (int i=0; i<utmEPSGs.length; ++i) {
-            res[i] = "EPSG:"+utmEPSGs[i];
-        }
-        return res;
-    }
-
-    @Override
-    public Collection<String> getPreferencesFromCode(String code) {
-        for (int i=0; i < utmEPSGs.length; i++ )
-            if (("EPSG:"+utmEPSGs[i]).equals(code))
-                return Collections.singleton(Integer.toString(i+1));
-        return null;
-    }
-
-    @Override
-    public void setPreferences(Collection<String> args) {
-        int currentGeodesic = DEFAULT_GEODESIC;
-        if (args != null) {
-            try {
-                for(String s : args)
-                {
-                    currentGeodesic = Integer.parseInt(s)-1;
-                    if(currentGeodesic < 0 || currentGeodesic >= utmEPSGs.length) {
-                        currentGeodesic = DEFAULT_GEODESIC;
-                    }
-                    break;
-                }
-            } catch(NumberFormatException e) {}
-        }
-        updateParameters(currentGeodesic);
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java	(revision 5234)
@@ -54,9 +54,8 @@
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
-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.bbox.SlippyMapBBoxChooser;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionChoice;
+import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.io.UTFInputStreamReader;
 import org.openstreetmap.josm.tools.GBC;
@@ -492,12 +491,6 @@
 
     private boolean isProjSupported(String crs) {
-        for (Projection proj : Projections.getProjections()) {
-            if (proj instanceof ProjectionSubPrefs) {
-                if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null)
-                    return true;
-            } else {
-                if (proj.toCode().equals(crs))
-                    return true;
-            }
+        for (ProjectionChoice pc : ProjectionPreference.getProjectionChoices()) {
+            if (pc.getPreferencesFromCode(crs) != null) return true;
         }
         return false;
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/AbstractProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/AbstractProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/AbstractProjectionChoice.java	(revision 5234)
@@ -0,0 +1,22 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+abstract public class AbstractProjectionChoice implements ProjectionChoice {
+    private String id;
+    private String name;
+
+    public AbstractProjectionChoice(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+    @Override
+    public String getId() {
+        return id;
+    }
+    
+    @Override
+    public String toString() {
+        return name;
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/Alias.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/Alias.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/Alias.java	(revision 5234)
@@ -0,0 +1,11 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+/**
+ * Provide an alias.
+ *
+ * This is used for migration and can be removed end 2012.
+ */
+public interface Alias {
+    String getAlias();
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CustomProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CustomProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CustomProjectionChoice.java	(revision 5234)
@@ -0,0 +1,240 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.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.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;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.plaf.basic.BasicComboBoxEditor;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.projection.CustomProjection;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionConfigurationException;
+import org.openstreetmap.josm.data.projection.Projections;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+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;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Utils;
+
+public class CustomProjectionChoice extends AbstractProjectionChoice implements Alias, SubPrefsOptions {
+
+    private String pref;
+
+    public CustomProjectionChoice() {
+        super("core:custom", tr("Custom Projection"));
+    }
+
+    private static class PreferencePanel extends JPanel {
+
+        public JTextField input;
+        private HistoryComboBox cbInput;
+
+        public PreferencePanel(String initialText, ActionListener listener) {
+            build(initialText, listener);
+        }
+
+        private void build(String initialText, 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(initialText == null ? "" : initialText);
+
+            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."));
+                    }
+                    listener.actionPerformed(null);
+                }
+
+                @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));
+
+            JButton btnInfo = new JButton(tr("Parameter information..."));
+            btnInfo.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    CustomProjectionChoice.ParameterInfoDialog dlg = new CustomProjectionChoice.ParameterInfoDialog();
+                    dlg.showDialog();
+                    dlg.toFront();
+                }
+            });
+
+            this.setLayout(new GridBagLayout());
+            JPanel p2 = new JPanel(new GridBagLayout());
+            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));
+            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));
+            this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+            p2 = new JPanel(new GridBagLayout());
+            p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0));
+            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());
+        }
+    }
+
+    public static class ParameterInfoDialog extends ExtendedDialog {
+
+        public ParameterInfoDialog() {
+            super(null, tr("Parameter information"), new String[] { tr("Close") }, false);
+            setContent(build());
+        }
+
+        private JComponent build() {
+            StringBuilder s = new StringBuilder();
+            s.append("<b>+proj=...</b> - <i>"+tr("Projection name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.projs)+"<br>");
+            s.append("<b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>"+tr("Projection parameters")+"</i><br>");
+            s.append("<b>+x_0=..., +y_0=...</b> - <i>"+tr("False easting and false northing")+"</i><br>");
+            s.append("<b>+lon_0=...</b> - <i>"+tr("Central meridian")+"</i><br>");
+            s.append("<b>+k_0=...</b> - <i>"+tr("Scaling factor")+"</i><br>");
+            s.append("<b>+ellps=...</b> - <i>"+tr("Ellipsoid name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.ellipsoids)+"<br>");
+            s.append("<b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>"+tr("Ellipsoid parameters")+"</i><br>");
+            s.append("<b>+datum=...</b> - <i>"+tr("Datum name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.datums)+"<br>");
+            s.append("<b>+towgs84=...</b> - <i>"+tr("3 or 7 term datum transform parameters")+"</i><br>");
+            s.append("<b>+nadgrids=...</b> - <i>"+tr("NTv2 grid file")+"</i><br>");
+            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());
+            return info;
+        }
+
+        private String listKeys(Map<String, ?> map) {
+            List<String> keys = new ArrayList<String>(map.keySet());
+            Collections.sort(keys);
+            return Utils.join(", ", keys);
+        }
+    }
+
+    @Override
+    public void setPreferences(Collection<String> args) {
+        if (args != null && !args.isEmpty()) {
+            pref = args.iterator().next();
+        }
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new CustomProjection(pref);
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new PreferencePanel(pref, listener);
+    }
+
+    @Override
+    public Collection<String> getPreferences(JPanel panel) {
+        PreferencePanel prefPanel = (PreferencePanel) panel;
+        String pref = prefPanel.input.getText();
+        prefPanel.rememberHistory();
+        return Collections.singleton(pref);
+    }
+
+    @Override
+    public String[] allCodes() {
+        return new String[0];
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        return null;
+    }
+
+    @Override
+    public String getAlias() {
+        return "org.openstreetmap.josm.data.projection.CustomProjectionPrefGui";
+    }
+
+    @Override
+    public boolean showProjectionCode() {
+        return false;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/GaussKruegerProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/GaussKruegerProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/GaussKruegerProjectionChoice.java	(revision 5234)
@@ -0,0 +1,61 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.projection.GaussKrueger;
+import org.openstreetmap.josm.data.projection.Projection;
+
+public class GaussKruegerProjectionChoice extends ListProjectionChoice implements Alias {
+
+    private static String[] zones = { "2", "3", "4", "5" };
+
+    public GaussKruegerProjectionChoice() {
+        super("core:gauss-krueger", tr("Gau\u00DF-Kr\u00FCger"), zones, tr("GK Zone"));
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new GaussKrueger(indexToZone(index));
+    }
+
+    @Override
+    protected int indexToZone(int index) {
+        return index + 2;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone - 2;
+    }
+
+    @Override
+    public String[] allCodes() {
+        String[] codes = new String[4];
+        for (int zone = 2; zone <= 5; zone++) {
+            codes[zone-2] = "EPSG:" + (31464 + zone);
+        }
+        return codes;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code)
+    {
+        //zone 2 = EPSG:31466 up to zone 5 = EPSG:31469
+        for (int zone = 2; zone <= 5; zone++) {
+            String epsg = "EPSG:" + (31464 + zone);
+            if (epsg.equals(code))
+                return Collections.singleton(String.valueOf(zone));
+        }
+        return null;
+    }
+    
+    @Override
+    public String getAlias() {
+        return GaussKrueger.class.getName();
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertCC9ZonesProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertCC9ZonesProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertCC9ZonesProjectionChoice.java	(revision 5234)
@@ -0,0 +1,92 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class LambertCC9ZonesProjectionChoice extends ListProjectionChoice implements Alias {
+
+    private static String[] lambert9zones = {
+        tr("{0} ({1} to {2} degrees)", 1,41,43),
+        tr("{0} ({1} to {2} degrees)", 2,42,44),
+        tr("{0} ({1} to {2} degrees)", 3,43,45),
+        tr("{0} ({1} to {2} degrees)", 4,44,46),
+        tr("{0} ({1} to {2} degrees)", 5,45,47),
+        tr("{0} ({1} to {2} degrees)", 6,46,48),
+        tr("{0} ({1} to {2} degrees)", 7,47,49),
+        tr("{0} ({1} to {2} degrees)", 8,48,50),
+        tr("{0} ({1} to {2} degrees)", 9,49,51)
+    };
+
+    public LambertCC9ZonesProjectionChoice() {
+        super("core:lambertcc9", tr("Lambert CC9 Zone (France)"), lambert9zones, tr("Lambert CC Zone"));
+    }
+
+    private class LambertCC9CBPanel extends CBPanel {
+        public LambertCC9CBPanel(Object[] entries, int initialIndex, String label, ActionListener listener) {
+            super(entries, initialIndex, label, listener);
+            this.add(new JLabel(ImageProvider.get("data/projection", "LambertCC9Zones.png")), GBC.eol().fill(GBC.HORIZONTAL));
+            this.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+        }
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new LambertCC9CBPanel(entries, index, label, listener);
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new LambertCC9Zones(index);
+    }
+
+    @Override
+    public String[] allCodes() {
+        String[] codes = new String[9];
+        for (int zone = 0; zone < 9; zone++) {
+            codes[zone] = "EPSG:" + (3942 + zone);
+        }
+        return codes;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        //zone 1=CC42=EPSG:3942 up to zone 9=CC50=EPSG:3950
+        if (code.startsWith("EPSG:39") && code.length() == 9) {
+            try {
+                String zonestring = code.substring(5,9);
+                int zoneval = Integer.parseInt(zonestring)-3942;
+                if(zoneval >= 0 && zoneval <= 8)
+                    return Collections.singleton(String.valueOf(zoneval+1));
+            } catch(NumberFormatException ex) {}
+        }
+        return null;
+    }
+    
+    @Override
+    protected int indexToZone(int index) {
+        return index + 1;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone - 1;
+    }
+
+    @Override
+    public String getAlias() {
+        return LambertCC9Zones.class.getName();
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/LambertProjectionChoice.java	(revision 5234)
@@ -0,0 +1,86 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class LambertProjectionChoice extends ListProjectionChoice implements Alias {
+
+    public static String[] lambert4zones = {
+        tr("{0} ({1} to {2} degrees)", 1,"51.30","48.15"),
+        tr("{0} ({1} to {2} degrees)", 2,"48.15","45.45"),
+        tr("{0} ({1} to {2} degrees)", 3,"45.45","42.76"),
+        tr("{0} (Corsica)", 4)
+    };
+
+    public LambertProjectionChoice() {
+        super("core:lambert", tr("Lambert 4 Zones (France)"), lambert4zones, tr("Lambert CC Zone"));
+    }
+
+    private class LambertCBPanel extends CBPanel {
+        public LambertCBPanel(Object[] entries, int initialIndex, String label, ActionListener listener) {
+            super(entries, initialIndex, label, listener);
+            this.add(new JLabel(ImageProvider.get("data/projection", "Departements_Lambert4Zones.png")), GBC.eol().fill(GBC.HORIZONTAL));
+            this.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+        }
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new LambertCBPanel(entries, index, label, listener);
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new Lambert(index);
+    }
+
+    @Override
+    public String[] allCodes() {
+        String[] codes = new String[4];
+        for (int zone = 0; zone < 4; zone++) {
+            codes[zone] = "EPSG:"+(27561+zone);
+        }
+        return codes;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        if (code.startsWith("EPSG:2756") && code.length() == 10) {
+            try {
+                String zonestring = code.substring(9);
+                int zoneval = Integer.parseInt(zonestring);
+                if(zoneval >= 1 && zoneval <= 4)
+                    return Collections.singleton(zonestring);
+            } catch(NumberFormatException e) {}
+        }
+        return null;
+    }
+
+    @Override
+    protected int indexToZone(int index) {
+        return index + 1;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone - 1;
+    }
+
+    @Override
+    public String getAlias() {
+        return Lambert.class.getName();
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ListProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ListProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ListProjectionChoice.java	(revision 5234)
@@ -0,0 +1,106 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * A projection choice, that offers a list of projections in a combo-box.
+ */
+abstract public class ListProjectionChoice extends AbstractProjectionChoice {
+
+    protected int index;
+    protected int defaultIndex;
+    protected Object[] entries;
+    protected String label;
+
+    public ListProjectionChoice(String id, String name, Object[] entries, String label) {
+        this(id, name, entries, label, 0);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param id the unique id for this ProjectionChoice
+     * @param name the display name
+     * @param entries the list of display entries for the combo-box
+     * @param label a label shown left to the combo-box
+     * @param defaultIndex the default index for the combo-box
+     */
+    public ListProjectionChoice(String id, String name, Object[] entries, String label, int defaultIndex) {
+        super(id, name);
+        this.entries = entries;
+        this.label = label;
+        this.defaultIndex = defaultIndex;
+    }
+
+    /**
+     * Convert 0-based index to preference value.
+     */
+    abstract protected int indexToZone(int index);
+
+    /**
+     * Convert preference value to 0-based index.
+     */
+    abstract protected int zoneToIndex(int zone);
+
+    @Override
+    public void setPreferences(Collection<String> args) {
+        Integer zone = null;
+        if (args != null && args.size() >= 1) {
+            try {
+                zone = Integer.parseInt(args.iterator().next());
+            } catch(NumberFormatException e) {}
+        }
+        int index;
+        if (zone == null) {
+            index = defaultIndex;
+        } else {
+            index = zoneToIndex(zone);
+            if (index < 0 || index >= entries.length) {
+                index = defaultIndex;
+            }
+        }
+        this.index = index;
+    }
+
+    protected class CBPanel extends JPanel {
+        public JComboBox prefcb;
+
+        public CBPanel(Object[] entries, int initialIndex, String label, final ActionListener listener) {
+            prefcb = new JComboBox(entries);
+
+            prefcb.setSelectedIndex(initialIndex);
+            this.setLayout(new GridBagLayout());
+            this.add(new JLabel(label), GBC.std().insets(5,5,0,5));
+            this.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+            this.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
+            this.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+
+            if (listener != null) {
+                prefcb.addActionListener(listener);
+            }
+        }
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new CBPanel(entries, index, label, listener);
+    }
+
+    @Override
+    public Collection<String> getPreferences(JPanel panel) {
+        CBPanel p = (CBPanel) panel;
+        int index = p.prefcb.getSelectedIndex();
+        return Collections.singleton(Integer.toString(indexToZone(index)));
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionChoice.java	(revision 5234)
@@ -0,0 +1,71 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import java.awt.event.ActionListener;
+import java.util.Collection;
+
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.projection.Projection;
+
+/**
+ * This class offers a choice of projections to the user.
+ *
+ * It can display a GUI panel, in order to select the parameters.
+ */
+public interface ProjectionChoice {
+
+    /**
+     * Get a unique id for the projection choice.
+     */
+    String getId();
+
+    /**
+     * Set the internal state to match the preferences.
+     *
+     * Will be called before getPreferencePanel and when the
+     * listener from getPreferencePanel is invoked.
+     *
+     * Argument may be null to reset everything.
+     */
+    void setPreferences(Collection<String> args);
+
+    /**
+     * Get the projection that matches the internal state.
+     */
+    Projection getProjection();
+
+    /**
+     * Generate and provide the GUI.
+     *
+     * It will be displayed to the user. Call the listener, when the user makes
+     * changes in the GUI, so the projection info in the top panel gets updated.
+     *
+     * @param listener   listener for any change of preferences
+     * @return the GUI panel
+     */
+    JPanel getPreferencePanel(ActionListener listener);
+
+    /**
+     * Extract preferences from the GUI.
+     *
+     * Will be called when the preference dialog is dismissed or
+     * when the listener from getPreferencePanel is invoked.
+     */
+    Collection<String> getPreferences(JPanel panel);
+
+    /**
+     * Return all projection codes supported by this projection class.
+     */
+    String[] allCodes();
+
+    /**
+     * Get Preferences from projection code.
+     * 
+     * @return null when code is not part of this projection choice.
+     * An empty Collection as return value indicates, that the code is supported,
+     * but no preferences are required to set it up.
+     */
+    Collection<String> getPreferencesFromCode(String code);
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 5234)
@@ -10,4 +10,8 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import javax.swing.BorderFactory;
@@ -23,10 +27,14 @@
 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.BelgianLambert1972;
+import org.openstreetmap.josm.data.projection.BelgianLambert2008;
+import org.openstreetmap.josm.data.projection.Epsg3008;
+import org.openstreetmap.josm.data.projection.Epsg4326;
+import org.openstreetmap.josm.data.projection.Lambert93;
+import org.openstreetmap.josm.data.projection.LambertEST;
 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.data.projection.TransverseMercatorLV;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
@@ -35,5 +43,4 @@
 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;
 
@@ -46,15 +53,52 @@
     }
 
-    private static final StringProperty PROP_PROJECTION = new StringProperty("projection", Mercator.class.getName());
+    private static List<ProjectionChoice> projectionChoices = new ArrayList<ProjectionChoice>();
+    private static Map<String, ProjectionChoice> projectionChoicesById = new HashMap<String, ProjectionChoice>();
+    private static Map<String, String> aliasNormalizer = new HashMap<String, String>();
+
+    public static ProjectionChoice mercator = new SingleProjectionChoice("core:mercator", new Mercator());
+    static {
+        // global projections
+        registerProjectionChoice("core:wgs84", new Epsg4326());
+        registerProjectionChoice(mercator);
+        registerProjectionChoice(new UTMProjectionChoice());
+        // regional - alphabetical order by country code
+        registerProjectionChoice("core:belambert1972", new BelgianLambert1972());   // BE
+        registerProjectionChoice("core:belambert2008", new BelgianLambert2008());   // BE
+        registerProjectionChoice(new SwissGridProjectionChoice());                  // CH
+        registerProjectionChoice(new GaussKruegerProjectionChoice());               // DE
+        registerProjectionChoice("core:lambertest", new LambertEST());              // EE
+        registerProjectionChoice(new LambertProjectionChoice());                    // FR
+        registerProjectionChoice("core:lambert93", new Lambert93());                // FR
+        registerProjectionChoice(new LambertCC9ZonesProjectionChoice());            // FR
+        registerProjectionChoice(new UTM_France_DOM_ProjectionChoice());            // FR
+        registerProjectionChoice("core:tmerclv", new TransverseMercatorLV());       // LV
+        registerProjectionChoice(new PuwgProjectionChoice());                       // PL
+        registerProjectionChoice("core:sweref99", new Epsg3008());                  // SE
+        registerProjectionChoice(new CustomProjectionChoice());
+    }
+
+    public static void registerProjectionChoice(ProjectionChoice c) {
+        projectionChoices.add(c);
+        projectionChoicesById.put(c.getId(), c);
+        aliasNormalizer.put(c.getId(), c.getId());
+        if (c instanceof Alias) {
+            String alias = ((Alias) c).getAlias();
+            projectionChoicesById.put(alias, c);
+            aliasNormalizer.put(alias, c.getId());
+        }
+    }
+
+    public static void registerProjectionChoice(String id, Projection projection) {
+        registerProjectionChoice(new SingleProjectionChoice(id, projection));
+    }
+
+    public static List<ProjectionChoice> getProjectionChoices() {
+        return Collections.unmodifiableList(projectionChoices);
+    }
+
+    private static final StringProperty PROP_PROJECTION = new StringProperty("projection", mercator.getId());
     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]);
@@ -69,5 +113,5 @@
      * Combobox with all projections available
      */
-    private JComboBox projectionCombo = new JComboBox(Projections.getProjections().toArray());
+    private JComboBox projectionCombo = new JComboBox(projectionChoices.toArray());
 
     /**
@@ -104,5 +148,5 @@
 
     public void addGui(PreferenceTabbedPane gui) {
-        setupProjectionCombo();
+        ProjectionChoice pc = setupProjectionCombo();
 
         for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
@@ -131,5 +175,4 @@
         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));
 
@@ -146,30 +189,31 @@
         gui.getMapPreference().mapcontent.addTab(tr("Map Projection"), scrollpane);
 
-        updateMeta(Main.getProjection());
-    }
-
-    private void updateMeta(Projection proj)
-    {
+        selectedProjectionChanged(pc);
+    }
+
+    private void updateMeta(ProjectionChoice pc) {
+        pc.setPreferences(pc.getPreferences(projSubPrefPanel));
+        Projection proj = pc.getProjection();
         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);
-    }
-
+        boolean showCode = true;
+        if (pc instanceof SubPrefsOptions) {
+            showCode = ((SubPrefsOptions) pc).showProjectionCode();
+        }
+        projectionCodeLabel.setVisible(showCode);
+        projectionCodeGlue.setVisible(showCode);
+        projectionCode.setVisible(showCode);
+    }
+
+    @Override
     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);
+        ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
+
+        String id = pc.getId();
+        Collection<String> prefs = pc.getPreferences(projSubPrefPanel);
+
+        setProjection(id, prefs);
 
         if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) {
@@ -183,63 +227,28 @@
     }
 
-    static public void setProjection()
-    {
+    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) {
+    static public void setProjection(String id, Collection<String> pref) {
+        ProjectionChoice pc = projectionChoicesById.get(id);
+
+        if (pc == null) {
             JOptionPane.showMessageDialog(
                     Main.parent,
-                    tr("The projection {0} could not be activated. Using Mercator", name),
+                    tr("The projection {0} could not be activated. Using Mercator", id),
                     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();
+            pref = null;
+            pc = mercator;
+        }
+        id = pc.getId();
+        PROP_PROJECTION.put(id);
+        PROP_SUB_PROJECTION.put(pref);
+        Main.pref.putCollection("projection.sub."+id, pref);
+        pc.setPreferences(pref);
+        Projection proj = pc.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);
-        }
     }
 
@@ -249,13 +258,5 @@
      * @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);
-        }
-
+    private void selectedProjectionChanged(final ProjectionChoice pc) {
         // Don't try to update if we're still starting up
         int size = projPanel.getComponentCount();
@@ -263,10 +264,18 @@
             return;
 
+        final ActionListener listener = new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                updateMeta(pc);
+            }
+        };
+
         // Replace old panel with new one
         projSubPrefPanelWrapper.removeAll();
+        projSubPrefPanel = pc.getPreferencePanel(listener);
         projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
         projPanel.revalidate();
         projSubPrefPanel.repaint();
-        updateMeta(proj);
+        updateMeta(pc);
     }
 
@@ -274,29 +283,39 @@
      * Sets up projection combobox with default values and action listener
      */
-    private void setupProjectionCombo() {
-        boolean found = false;
+    private ProjectionChoice setupProjectionCombo() {
+        ProjectionChoice pc = null;
         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())) {
+            ProjectionChoice pc1 = (ProjectionChoice) projectionCombo.getItemAt(i);
+            pc1.setPreferences(getSubprojectionPreference(pc1));
+            if (pc1.getId().equals(aliasNormalizer.get(PROP_PROJECTION.get()))) {
                 projectionCombo.setSelectedIndex(i);
-                selectedProjectionChanged(proj);
-                found = true;
-                break;
-            }
-        }
-        if (!found)
+                selectedProjectionChanged(pc1);
+                pc = pc1;
+            }
+        }
+        // If the ProjectionChoice from the preferences is not available, it
+        // should have been set to Mercator at JOSM start.
+        if (pc == null)
             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);
+                ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
+                selectedProjectionChanged(pc);
             }
         });
+        return pc;
+    }
+
+    private Collection<String> getSubprojectionPreference(ProjectionChoice pc) {
+        Collection<String> c1 = Main.pref.getCollection("projection.sub."+pc.getId(), null);
+        if (c1 != null)
+            return c1;
+        if (pc instanceof Alias) {
+            String alias = ((Alias) pc).getAlias();
+            String sname = alias.substring(alias.lastIndexOf(".")+1);
+            return Main.pref.getCollection("projection.sub."+sname, null);
+        }
+        return null;
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/PuwgProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/PuwgProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/PuwgProjectionChoice.java	(revision 5234)
@@ -0,0 +1,56 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.Puwg;
+
+public class PuwgProjectionChoice extends ListProjectionChoice implements Alias {
+
+    public PuwgProjectionChoice() {
+        super("core:puwg", tr("PUWG (Poland)"), Puwg.Zones, tr("PUWG Zone"));
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new Puwg(index);
+    }
+
+    @Override
+    public String[] allCodes() {
+        String[] zones = new String[Puwg.Zones.length];
+        for (int zone = 0; zone < Puwg.Zones.length; zone++) {
+            zones[zone] = Puwg.Zones[zone].toCode();
+        }
+        return zones;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        for (Puwg.PuwgData p : Puwg.Zones) {
+            if (code.equals(p.toCode()))
+                return Collections.singleton(code);
+        }
+        return null;
+    }
+
+    @Override
+    public String getAlias() {
+        return Puwg.class.getName();
+    }
+
+    @Override
+    protected int indexToZone(int index) {
+        return index;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone;
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SingleProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SingleProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SingleProjectionChoice.java	(revision 5234)
@@ -0,0 +1,79 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import java.awt.event.ActionListener;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.projection.Projection;
+
+/**
+ * ProjectionChoice, that offers just one projection as choice.
+ *
+ * The GUI is an empty panel.
+ */
+public class SingleProjectionChoice implements ProjectionChoice, Alias {
+
+    private String id;
+    private String name;
+    private Projection projection;
+
+    public SingleProjectionChoice(String id, String name, Projection projection) {
+        this.id = id;
+        this.name = name;
+        this.projection = projection;
+    }
+
+    public SingleProjectionChoice(String id, Projection projection) {
+        this(id, projection.toString(), projection);
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new JPanel();
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String[] allCodes() {
+        return new String[] { projection.toCode() };
+    }
+
+    @Override
+    public void setPreferences(Collection<String> args) {
+    }
+
+    @Override
+    public Collection<String> getPreferences(JPanel p) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Projection getProjection() {
+        return projection;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        if (code.equals(projection.toCode()))
+            return Collections.emptyList();
+        else
+            return null;
+    }
+
+    @Override
+    public String getAlias() {
+        return projection.getClass().getName();
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java	(revision 5233)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SubPrefsOptions.java	(revision 5234)
@@ -3,5 +3,5 @@
 
 /**
- * ProjectionSubPrefs can implement this interface to set some additional options
+ * ProjectionChoice can implement this interface to set some additional options
  */
 public interface SubPrefsOptions {
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SwissGridProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SwissGridProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/SwissGridProjectionChoice.java	(revision 5234)
@@ -0,0 +1,29 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+
+import javax.swing.Box;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.projection.SwissGrid;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.tools.GBC;
+
+public class SwissGridProjectionChoice extends SingleProjectionChoice {
+
+    public SwissGridProjectionChoice() {
+        super("core:swissgrid", tr("Swiss Grid (Switzerland)"), new SwissGrid());
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        JPanel p = new JPanel(new GridBagLayout());
+        p.add(new HtmlPanel(tr("<i>CH1903 / LV03 (without local corrections)</i>")), GBC.eol().fill(GBC.HORIZONTAL));
+        p.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
+        return p;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTMProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTMProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTMProjectionChoice.java	(revision 5234)
@@ -0,0 +1,187 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.UTM;
+import org.openstreetmap.josm.tools.GBC;
+
+public class UTMProjectionChoice extends ListProjectionChoice implements Alias {
+
+    private static final UTM.Hemisphere DEFAULT_HEMISPHERE = UTM.Hemisphere.North;
+
+    private UTM.Hemisphere hemisphere;
+    
+    /**
+     * Applies an additional false easting of 3000000 m if true.
+     */
+    private boolean offset;
+
+    private final static List<String> cbEntries = new ArrayList<String>();
+    static {
+        for (int i = 1; i <= 60; i++) {
+                cbEntries.add(Integer.toString(i));
+        }
+    }
+
+    public UTMProjectionChoice() {
+        super("core:utm", tr("UTM"), cbEntries.toArray(), tr("UTM Zone"));
+    }
+
+    private class UTMPanel extends CBPanel {
+
+        public JRadioButton north, south;
+        public JCheckBox offsetBox;
+
+        public UTMPanel(Object[] entries, int initialIndex, String label, ActionListener listener) {
+            super(entries, initialIndex, label, listener);
+
+            //Hemisphere
+            north = new JRadioButton();
+            north.setSelected(hemisphere == UTM.Hemisphere.North);
+            south = new JRadioButton();
+            south.setSelected(hemisphere == UTM.Hemisphere.South);
+
+            ButtonGroup group = new ButtonGroup();
+            group.add(north);
+            group.add(south);
+
+            JPanel bPanel = new JPanel();
+            bPanel.setLayout(new GridBagLayout());
+
+            bPanel.add(new JLabel(tr("North")), GBC.std().insets(5, 5, 0, 5));
+            bPanel.add(north, GBC.std().fill(GBC.HORIZONTAL));
+            bPanel.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+            bPanel.add(new JLabel(tr("South")), GBC.std().insets(5, 5, 0, 5));
+            bPanel.add(south, GBC.std().fill(GBC.HORIZONTAL));
+            bPanel.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+
+            this.add(new JLabel(tr("Hemisphere")), GBC.std().insets(5,5,0,5));
+            this.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+            this.add(bPanel, GBC.eop().fill(GBC.HORIZONTAL));
+            this.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+
+            //Offset
+            offsetBox = new JCheckBox();
+            offsetBox.setSelected(offset);
+
+            this.add(new JLabel(tr("Offset 3.000.000m east")), GBC.std().insets(5,5,0,5));
+            this.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+            this.add(offsetBox, GBC.eop().fill(GBC.HORIZONTAL));
+            this.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+
+            if (listener != null) {
+                north.addActionListener(listener);
+                south.addActionListener(listener);
+                offsetBox.addActionListener(listener);
+            }
+        }
+    }
+
+    @Override
+    public JPanel getPreferencePanel(ActionListener listener) {
+        return new UTMPanel(entries, index, label, listener);
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new UTM(indexToZone(index), hemisphere, offset);
+    }
+
+    @Override
+    public Collection<String> getPreferences(JPanel panel) {
+        UTMPanel p = (UTMPanel) panel;
+        int index = p.prefcb.getSelectedIndex();
+        UTM.Hemisphere hemisphere = p.south.isSelected()?UTM.Hemisphere.South:UTM.Hemisphere.North;
+        boolean offset = p.offsetBox.isSelected();
+        return Arrays.asList(Integer.toString(indexToZone(index)), hemisphere.toString(), (offset?"offset":"standard"));
+    }
+
+    @Override
+    public String[] allCodes() {
+        ArrayList<String> projections = new ArrayList<String>(60*4);
+        for (int zone = 1;zone <= 60; zone++) {
+            for (boolean offset : new boolean[] { false, true }) {
+                for (UTM.Hemisphere hemisphere : UTM.Hemisphere.values()) {
+                    projections.add("EPSG:" + ((offset?325800:32600) + zone + (hemisphere == UTM.Hemisphere.South?100:0)));
+                }
+            }
+        }
+        return projections.toArray(new String[0]);
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        boolean offset = code.startsWith("EPSG:3258") || code.startsWith("EPSG:3259");
+
+        if (code.startsWith("EPSG:326") || code.startsWith("EPSG:327") || offset) {
+            try {
+                UTM.Hemisphere hemisphere;
+                String zonestring;
+                if (offset) {
+                    hemisphere = code.charAt(8)=='8'?UTM.Hemisphere.North:UTM.Hemisphere.South;
+                    zonestring = code.substring(9);
+                } else {
+                    hemisphere = code.charAt(7)=='6'?UTM.Hemisphere.North:UTM.Hemisphere.South;
+                    zonestring = code.substring(8);
+                }
+
+                int zoneval = Integer.parseInt(zonestring);
+                if(zoneval > 0 && zoneval <= 60)
+                    return Arrays.asList(zonestring, hemisphere.toString(), (offset?"offset":"standard"));
+            } catch(NumberFormatException e) {}
+        }
+        return null;
+    }
+
+    @Override
+    public void setPreferences(Collection<String> args) {
+        super.setPreferences(args);
+        UTM.Hemisphere hemisphere = DEFAULT_HEMISPHERE;
+        boolean offset = false;
+
+        if(args != null) {
+            String[] array = args.toArray(new String[0]);
+
+            if (array.length > 1) {
+                hemisphere = UTM.Hemisphere.valueOf(array[1]);
+            }
+
+            if (array.length > 2) {
+                offset = array[2].equals("offset");
+            }
+        }
+        this.hemisphere = hemisphere;
+        this.offset = offset;
+    }
+
+    @Override
+    protected int indexToZone(int index) {
+        return index + 1;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone - 1;
+    }
+
+    @Override
+    public String getAlias() {
+        return UTM.class.getName();
+    }
+
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTM_France_DOM_ProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTM_France_DOM_ProjectionChoice.java	(revision 5234)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/UTM_France_DOM_ProjectionChoice.java	(revision 5234)
@@ -0,0 +1,62 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.UTM_France_DOM;
+
+public class UTM_France_DOM_ProjectionChoice extends ListProjectionChoice implements Alias {
+
+    private final static String FortMarigotName = tr("Guadeloupe Fort-Marigot 1949");
+    private final static String SainteAnneName = tr("Guadeloupe Ste-Anne 1948");
+    private final static String MartiniqueName = tr("Martinique Fort Desaix 1952");
+    private final static String Reunion92Name = tr("Reunion RGR92");
+    private final static String Guyane92Name = tr("Guyane RGFG95");
+    private final static String[] utmGeodesicsNames = { FortMarigotName, SainteAnneName, MartiniqueName, Reunion92Name, Guyane92Name};
+
+    public UTM_France_DOM_ProjectionChoice() {
+        super("core:utmfrancedom", tr("UTM France (DOM)"), utmGeodesicsNames, tr("UTM Geodesic system"));
+    }
+
+    @Override
+    protected int indexToZone(int index) {
+        return index + 1;
+    }
+
+    @Override
+    protected int zoneToIndex(int zone) {
+        return zone - 1;
+    }
+
+    @Override
+    public Projection getProjection() {
+        return new UTM_France_DOM(index);
+    }
+
+    @Override
+    public String[] allCodes() {
+        String[] res = new String[UTM_France_DOM.utmEPSGs.length];
+        for (int i=0; i<UTM_France_DOM.utmEPSGs.length; ++i) {
+            res[i] = "EPSG:"+UTM_France_DOM.utmEPSGs[i];
+        }
+        return res;
+    }
+
+    @Override
+    public Collection<String> getPreferencesFromCode(String code) {
+        for (int i=0; i < UTM_France_DOM.utmEPSGs.length; i++ )
+            if (("EPSG:"+UTM_France_DOM.utmEPSGs[i]).equals(code))
+                return Collections.singleton(Integer.toString(i+1));
+        return null;
+    }
+
+    @Override
+    public String getAlias() {
+        return UTM_France_DOM.class.getName();
+    }
+
+}
