Index: /trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java	(revision 3796)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java	(revision 3797)
@@ -24,9 +24,9 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
-import org.openstreetmap.josm.gui.preferences.StyleSourceEditor.StyleSourceInfo;
+import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
 import org.openstreetmap.josm.tools.GBC;
 
 public class MapPaintPreference implements PreferenceSetting {
-    private StyleSourceEditor sources;
+    private SourceEditor sources;
     private JCheckBox enableIconDefault;
     private JCheckBox enableDefault;
@@ -81,5 +81,5 @@
                     public void stateChanged(ChangeEvent e) {
                         if (gui.mapcontent.getSelectedComponent() == panel) {
-                            sources.initiallyLoadAvailableStyles();
+                            sources.initiallyLoadAvailableSources();
                         }
                     }
@@ -88,5 +88,5 @@
     }
 
-    class MapPaintSourceEditor extends StyleSourceEditor {
+    class MapPaintSourceEditor extends SourceEditor {
 
         final private String iconpref = "mappaint.icon.sources";
@@ -103,5 +103,5 @@
         @Override
         public boolean finish() {
-            List<SourceEntry> activeStyles = activeStylesModel.getStyles();
+            List<SourceEntry> activeStyles = activeSourcesModel.getSources();
 
             boolean changed = (new MapPaintPrefMigration()).put(activeStyles);
@@ -122,5 +122,5 @@
 
         @Override
-        public Collection<StyleSourceInfo> getDefault() {
+        public Collection<ExtendedSourceEntry> getDefault() {
             return (new MapPaintPrefMigration()).getDefault();
         }
@@ -138,4 +138,6 @@
                 case ACTIVE_SOURCES:
                     return tr("Active styles:");
+                case NEW_SOURCE_ENTRY_TOOLTIP:
+                     return tr("Add a new style by entering filename or URL");
                 case NEW_SOURCE_ENTRY:
                     return tr("New style entry:");
@@ -194,5 +196,5 @@
     }
 
-    public static class MapPaintPrefMigration extends StyleSourceEditor.SourcePrefMigration {
+    public static class MapPaintPrefMigration extends SourceEditor.SourcePrefMigration {
 
         public MapPaintPrefMigration() {
@@ -203,6 +205,6 @@
 
         @Override
-        public Collection<StyleSourceInfo> getDefault() {
-            StyleSourceInfo i = new StyleSourceInfo("elemstyles.xml", "resource://data/elemstyles.xml");
+        public Collection<ExtendedSourceEntry> getDefault() {
+            ExtendedSourceEntry i = new ExtendedSourceEntry("elemstyles.xml", "resource://data/elemstyles.xml");
             i.name = "standard";
             i.shortdescription = tr("Internal Style");
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 3797)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 3797)
@@ -0,0 +1,1396 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.gui.preferences;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.Utils.equal;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventObject;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.DefaultListModel;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.io.MirroredInputStream;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.LanguageInfo;
+import org.xml.sax.SAXException;
+
+public abstract class SourceEditor extends JPanel {
+
+    protected JTable tblActiveSources;
+    protected ActiveSourcesModel activeSourcesModel;
+    protected JList lstAvailableSources;
+    protected AvailableSourcesListModel availableSourcesModel;
+    protected JTable tblIconPaths = null;
+    protected IconPathTableModel iconPathsModel;
+    protected boolean sourcesInitiallyLoaded;
+    protected String availableSourcesUrl;
+
+    /**
+     * constructor
+     * @param availableSourcesUrl the URL to the list of available sources
+     */
+    public SourceEditor(final String availableSourcesUrl) {
+
+        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
+        lstAvailableSources = new JList(availableSourcesModel = new AvailableSourcesListModel(selectionModel));
+        lstAvailableSources.setSelectionModel(selectionModel);
+        lstAvailableSources.setCellRenderer(new SourceEntryListCellRenderer());
+        this.availableSourcesUrl = availableSourcesUrl;
+
+        selectionModel = new DefaultListSelectionModel();
+        tblActiveSources = new JTable(activeSourcesModel = new ActiveSourcesModel(selectionModel));
+        tblActiveSources.putClientProperty("terminateEditOnFocusLost", true);
+        tblActiveSources.setSelectionModel(selectionModel);
+        tblActiveSources.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        tblActiveSources.setShowGrid(false);
+        tblActiveSources.setIntercellSpacing(new Dimension(0, 0));
+        tblActiveSources.setTableHeader(null);
+        tblActiveSources.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+        SourceEntryTableCellRenderer sourceEntryRenderer = new SourceEntryTableCellRenderer();
+        tblActiveSources.getColumnModel().getColumn(0).setCellRenderer(sourceEntryRenderer);
+        activeSourcesModel.addTableModelListener(new TableModelListener() {
+            // Force swing to show horizontal scrollbars for the JTable
+            // Yes, this is a little ugly, but should work
+            @Override
+            public void tableChanged(TableModelEvent e) {
+                adjustColumnWidth(tblActiveSources, 0);
+            }
+        });
+        activeSourcesModel.setActiveSources(getInitialSourcesList());
+        
+        final EditActiveSourceAction editActiveSourceAction = new EditActiveSourceAction();
+        tblActiveSources.getSelectionModel().addListSelectionListener(editActiveSourceAction);
+        tblActiveSources.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    int row = tblActiveSources.rowAtPoint(e.getPoint());
+                    if (row < 0 || row >= tblActiveSources.getRowCount())
+                        return;
+                    editActiveSourceAction.actionPerformed(null);
+                }
+            }
+        });
+
+        RemoveActiveSourcesAction removeActiveSourcesAction = new RemoveActiveSourcesAction();
+        tblActiveSources.getSelectionModel().addListSelectionListener(removeActiveSourcesAction);
+        tblActiveSources.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
+        tblActiveSources.getActionMap().put("delete", removeActiveSourcesAction);
+
+        ActivateSourcesAction activateSourcesAction = new ActivateSourcesAction();
+        lstAvailableSources.addListSelectionListener(activateSourcesAction);
+        JButton activate = new JButton(activateSourcesAction);
+
+        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+        setLayout(new GridBagLayout());
+
+        GridBagConstraints gbc = new GridBagConstraints();
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.weightx = 0.5;
+        gbc.gridwidth = 2;
+        gbc.anchor = GBC.WEST;
+        gbc.insets = new Insets(5, 11, 0, 0);
+
+        add(new JLabel(getStr(I18nString.AVAILABLE_SOURCES)), gbc);
+
+        gbc.gridx = 2;
+        gbc.insets = new Insets(5, 0, 0, 6);
+
+        add(new JLabel(getStr(I18nString.ACTIVE_SOURCES)), gbc);
+
+        gbc.gridwidth = 1;
+        gbc.gridx = 0;
+        gbc.gridy++;
+        gbc.weighty = 0.8;
+        gbc.fill = GBC.BOTH;
+        gbc.anchor = GBC.CENTER;
+        gbc.insets = new Insets(0, 11, 0, 0);
+
+        JScrollPane sp1 = new JScrollPane(lstAvailableSources);
+        add(sp1, gbc);
+
+        gbc.gridx = 1;
+        gbc.weightx = 0.0;
+        gbc.fill = GBC.VERTICAL;
+        gbc.insets = new Insets(0, 0, 0, 0);
+
+        JToolBar middleTB = new JToolBar();
+        middleTB.setFloatable(false);
+        middleTB.setBorderPainted(false);
+        middleTB.setOpaque(false);
+        middleTB.add(Box.createHorizontalGlue());
+        middleTB.add(activate);
+        middleTB.add(Box.createHorizontalGlue());
+        add(middleTB, gbc);
+
+        gbc.gridx++;
+        gbc.weightx = 0.5;
+        gbc.fill = GBC.BOTH;
+
+        JScrollPane sp = new JScrollPane(tblActiveSources);
+        add(sp, gbc);
+        sp.setColumnHeaderView(null);
+
+        gbc.gridx++;
+        gbc.weightx = 0.0;
+        gbc.fill = GBC.VERTICAL;
+        gbc.insets = new Insets(0, 0, 0, 6);
+
+        JToolBar sideButtonTB = new JToolBar(JToolBar.VERTICAL);
+        sideButtonTB.setFloatable(false);
+        sideButtonTB.setBorderPainted(false);
+        sideButtonTB.setOpaque(false);
+        sideButtonTB.add(new NewActiveSourceAction());
+        sideButtonTB.add(editActiveSourceAction);
+        sideButtonTB.add(removeActiveSourcesAction);
+        add(sideButtonTB, gbc);
+
+        gbc.gridx = 0;
+        gbc.gridy++;
+        gbc.weighty = 0.0;
+        gbc.weightx = 0.5;
+        gbc.fill = GBC.HORIZONTAL;
+        gbc.anchor = GBC.WEST;
+        gbc.insets = new Insets(0, 11, 0, 0);
+
+        JToolBar bottomLeftTB = new JToolBar(JToolBar.VERTICAL);
+        bottomLeftTB.setFloatable(false);
+        bottomLeftTB.setBorderPainted(false);
+        bottomLeftTB.setOpaque(false);
+        bottomLeftTB.add(new ReloadSourcesAction(availableSourcesUrl));
+        middleTB.add(Box.createHorizontalGlue());
+        add(bottomLeftTB, gbc);
+
+        gbc.gridx = 2;
+        gbc.anchor = GBC.CENTER;
+        gbc.insets = new Insets(0, 0, 0, 0);
+
+        JToolBar bottomRightTB = new JToolBar();
+        bottomRightTB.setFloatable(false);
+        bottomRightTB.setBorderPainted(false);
+        bottomRightTB.setOpaque(false);
+        bottomRightTB.add(Box.createHorizontalGlue());
+        bottomRightTB.add(new JButton(new ResetAction()));
+        add(bottomRightTB, gbc);
+
+        /***
+         * Icon configuration
+         **/
+
+        selectionModel = new DefaultListSelectionModel();
+        tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
+        tblIconPaths.setSelectionModel(selectionModel);
+        tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+        tblIconPaths.setTableHeader(null);
+        tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(false));
+        tblIconPaths.setRowHeight(20);
+        tblIconPaths.putClientProperty("terminateEditOnFocusLost", true);
+        iconPathsModel.setIconPaths(getInitialIconPathsList());
+
+        EditIconPathAction editIconPathAction = new EditIconPathAction();
+        tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
+
+        RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
+        tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
+        tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
+        tblIconPaths.getActionMap().put("delete", removeIconPathAction);
+
+        gbc.gridx = 0;
+        gbc.gridy++;
+        gbc.weightx = 1.0;
+        gbc.gridwidth = GBC.REMAINDER;
+        gbc.insets = new Insets(8, 11, 8, 6);
+
+        add(new JSeparator(), gbc);
+
+        gbc.gridy++;
+        gbc.insets = new Insets(0, 11, 0, 6);
+
+        add(new JLabel(tr("Icon paths:")), gbc);
+
+        gbc.gridy++;
+        gbc.weighty = 0.2;
+        gbc.gridwidth = 3;
+        gbc.fill = GBC.BOTH;
+        gbc.insets = new Insets(0, 11, 0, 0);
+
+        add(sp = new JScrollPane(tblIconPaths), gbc);
+        sp.setColumnHeaderView(null);
+
+        gbc.gridx = 3;
+        gbc.gridwidth = 1;
+        gbc.weightx = 0.0;
+        gbc.fill = GBC.VERTICAL;
+        gbc.insets = new Insets(0, 0, 0, 6);
+
+        JToolBar sideButtonTBIcons = new JToolBar(JToolBar.VERTICAL);
+        sideButtonTBIcons.setFloatable(false);
+        sideButtonTBIcons.setBorderPainted(false);
+        sideButtonTBIcons.setOpaque(false);
+        sideButtonTBIcons.add(new NewIconPathAction());
+        sideButtonTBIcons.add(editIconPathAction);
+        sideButtonTBIcons.add(removeIconPathAction);
+        add(sideButtonTBIcons, gbc);
+    }
+
+    /**
+     * Load the list of source entries that the user has configured.
+     */
+    abstract public Collection<? extends SourceEntry> getInitialSourcesList();
+
+    /**
+     * Load the list of configured icon paths.
+     */
+    abstract public Collection<String> getInitialIconPathsList();
+
+    /**
+     * Get the default list of entries (used when resetting the list).
+     */
+    abstract public Collection<ExtendedSourceEntry> getDefault();
+
+    /**
+     * Save the settings after user clicked "Ok".
+     * @return true if restart is required
+     */
+    abstract public boolean finish();
+
+    /**
+     * Provide the GUI strings. (There are differences for MapPaint and Preset)
+     */
+    abstract protected String getStr(I18nString ident);
+
+    /**
+     * Identifiers for strings that need to be provided.
+     */
+    protected enum I18nString { AVAILABLE_SOURCES, ACTIVE_SOURCES, NEW_SOURCE_ENTRY_TOOLTIP, NEW_SOURCE_ENTRY,
+            REMOVE_SOURCE_TOOLTIP, EDIT_SOURCE_TOOLTIP, ACTIVATE_TOOLTIP, RELOAD_ALL_AVAILABLE,
+            LOADING_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC,
+            ILLEGAL_FORMAT_OF_ENTRY }
+
+    /**
+     * adjust the preferred width of column col to the maximum preferred width of the cells
+     * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+     */
+    private static void adjustColumnWidth(JTable tbl, int col) {
+        int maxwidth = 0;
+		for (int row=0; row<tbl.getRowCount(); row++) {
+            TableCellRenderer tcr = tbl.getCellRenderer(row, col);
+                Object val = tbl.getValueAt(row, col);
+                Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col);
+                maxwidth = Math.max(comp.getPreferredSize().width, maxwidth);
+		}
+		tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth);
+    }
+
+    public boolean hasActiveSourcesChanged() {
+        Collection<? extends SourceEntry> prev = getInitialSourcesList();
+        List<SourceEntry> cur = activeSourcesModel.getSources();
+        if (prev.size() != cur.size())
+            return true;
+        Iterator<? extends SourceEntry> p = prev.iterator();
+        Iterator<SourceEntry> c = cur.iterator();
+        while (p.hasNext()) {
+            SourceEntry pe = p.next();
+            SourceEntry ce = c.next();
+            if (!equal(pe.url, ce.url) || !equal(pe.name, ce.name) || pe.active != ce.active)
+                return true;
+        }
+        return false;
+    }
+
+    public Collection<SourceEntry> getActiveSources() {
+        return activeSourcesModel.getSources();
+    }
+
+    public void removeSources(Collection<Integer> idxs) {
+        activeSourcesModel.removeIdxs(idxs);
+    }
+
+    protected void reloadAvailableSources(String url) {
+        Main.worker.submit(new SourceLoader(url));
+    }
+
+    public void initiallyLoadAvailableSources() {
+        if (!sourcesInitiallyLoaded) {
+            reloadAvailableSources(this.availableSourcesUrl);
+        }
+        sourcesInitiallyLoaded = true;
+    }
+
+    protected static class AvailableSourcesListModel extends DefaultListModel {
+        private ArrayList<ExtendedSourceEntry> data;
+        private DefaultListSelectionModel selectionModel;
+
+        public AvailableSourcesListModel(DefaultListSelectionModel selectionModel) {
+            data = new ArrayList<ExtendedSourceEntry>();
+            this.selectionModel = selectionModel;
+        }
+
+        public void setSources(List<ExtendedSourceEntry> sources) {
+            data.clear();
+            if (sources != null) {
+                data.addAll(sources);
+            }
+            fireContentsChanged(this, 0, data.size());
+        }
+
+        @Override
+        public Object getElementAt(int index) {
+            return data.get(index);
+        }
+
+        @Override
+        public int getSize() {
+            if (data == null) return 0;
+            return data.size();
+        }
+
+        public void deleteSelected() {
+            Iterator<ExtendedSourceEntry> it = data.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                it.next();
+                if (selectionModel.isSelectedIndex(i)) {
+                    it.remove();
+                }
+                i++;
+            }
+            fireContentsChanged(this, 0, data.size());
+        }
+
+        public List<ExtendedSourceEntry> getSelected() {
+            ArrayList<ExtendedSourceEntry> ret = new ArrayList<ExtendedSourceEntry>();
+            for(int i=0; i<data.size();i++) {
+                if (selectionModel.isSelectedIndex(i)) {
+                    ret.add(data.get(i));
+                }
+            }
+            return ret;
+        }
+    }
+
+    protected static class ActiveSourcesModel extends AbstractTableModel {
+        private List<SourceEntry> data;
+        private DefaultListSelectionModel selectionModel;
+
+        public ActiveSourcesModel(DefaultListSelectionModel selectionModel) {
+            this.selectionModel = selectionModel;
+            this.data = new ArrayList<SourceEntry>();
+        }
+
+        public int getColumnCount() {
+            return 1;
+        }
+
+        public int getRowCount() {
+            return data == null ? 0 : data.size();
+        }
+
+        @Override
+        public SourceEntry getValueAt(int rowIndex, int columnIndex) {
+            return data.get(rowIndex);
+        }
+
+        @Override
+        public boolean isCellEditable(int rowIndex, int columnIndex) {
+            return false;
+        }
+
+        @Override
+        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+            updateSource(rowIndex, (String)aValue);
+        }
+
+        public void setActiveSources(Collection<? extends SourceEntry> sources) {
+            data.clear();
+            if (sources != null) {
+                data.addAll(sources);
+            }
+            fireTableDataChanged();
+        }
+
+        public void addSource(SourceEntry entry) {
+            if (entry == null) return;
+            data.add(entry);
+            fireTableDataChanged();
+            int idx = data.indexOf(entry);
+            if (idx >= 0) {
+                selectionModel.setSelectionInterval(idx, idx);
+            }
+        }
+
+        public void updateSource(int pos, String src) {
+            if (src == null) return;
+            if (pos < 0 || pos >= getRowCount()) return;
+            data.get(pos).url = src;
+            fireTableDataChanged();
+            int idx = data.indexOf(src);
+            if (idx >= 0) {
+                selectionModel.setSelectionInterval(idx, idx);
+            }
+        }
+
+        public void removeSelected() {
+            Iterator<SourceEntry> it = data.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                it.next();
+                if (selectionModel.isSelectedIndex(i)) {
+                    it.remove();
+                }
+                i++;
+            }
+            fireTableDataChanged();
+        }
+
+        public void removeIdxs(Collection<Integer> idxs) {
+            List<SourceEntry> newData = new ArrayList<SourceEntry>();
+            for (int i=0; i<data.size(); ++i) {
+                if (!idxs.contains(i)) {
+                    newData.add(data.get(i));
+                }
+            }
+            data = newData;
+            fireTableDataChanged();
+        }
+
+        public void addExtendedSourceEntries(List<ExtendedSourceEntry> sources) {
+            if (sources == null) return;
+            for (ExtendedSourceEntry info: sources) {
+                data.add(new SourceEntry(info.url, info.name, info.getDisplayName(), true));
+            }
+            fireTableDataChanged();
+            selectionModel.clearSelection();
+            for (ExtendedSourceEntry info: sources) {
+                int pos = data.indexOf(info);
+                if (pos >=0) {
+                    selectionModel.addSelectionInterval(pos, pos);
+                }
+            }
+        }
+
+        public List<SourceEntry> getSources() {
+            return new ArrayList<SourceEntry>(data);
+        }
+    }
+
+    public static class ExtendedSourceEntry extends SourceEntry {
+        public String simpleFileName;
+        public String version;
+        public String author;
+        public String link;
+        public String description;
+
+        public ExtendedSourceEntry(String simpleFileName, String url) {
+            super(url, null, null, true);
+            this.simpleFileName = simpleFileName;
+            version = author = link = description = shortdescription = null;
+        }
+
+        /**
+         * @return string representation for GUI list or menu entry
+         */
+        public String getDisplayName() {
+            return shortdescription == null ? simpleFileName : shortdescription;
+        }
+
+        public String getTooltip() {
+            String s = tr("Short Description: {0}", getDisplayName()) + "<br>" + tr("URL: {0}", url);
+            if (author != null) {
+                s += "<br>" + tr("Author: {0}", author);
+            }
+            if (link != null) {
+                s += "<br>" + tr("Webpage: {0}", link);
+            }
+            if (description != null) {
+                s += "<br>" + tr("Description: {0}", description);
+            }
+            if (version != null) {
+                s += "<br>" + tr("Version: {0}", version);
+            }
+            return "<html>" + s + "</html>";
+        }
+
+        @Override
+        public String toString() {
+            return "<html><b>" + getDisplayName() + "</b> (" + url + ")</html>";
+        }
+    }
+
+    protected class EditSourceEntryDialog extends ExtendedDialog {
+
+        /**
+         * We call this text field "name", but it is actually the shortdescription.
+         */
+        private JTextField tfName;
+        private JTextField tfURL;
+
+        public EditSourceEntryDialog(Component parent, String title, SourceEntry e) {
+            super(parent,
+                    title,
+                    new String[] {tr("Ok"), tr("Cancel")});
+
+            JPanel p = new JPanel(new GridBagLayout());
+
+            tfName = new JTextField(60);
+            p.add(new JLabel(tr("Name (optional):")), GBC.std().insets(15, 0, 5, 5));
+            p.add(tfName, GBC.eol().insets(0, 0, 5, 5));
+
+            tfURL = new JTextField(60);
+            p.add(new JLabel(tr("URL / File:")), GBC.std().insets(15, 0, 5, 0));
+            p.add(tfURL, GBC.std().insets(0, 0, 5, 0));
+            JButton fileChooser = new JButton(new LaunchFileChooserAction());
+            fileChooser.setMargin(new Insets(0, 0, 0, 0));
+            p.add(fileChooser, GBC.eol().insets(0, 0, 5, 0));
+
+            if (e != null) {
+                if (e.shortdescription != null) {
+                    tfName.setText(e.shortdescription);
+                }
+                tfURL.setText(e.url);
+            }
+
+            setButtonIcons(new String[] {"ok", "cancel"});
+            setContent(p);
+        }
+
+        class LaunchFileChooserAction extends AbstractAction {
+            public LaunchFileChooserAction() {
+                putValue(SMALL_ICON, ImageProvider.get("open"));
+                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
+            }
+
+            protected void prepareFileChooser(String url, JFileChooser fc) {
+                if (url == null || url.trim().length() == 0) return;
+                URL sourceUrl = null;
+                try {
+                    sourceUrl = new URL(url);
+                } catch(MalformedURLException e) {
+                    File f = new File(url);
+                    if (f.isFile()) {
+                        f = f.getParentFile();
+                    }
+                    if (f != null) {
+                        fc.setCurrentDirectory(f);
+                    }
+                    return;
+                }
+                if (sourceUrl.getProtocol().startsWith("file")) {
+                    File f = new File(sourceUrl.getPath());
+                    if (f.isFile()) {
+                        f = f.getParentFile();
+                    }
+                    if (f != null) {
+                        fc.setCurrentDirectory(f);
+                    }
+                }
+            }
+
+            public void actionPerformed(ActionEvent e) {
+                JFileChooser fc= new JFileChooser();
+                prepareFileChooser(tfURL.getText(), fc);
+                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(SourceEditor.this));
+                if (ret != JFileChooser.APPROVE_OPTION)
+                    return;
+                tfURL.setText(fc.getSelectedFile().toString());
+            }
+        }
+
+        public String getShortdescription() {
+            return tfName.getText();
+        }
+
+        public String getURL() {
+            return tfURL.getText();
+        }
+    }
+
+    class NewActiveSourceAction extends AbstractAction {
+        public NewActiveSourceAction() {
+            putValue(NAME, tr("New"));
+            putValue(SHORT_DESCRIPTION, getStr(I18nString.NEW_SOURCE_ENTRY_TOOLTIP));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
+                    SourceEditor.this,
+                    getStr(I18nString.NEW_SOURCE_ENTRY),
+                    null);
+            editEntryDialog.showDialog();
+            if (editEntryDialog.getValue() == 1) {
+                activeSourcesModel.addSource(new SourceEntry(
+                        editEntryDialog.getURL(),
+                        null, editEntryDialog.getShortdescription(), true));
+                activeSourcesModel.fireTableDataChanged();
+            }
+        }
+    }
+
+    class RemoveActiveSourcesAction extends AbstractAction implements ListSelectionListener {
+
+        public RemoveActiveSourcesAction() {
+            putValue(NAME, tr("Remove"));
+            putValue(SHORT_DESCRIPTION, getStr(I18nString.REMOVE_SOURCE_TOOLTIP));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(tblActiveSources.getSelectedRowCount() > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            activeSourcesModel.removeSelected();
+        }
+    }
+
+    class EditActiveSourceAction extends AbstractAction implements ListSelectionListener {
+        public EditActiveSourceAction() {
+            putValue(NAME, tr("Edit"));
+            putValue(SHORT_DESCRIPTION, getStr(I18nString.EDIT_SOURCE_TOOLTIP));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(tblActiveSources.getSelectedRowCount() == 1);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent evt) {
+            int pos = tblActiveSources.getSelectedRow();
+            if (pos < 0 || pos >= tblActiveSources.getRowCount())
+                return;
+
+            SourceEntry e = activeSourcesModel.getValueAt(pos, 0);
+
+            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
+                    SourceEditor.this, tr("Edit source entry:"), e);
+            editEntryDialog.showDialog();
+            if (editEntryDialog.getValue() == 1) {
+                if (e.shortdescription != null || !equal(editEntryDialog.getShortdescription(), "")) {
+                    e.shortdescription = editEntryDialog.getShortdescription();
+                    if (equal(e.shortdescription, "")) {
+                        e.shortdescription = null;
+                    }
+                }
+                e.url = editEntryDialog.getURL();
+                activeSourcesModel.fireTableCellUpdated(pos, 0);
+            }
+        }
+    }
+
+    class ActivateSourcesAction extends AbstractAction implements ListSelectionListener {
+        public ActivateSourcesAction() {
+            putValue(SHORT_DESCRIPTION, getStr(I18nString.ACTIVATE_TOOLTIP));
+            putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(lstAvailableSources.getSelectedIndices().length > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            List<ExtendedSourceEntry> sources = availableSourcesModel.getSelected();
+            activeSourcesModel.addExtendedSourceEntries(sources);
+        }
+    }
+
+    class ResetAction extends AbstractAction {
+
+        public ResetAction() {
+            putValue(NAME, tr("Reset"));
+            putValue(SHORT_DESCRIPTION, tr("Reset to default"));
+            putValue(SMALL_ICON, ImageProvider.get("preferences", "reset"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            activeSourcesModel.setActiveSources(getDefault());
+        }
+    }
+
+    class ReloadSourcesAction extends AbstractAction {
+        private String url;
+        public ReloadSourcesAction(String url) {
+            putValue(NAME, tr("Reload"));
+            putValue(SHORT_DESCRIPTION, tr(getStr(I18nString.RELOAD_ALL_AVAILABLE), url));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
+            this.url = url;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            MirroredInputStream.cleanup(url);
+            reloadAvailableSources(url);
+        }
+    }
+
+    protected static class IconPathTableModel extends AbstractTableModel {
+        private ArrayList<String> data;
+        private DefaultListSelectionModel selectionModel;
+
+        public IconPathTableModel(DefaultListSelectionModel selectionModel) {
+            this.selectionModel = selectionModel;
+            this.data = new ArrayList<String>();
+        }
+
+        public int getColumnCount() {
+            return 1;
+        }
+
+        public int getRowCount() {
+            return data == null ? 0 : data.size();
+        }
+
+        public Object getValueAt(int rowIndex, int columnIndex) {
+            return data.get(rowIndex);
+        }
+
+        @Override
+        public boolean isCellEditable(int rowIndex, int columnIndex) {
+            return true;
+        }
+
+        @Override
+        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+            updatePath(rowIndex, (String)aValue);
+        }
+
+        public void setIconPaths(Collection<String> paths) {
+            data.clear();
+            if (paths !=null) {
+                data.addAll(paths);
+            }
+            sort();
+            fireTableDataChanged();
+        }
+
+        public void addPath(String path) {
+            if (path == null) return;
+            data.add(path);
+            sort();
+            fireTableDataChanged();
+            int idx = data.indexOf(path);
+            if (idx >= 0) {
+                selectionModel.setSelectionInterval(idx, idx);
+            }
+        }
+
+        public void updatePath(int pos, String path) {
+            if (path == null) return;
+            if (pos < 0 || pos >= getRowCount()) return;
+            data.set(pos, path);
+            sort();
+            fireTableDataChanged();
+            int idx = data.indexOf(path);
+            if (idx >= 0) {
+                selectionModel.setSelectionInterval(idx, idx);
+            }
+        }
+
+        public void removeSelected() {
+            Iterator<String> it = data.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                it.next();
+                if (selectionModel.isSelectedIndex(i)) {
+                    it.remove();
+                }
+                i++;
+            }
+            fireTableDataChanged();
+            selectionModel.clearSelection();
+        }
+
+        protected void sort() {
+            Collections.sort(
+                    data,
+                    new Comparator<String>() {
+                        public int compare(String o1, String o2) {
+                            if (o1.equals("") && o2.equals(""))
+                                return 0;
+                            if (o1.equals("")) return 1;
+                            if (o2.equals("")) return -1;
+                            return o1.compareTo(o2);
+                        }
+                    }
+            );
+        }
+
+        public List<String> getIconPaths() {
+            return new ArrayList<String>(data);
+        }
+    }
+
+    class NewIconPathAction extends AbstractAction {
+        public NewIconPathAction() {
+            putValue(NAME, tr("New"));
+            putValue(SHORT_DESCRIPTION, tr("Add a new icon path"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            iconPathsModel.addPath("");
+            tblIconPaths.editCellAt(iconPathsModel.getRowCount() -1,0);
+        }
+    }
+
+    class RemoveIconPathAction extends AbstractAction implements ListSelectionListener {
+        public RemoveIconPathAction() {
+            putValue(NAME, tr("Remove"));
+            putValue(SHORT_DESCRIPTION, tr("Remove the selected icon paths"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(tblIconPaths.getSelectedRowCount() > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            iconPathsModel.removeSelected();
+        }
+    }
+
+    class EditIconPathAction extends AbstractAction implements ListSelectionListener {
+        public EditIconPathAction() {
+            putValue(NAME, tr("Edit"));
+            putValue(SHORT_DESCRIPTION, tr("Edit the selected icon path"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
+            updateEnabledState();
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(tblIconPaths.getSelectedRowCount() == 1);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            int row = tblIconPaths.getSelectedRow();
+            tblIconPaths.editCellAt(row, 0);
+        }
+    }
+
+    static class SourceEntryListCellRenderer extends JLabel implements ListCellRenderer {
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+                boolean cellHasFocus) {
+            String s = value.toString();
+            setText(s);
+            if (isSelected) {
+                setBackground(list.getSelectionBackground());
+                setForeground(list.getSelectionForeground());
+            } else {
+                setBackground(list.getBackground());
+                setForeground(list.getForeground());
+            }
+            setEnabled(list.isEnabled());
+            setFont(list.getFont());
+            setFont(getFont().deriveFont(Font.PLAIN));
+            setOpaque(true);
+            setToolTipText(((ExtendedSourceEntry) value).getTooltip());
+            return this;
+        }
+    }
+
+    class SourceLoader extends PleaseWaitRunnable {
+        private String url;
+        private BufferedReader reader;
+        private boolean canceled;
+
+        public SourceLoader(String url) {
+            super(tr(getStr(I18nString.LOADING_SOURCES_FROM), url));
+            this.url = url;
+        }
+
+        @Override
+        protected void cancel() {
+            canceled = true;
+            if (reader!= null) {
+                try {
+                    reader.close();
+                } catch(IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+        @Override
+        protected void finish() {}
+
+        protected void warn(Exception e) {
+            String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
+            emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
+            String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg);
+
+            HelpAwareOptionPane.showOptionDialog(
+                    Main.parent,
+                    msg,
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE,
+                    ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC))
+            );
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            LinkedList<ExtendedSourceEntry> sources = new LinkedList<ExtendedSourceEntry>();
+            String lang = LanguageInfo.getLanguageCodeXML();
+            try {
+                sources.addAll(getDefault());
+                MirroredInputStream stream = new MirroredInputStream(url);
+                InputStreamReader r;
+                try {
+                    r = new InputStreamReader(stream, "UTF-8");
+                } catch (UnsupportedEncodingException e) {
+                    r = new InputStreamReader(stream);
+                }
+                reader = new BufferedReader(r);
+
+                String line;
+                ExtendedSourceEntry last = null;
+
+                while ((line = reader.readLine()) != null && !canceled) {
+                    if (line.trim().equals("")) {
+                        continue; // skip empty lines
+                    }
+                    if (line.startsWith("\t")) {
+                        Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
+                        if (! m.matches()) {
+                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
+                            continue;
+                        }
+                        if (last != null) {
+                            String key = m.group(1);
+                            String value = m.group(2);
+                            if ("author".equals(key) && last.author == null) {
+                                last.author = value;
+                            } else if ("version".equals(key)) {
+                                last.version = value;
+                            } else if ("link".equals(key) && last.link == null) {
+                                last.link = value;
+                            } else if ("description".equals(key) && last.description == null) {
+                                last.description = value;
+                            } else if ("shortdescription".equals(key) && last.shortdescription == null) {
+                                last.shortdescription = value;
+                            } else if ("name".equals(key) && last.name == null) {
+                                last.name = value;
+                            } else if ((lang + "author").equals(key)) {
+                                last.author = value;
+                            } else if ((lang + "link").equals(key)) {
+                                last.link = value;
+                            } else if ((lang + "description").equals(key)) {
+                                last.description = value;
+                            } else if ((lang + "shortdescription").equals(key)) {
+                                last.shortdescription = value;
+                            }
+                        }
+                    } else {
+                        last = null;
+                        Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
+                        if (m.matches()) {
+                            sources.add(last = new ExtendedSourceEntry(m.group(1), m.group(2)));
+                        } else {
+                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                if (canceled)
+                    // ignore the exception and return
+                    return;
+                OsmTransferException ex = new OsmTransferException(e);
+                ex.setUrl(url);
+                warn(ex);
+                return;
+            }
+            availableSourcesModel.setSources(sources);
+        }
+    }
+    
+    class SourceEntryTableCellRenderer extends DefaultTableCellRenderer {
+        @Override
+        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+            SourceEntry se = (SourceEntry) value;
+            JLabel label = (JLabel)super.getTableCellRendererComponent(table,
+                    fromSourceEntry(se), isSelected, hasFocus, row, column);
+            return label;
+        }
+
+        private String fromSourceEntry(SourceEntry entry) {
+            StringBuilder s = new StringBuilder("<html><b>");
+            if (entry.shortdescription != null) {
+                s.append(entry.shortdescription).append("</b> (");
+            }
+            s.append(entry.url);
+            if (entry.name != null) {
+                s.append(")");
+            }
+            s.append("</html>");
+            return s.toString();
+        }
+    }
+
+    class FileOrUrlCellEditor extends JPanel implements TableCellEditor {
+        private JTextField tfFileName;
+        private CopyOnWriteArrayList<CellEditorListener> listeners;
+        private String value;
+        private JFileChooser fileChooser;
+        private boolean isFile;
+
+        protected JFileChooser getFileChooser() {
+            if (fileChooser == null) {
+                this.fileChooser = new JFileChooser();
+                if(!isFile) {
+                    fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+                }
+            }
+            return fileChooser;
+        }
+
+        /**
+         * build the GUI
+         */
+        protected void build() {
+            setLayout(new GridBagLayout());
+            GridBagConstraints gc = new GridBagConstraints();
+            gc.gridx = 0;
+            gc.gridy = 0;
+            gc.fill = GridBagConstraints.BOTH;
+            gc.weightx = 1.0;
+            gc.weighty = 1.0;
+            add(tfFileName = new JTextField(), gc);
+
+            gc.gridx = 1;
+            gc.gridy = 0;
+            gc.fill = GridBagConstraints.BOTH;
+            gc.weightx = 0.0;
+            gc.weighty = 1.0;
+            add(new JButton(new LaunchFileChooserAction()));
+
+            tfFileName.addFocusListener(
+                    new FocusAdapter() {
+                        @Override
+                        public void focusGained(FocusEvent e) {
+                            tfFileName.selectAll();
+                        }
+                    }
+            );
+        }
+
+        public FileOrUrlCellEditor(boolean isFile) {
+            this.isFile = isFile;
+            listeners = new CopyOnWriteArrayList<CellEditorListener>();
+            build();
+        }
+
+        public void addCellEditorListener(CellEditorListener l) {
+            if (l != null) {
+                listeners.addIfAbsent(l);
+            }
+        }
+
+        protected void fireEditingCanceled() {
+            for (CellEditorListener l: listeners) {
+                l.editingCanceled(new ChangeEvent(this));
+            }
+        }
+
+        protected void fireEditingStopped() {
+            for (CellEditorListener l: listeners) {
+                l.editingStopped(new ChangeEvent(this));
+            }
+        }
+
+        public void cancelCellEditing() {
+            fireEditingCanceled();
+        }
+
+        public Object getCellEditorValue() {
+            return value;
+        }
+
+        public boolean isCellEditable(EventObject anEvent) {
+            if (anEvent instanceof MouseEvent)
+                return ((MouseEvent)anEvent).getClickCount() >= 2;
+                return true;
+        }
+
+        public void removeCellEditorListener(CellEditorListener l) {
+            listeners.remove(l);
+        }
+
+        public boolean shouldSelectCell(EventObject anEvent) {
+            return true;
+        }
+
+        public boolean stopCellEditing() {
+            value = tfFileName.getText();
+            fireEditingStopped();
+            return true;
+        }
+
+        public void setInitialValue(String initialValue) {
+            this.value = initialValue;
+            if (initialValue == null) {
+                this.tfFileName.setText("");
+            } else {
+                this.tfFileName.setText(initialValue);
+            }
+        }
+
+        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+            setInitialValue((String)value);
+            tfFileName.selectAll();
+            return this;
+        }
+
+        class LaunchFileChooserAction extends AbstractAction {
+            public LaunchFileChooserAction() {
+                putValue(NAME, "...");
+                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
+            }
+
+            protected void prepareFileChooser(String url, JFileChooser fc) {
+                if (url == null || url.trim().length() == 0) return;
+                URL sourceUrl = null;
+                try {
+                    sourceUrl = new URL(url);
+                } catch(MalformedURLException e) {
+                    File f = new File(url);
+                    if (f.isFile()) {
+                        f = f.getParentFile();
+                    }
+                    if (f != null) {
+                        fc.setCurrentDirectory(f);
+                    }
+                    return;
+                }
+                if (sourceUrl.getProtocol().startsWith("file")) {
+                    File f = new File(sourceUrl.getPath());
+                    if (f.isFile()) {
+                        f = f.getParentFile();
+                    }
+                    if (f != null) {
+                        fc.setCurrentDirectory(f);
+                    }
+                }
+            }
+
+            public void actionPerformed(ActionEvent e) {
+                JFileChooser fc = getFileChooser();
+                prepareFileChooser(tfFileName.getText(), fc);
+                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(SourceEditor.this));
+                if (ret != JFileChooser.APPROVE_OPTION)
+                    return;
+                tfFileName.setText(fc.getSelectedFile().toString());
+            }
+        }
+    }
+
+
+    /**
+     * Convert mappaint and preset source preferences from a simple list to
+     * array with one line for each source entry.
+     *
+     * MapPaint:
+     *
+     *    Old format
+     *      key: mappaint.style.sources
+     *      value: list of "<name>=<url>" pairs. The "<name>=" part is optional.
+     *          The style is always active.
+     *      default: empty list
+     *
+     *      key: mappaint.style.enable-defaults
+     *      value: if true, the default style "resource://data/elemstyles.xml" should
+     *          be loaded.
+     *      default: true
+     *
+     *    New format
+     *      key: mappaint.style.sources-list
+     *      value: each line is a list with entries: url, name, shortdescription, active
+     *      default:
+     *          One line: "resource://data/elemstyles.xml", "standard", tr("Internal Style"), true
+     *
+     * Tagging Preset:
+     *
+     *      the same, but "name" and "active" are not needed and omitted
+     *
+     */
+    abstract public static class SourcePrefMigration {
+
+        private final String oldPref;
+        private final String oldPrefEnableDefaults;
+        private final String pref;
+
+        public SourcePrefMigration(String oldPref, String oldPrefEnableDefaults, String pref) {
+            this.oldPref = oldPref;
+            this.oldPrefEnableDefaults = oldPrefEnableDefaults;
+            this.pref = pref;
+        }
+
+        abstract public Collection<ExtendedSourceEntry> getDefault();
+
+        abstract public Collection<String> serialize(SourceEntry entry);
+
+        abstract public SourceEntry deserialize(List<String> entryStr);
+
+        public List<SourceEntry> get() {
+            List<SourceEntry> entries = readNewFormatImpl();
+            if (entries == null) {
+
+                entries = readOldFormat();
+                put(entries);
+                return entries;
+            }
+            return entries;
+        }
+
+        public boolean put(Collection<? extends SourceEntry> entries) {
+            boolean changed = false;
+            if (entries.isEmpty()) {
+                changed |= Main.pref.put(pref + "._empty_", true);
+                changed |= Main.pref.putArray(pref, null);
+            } else {
+                Collection<Collection<String>> setting = new ArrayList<Collection<String>>();
+                for (SourceEntry e : entries) {
+                    setting.add(serialize(e));
+                }
+                changed |= Main.pref.put(pref + "._empty_", null);
+                changed |= Main.pref.putArray(pref, setting);
+            }
+            return changed;
+        }
+
+        public List<SourceEntry> readOldFormat() {
+            List<SourceEntry> result = new ArrayList<SourceEntry>();
+            if (Main.pref.getBoolean(oldPrefEnableDefaults, true)) {
+                result.addAll(getDefault());
+            }
+
+            List<String> lines = new LinkedList<String>(Main.pref.getCollection(oldPref));
+            for (String line : lines) {
+                String[] a = null;
+                if (line.indexOf("=") >= 0) {
+                    a = line.split("=", 2);
+                } else {
+                    a = new String[] { null, line };
+                }
+                result.add(new SourceEntry(a[1], a[0], null, true));
+            }
+
+            return result;
+        }
+
+        public Collection<? extends SourceEntry> readNewFormat() {
+            List<SourceEntry> entries = readNewFormatImpl();
+            if (entries == null) {
+                return getDefault();
+            }
+            return entries;
+        }
+
+        private List<SourceEntry> readNewFormatImpl() {
+            List<SourceEntry> entries = new ArrayList<SourceEntry>();
+            Collection<Collection<String>> mappaintSrc = Main.pref.getArray(pref, null);
+            if (mappaintSrc == null || mappaintSrc.isEmpty()) {
+                if (Main.pref.getBoolean(pref + "._empty_", false)) {
+                    return Collections.<SourceEntry>emptyList();
+                }
+                return null;
+            }
+
+            for (Collection<String> sourcePref : mappaintSrc) {
+                SourceEntry e = deserialize(new ArrayList<String>(sourcePref));
+                if (e != null) {
+                    entries.add(e);
+                }
+            }
+            return entries;
+        }
+    }
+
+}
Index: unk/src/org/openstreetmap/josm/gui/preferences/StyleSourceEditor.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/StyleSourceEditor.java	(revision 3796)
+++ 	(revision )
@@ -1,1397 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.gui.preferences;
-
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
-import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.Utils.equal;
-
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-import java.awt.event.ActionEvent;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EventObject;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.DefaultListModel;
-import javax.swing.DefaultListSelectionModel;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JSeparator;
-import javax.swing.JTable;
-import javax.swing.JTextField;
-import javax.swing.JToolBar;
-import javax.swing.KeyStroke;
-import javax.swing.ListCellRenderer;
-import javax.swing.ListSelectionModel;
-import javax.swing.event.CellEditorListener;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.event.TableModelEvent;
-import javax.swing.event.TableModelListener;
-import javax.swing.table.AbstractTableModel;
-import javax.swing.table.DefaultTableCellRenderer;
-import javax.swing.table.TableCellEditor;
-import javax.swing.table.TableCellRenderer;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.PleaseWaitRunnable;
-import org.openstreetmap.josm.io.MirroredInputStream;
-import org.openstreetmap.josm.io.OsmTransferException;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.LanguageInfo;
-import org.xml.sax.SAXException;
-
-public abstract class StyleSourceEditor extends JPanel {
-
-    protected JTable tblActiveStyles;
-    protected ActiveStylesModel activeStylesModel;
-    protected JList lstAvailableStyles;
-    protected AvailableStylesListModel availableStylesModel;
-    protected JTable tblIconPaths = null;
-    protected IconPathTableModel iconPathsModel;
-    protected boolean stylesInitiallyLoaded;
-    protected String availableStylesUrl;
-
-    /**
-     * constructor
-     * @param availableStylesUrl the URL to the list of available style sources
-     */
-    public StyleSourceEditor(final String availableStylesUrl) {
-
-        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
-        lstAvailableStyles = new JList(availableStylesModel = new AvailableStylesListModel(selectionModel));
-        lstAvailableStyles.setSelectionModel(selectionModel);
-        lstAvailableStyles.setCellRenderer(new StyleSourceCellRenderer());
-        this.availableStylesUrl = availableStylesUrl;
-
-        selectionModel = new DefaultListSelectionModel();
-        tblActiveStyles = new JTable(activeStylesModel = new ActiveStylesModel(selectionModel));
-        tblActiveStyles.putClientProperty("terminateEditOnFocusLost", true);
-        tblActiveStyles.setSelectionModel(selectionModel);
-        tblActiveStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-        tblActiveStyles.setShowGrid(false);
-        tblActiveStyles.setIntercellSpacing(new Dimension(0, 0));
-        tblActiveStyles.setTableHeader(null);
-        tblActiveStyles.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
-        SourceEntryRenderer sourceEntryRenderer = new SourceEntryRenderer();
-        tblActiveStyles.getColumnModel().getColumn(0).setCellRenderer(sourceEntryRenderer);
-        activeStylesModel.addTableModelListener(new TableModelListener() {
-            // Force swing to show horizontal scrollbars for the JTable
-            // Yes, this is a little ugly, but should work
-            @Override
-            public void tableChanged(TableModelEvent e) {
-                adjustColumnWidth(tblActiveStyles, 0);
-            }
-        });
-        activeStylesModel.setActiveStyles(getInitialSourcesList());
-        
-        final EditActiveStyleAction editActiveStyleAction = new EditActiveStyleAction();
-        tblActiveStyles.getSelectionModel().addListSelectionListener(editActiveStyleAction);
-        tblActiveStyles.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                if (e.getClickCount() == 2) {
-                    int row = tblActiveStyles.rowAtPoint(e.getPoint());
-                    if (row < 0 || row >= tblActiveStyles.getRowCount())
-                        return;
-                    editActiveStyleAction.actionPerformed(null);
-                }
-            }
-        });
-
-        RemoveActiveStylesAction removeActiveStylesAction = new RemoveActiveStylesAction();
-        tblActiveStyles.getSelectionModel().addListSelectionListener(removeActiveStylesAction);
-        tblActiveStyles.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
-        tblActiveStyles.getActionMap().put("delete", removeActiveStylesAction);
-
-        ActivateStylesAction activateStylesAction = new ActivateStylesAction();
-        lstAvailableStyles.addListSelectionListener(activateStylesAction);
-        JButton activate = new JButton(activateStylesAction);
-
-        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
-        setLayout(new GridBagLayout());
-
-        GridBagConstraints gbc = new GridBagConstraints();
-        gbc.gridx = 0;
-        gbc.gridy = 0;
-        gbc.weightx = 0.5;
-        gbc.gridwidth = 2;
-        gbc.anchor = GBC.WEST;
-        gbc.insets = new Insets(5, 11, 0, 0);
-
-        add(new JLabel(getStr(I18nString.AVAILABLE_SOURCES)), gbc);
-
-        gbc.gridx = 2;
-        gbc.insets = new Insets(5, 0, 0, 6);
-
-        add(new JLabel(getStr(I18nString.ACTIVE_SOURCES)), gbc);
-
-        gbc.gridwidth = 1;
-        gbc.gridx = 0;
-        gbc.gridy++;
-        gbc.weighty = 0.8;
-        gbc.fill = GBC.BOTH;
-        gbc.anchor = GBC.CENTER;
-        gbc.insets = new Insets(0, 11, 0, 0);
-
-        JScrollPane sp1 = new JScrollPane(lstAvailableStyles);
-        add(sp1, gbc);
-
-        gbc.gridx = 1;
-        gbc.weightx = 0.0;
-        gbc.fill = GBC.VERTICAL;
-        gbc.insets = new Insets(0, 0, 0, 0);
-
-        JToolBar middleTB = new JToolBar();
-        middleTB.setFloatable(false);
-        middleTB.setBorderPainted(false);
-        middleTB.setOpaque(false);
-        middleTB.add(Box.createHorizontalGlue());
-        middleTB.add(activate);
-        middleTB.add(Box.createHorizontalGlue());
-        add(middleTB, gbc);
-
-        gbc.gridx++;
-        gbc.weightx = 0.5;
-        gbc.fill = GBC.BOTH;
-
-        JScrollPane sp = new JScrollPane(tblActiveStyles);
-        add(sp, gbc);
-        sp.setColumnHeaderView(null);
-
-        gbc.gridx++;
-        gbc.weightx = 0.0;
-        gbc.fill = GBC.VERTICAL;
-        gbc.insets = new Insets(0, 0, 0, 6);
-
-        JToolBar sideButtonTB = new JToolBar(JToolBar.VERTICAL);
-        sideButtonTB.setFloatable(false);
-        sideButtonTB.setBorderPainted(false);
-        sideButtonTB.setOpaque(false);
-        sideButtonTB.add(new NewActiveStyleAction());
-        sideButtonTB.add(editActiveStyleAction);
-        sideButtonTB.add(removeActiveStylesAction);
-        add(sideButtonTB, gbc);
-
-        gbc.gridx = 0;
-        gbc.gridy++;
-        gbc.weighty = 0.0;
-        gbc.weightx = 0.5;
-        gbc.fill = GBC.HORIZONTAL;
-        gbc.anchor = GBC.WEST;
-        gbc.insets = new Insets(0, 11, 0, 0);
-
-        JToolBar bottomLeftTB = new JToolBar(JToolBar.VERTICAL);
-        bottomLeftTB.setFloatable(false);
-        bottomLeftTB.setBorderPainted(false);
-        bottomLeftTB.setOpaque(false);
-        bottomLeftTB.add(new ReloadStylesAction(availableStylesUrl));
-        middleTB.add(Box.createHorizontalGlue());
-        add(bottomLeftTB, gbc);
-
-        gbc.gridx = 2;
-        gbc.anchor = GBC.CENTER;
-        gbc.insets = new Insets(0, 0, 0, 0);
-
-        JToolBar bottomRightTB = new JToolBar();
-        bottomRightTB.setFloatable(false);
-        bottomRightTB.setBorderPainted(false);
-        bottomRightTB.setOpaque(false);
-        bottomRightTB.add(Box.createHorizontalGlue());
-        bottomRightTB.add(new JButton(new ResetAction()));
-        add(bottomRightTB, gbc);
-
-        /***
-         * Icon configuration
-         **/
-
-        selectionModel = new DefaultListSelectionModel();
-        tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
-        tblIconPaths.setSelectionModel(selectionModel);
-        tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-        tblIconPaths.setTableHeader(null);
-        tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(false));
-        tblIconPaths.setRowHeight(20);
-        tblIconPaths.putClientProperty("terminateEditOnFocusLost", true);
-        iconPathsModel.setIconPaths(getInitialIconPathsList());
-
-        EditIconPathAction editIconPathAction = new EditIconPathAction();
-        tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
-
-        RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
-        tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
-        tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
-        tblIconPaths.getActionMap().put("delete", removeIconPathAction);
-
-        gbc.gridx = 0;
-        gbc.gridy++;
-        gbc.weightx = 1.0;
-        gbc.gridwidth = GBC.REMAINDER;
-        gbc.insets = new Insets(8, 11, 8, 6);
-
-        add(new JSeparator(), gbc);
-
-        gbc.gridy++;
-        gbc.insets = new Insets(0, 11, 0, 6);
-
-        add(new JLabel(tr("Icon paths:")), gbc);
-
-        gbc.gridy++;
-        gbc.weighty = 0.2;
-        gbc.gridwidth = 3;
-        gbc.fill = GBC.BOTH;
-        gbc.insets = new Insets(0, 11, 0, 0);
-
-        add(sp = new JScrollPane(tblIconPaths), gbc);
-        sp.setColumnHeaderView(null);
-
-        gbc.gridx = 3;
-        gbc.gridwidth = 1;
-        gbc.weightx = 0.0;
-        gbc.fill = GBC.VERTICAL;
-        gbc.insets = new Insets(0, 0, 0, 6);
-
-        JToolBar sideButtonTBIcons = new JToolBar(JToolBar.VERTICAL);
-        sideButtonTBIcons.setFloatable(false);
-        sideButtonTBIcons.setBorderPainted(false);
-        sideButtonTBIcons.setOpaque(false);
-        sideButtonTBIcons.add(new NewIconPathAction());
-        sideButtonTBIcons.add(editIconPathAction);
-        sideButtonTBIcons.add(removeIconPathAction);
-        add(sideButtonTBIcons, gbc);
-    }
-
-    /**
-     * Load the list of source entries that the user has configured.
-     */
-    abstract public Collection<? extends SourceEntry> getInitialSourcesList();
-
-    /**
-     * Load the list of configured icon paths.
-     */
-    abstract public Collection<String> getInitialIconPathsList();
-
-    /**
-     * Get the default list of entries (used when resetting the list).
-     */
-    abstract public Collection<StyleSourceInfo> getDefault();
-
-    /**
-     * Save the settings after user clicked "Ok".
-     * @return true if restart is required
-     */
-    abstract public boolean finish();
-
-    /**
-     * Provide the GUI strings. (There are differences for MapPaint and Preset)
-     */
-    abstract protected String getStr(I18nString ident);
-
-    /**
-     * Identifiers for strings that need to be provided.
-     */
-    protected enum I18nString { AVAILABLE_SOURCES, ACTIVE_SOURCES, NEW_SOURCE_ENTRY,
-            REMOVE_SOURCE_TOOLTIP, EDIT_SOURCE_TOOLTIP, ACTIVATE_TOOLTIP, RELOAD_ALL_AVAILABLE,
-            LOADING_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC,
-            ILLEGAL_FORMAT_OF_ENTRY }
-
-    /**
-     * adjust the preferred width of column col to the maximum preferred width of the cells
-     * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
-     */
-    private static void adjustColumnWidth(JTable tbl, int col) {
-        int maxwidth = 0;
-		for (int row=0; row<tbl.getRowCount(); row++) {
-            TableCellRenderer tcr = tbl.getCellRenderer(row, col);
-                Object val = tbl.getValueAt(row, col);
-                Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col);
-                maxwidth = Math.max(comp.getPreferredSize().width, maxwidth);
-		}
-		tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth);
-    }
-
-    public boolean hasActiveStylesChanged() {
-        Collection<? extends SourceEntry> prev = getInitialSourcesList();
-        List<SourceEntry> cur = activeStylesModel.getStyles();
-        if (prev.size() != cur.size())
-            return true;
-        Iterator<? extends SourceEntry> p = prev.iterator();
-        Iterator<SourceEntry> c = cur.iterator();
-        while (p.hasNext()) {
-            SourceEntry pe = p.next();
-            SourceEntry ce = c.next();
-            if (!equal(pe.url, ce.url) || !equal(pe.name, ce.name) || pe.active != ce.active)
-                return true;
-        }
-        return false;
-    }
-
-    public Collection<SourceEntry> getActiveStyles() {
-        return activeStylesModel.getStyles();
-    }
-
-    public void removeSources(Collection<Integer> idxs) {
-        activeStylesModel.removeIdxs(idxs);
-    }
-
-    protected void reloadAvailableStyles(String url) {
-        Main.worker.submit(new StyleSourceLoader(url));
-    }
-
-    public void initiallyLoadAvailableStyles() {
-        if (!stylesInitiallyLoaded) {
-            reloadAvailableStyles(this.availableStylesUrl);
-        }
-        stylesInitiallyLoaded = true;
-    }
-
-    protected static class AvailableStylesListModel extends DefaultListModel {
-        private ArrayList<StyleSourceInfo> data;
-        private DefaultListSelectionModel selectionModel;
-
-        public AvailableStylesListModel(DefaultListSelectionModel selectionModel) {
-            data = new ArrayList<StyleSourceInfo>();
-            this.selectionModel = selectionModel;
-        }
-
-        public void setStyleSources(List<StyleSourceInfo> styleSources) {
-            data.clear();
-            if (styleSources != null) {
-                data.addAll(styleSources);
-            }
-            fireContentsChanged(this, 0, data.size());
-        }
-
-        @Override
-        public Object getElementAt(int index) {
-            return data.get(index);
-        }
-
-        @Override
-        public int getSize() {
-            if (data == null) return 0;
-            return data.size();
-        }
-
-        public void deleteSelected() {
-            Iterator<StyleSourceInfo> it = data.iterator();
-            int i=0;
-            while(it.hasNext()) {
-                it.next();
-                if (selectionModel.isSelectedIndex(i)) {
-                    it.remove();
-                }
-                i++;
-            }
-            fireContentsChanged(this, 0, data.size());
-        }
-
-        public List<StyleSourceInfo> getSelected() {
-            ArrayList<StyleSourceInfo> ret = new ArrayList<StyleSourceInfo>();
-            for(int i=0; i<data.size();i++) {
-                if (selectionModel.isSelectedIndex(i)) {
-                    ret.add(data.get(i));
-                }
-            }
-            return ret;
-        }
-    }
-
-    protected static class ActiveStylesModel extends AbstractTableModel {
-        private List<SourceEntry> data;
-        private DefaultListSelectionModel selectionModel;
-
-        public ActiveStylesModel(DefaultListSelectionModel selectionModel) {
-            this.selectionModel = selectionModel;
-            this.data = new ArrayList<SourceEntry>();
-        }
-
-        public int getColumnCount() {
-            return 1;
-        }
-
-        public int getRowCount() {
-            return data == null ? 0 : data.size();
-        }
-
-        @Override
-        public SourceEntry getValueAt(int rowIndex, int columnIndex) {
-            return data.get(rowIndex);
-        }
-
-        @Override
-        public boolean isCellEditable(int rowIndex, int columnIndex) {
-            return false;
-        }
-
-        @Override
-        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
-            updateStyle(rowIndex, (String)aValue);
-        }
-
-        public void setActiveStyles(Collection<? extends SourceEntry> sources) {
-            //abstract public Collection<? extends StyleSourceEntry> getInitialStyleSources();
-            data.clear();
-            if (sources != null) {
-                data.addAll(sources);
-            }
-            fireTableDataChanged();
-        }
-
-        public void addStyle(SourceEntry style) {
-            if (style == null) return;
-            data.add(style);
-            fireTableDataChanged();
-            int idx = data.indexOf(style);
-            if (idx >= 0) {
-                selectionModel.setSelectionInterval(idx, idx);
-            }
-        }
-
-        public void updateStyle(int pos, String style) {
-            if (style == null) return;
-            if (pos < 0 || pos >= getRowCount()) return;
-            data.get(pos).url = style;
-            fireTableDataChanged();
-            int idx = data.indexOf(style);
-            if (idx >= 0) {
-                selectionModel.setSelectionInterval(idx, idx);
-            }
-        }
-
-        public void removeSelected() {
-            Iterator<SourceEntry> it = data.iterator();
-            int i=0;
-            while(it.hasNext()) {
-                it.next();
-                if (selectionModel.isSelectedIndex(i)) {
-                    it.remove();
-                }
-                i++;
-            }
-            fireTableDataChanged();
-        }
-
-        public void removeIdxs(Collection<Integer> idxs) {
-            List<SourceEntry> newData = new ArrayList<SourceEntry>();
-            for (int i=0; i<data.size(); ++i) {
-                if (!idxs.contains(i)) {
-                    newData.add(data.get(i));
-                }
-            }
-            data = newData;
-            fireTableDataChanged();
-        }
-
-        public void addStylesFromSources(List<StyleSourceInfo> sources) {
-            if (sources == null) return;
-            for (StyleSourceInfo info: sources) {
-                data.add(new SourceEntry(info.url, info.name, info.getDisplayName(), true));
-            }
-            fireTableDataChanged();
-            selectionModel.clearSelection();
-            for (StyleSourceInfo info: sources) {
-                int pos = data.indexOf(info);
-                if (pos >=0) {
-                    selectionModel.addSelectionInterval(pos, pos);
-                }
-            }
-        }
-
-        public List<SourceEntry> getStyles() {
-            return new ArrayList<SourceEntry>(data);
-        }
-    }
-
-    public static class StyleSourceInfo extends SourceEntry {
-        public String simpleFileName;
-        public String version;
-        public String author;
-        public String link;
-        public String description;
-
-        public StyleSourceInfo(String simpleFileName, String url) {
-            super(url, null, null, true);
-            this.simpleFileName = simpleFileName;
-            version = author = link = description = shortdescription = null;
-        }
-
-        /**
-         * @return string representation for GUI list or menu entry
-         */
-        public String getDisplayName() {
-            return shortdescription == null ? simpleFileName : shortdescription;
-        }
-
-        public String getTooltip() {
-            String s = tr("Short Description: {0}", getDisplayName()) + "<br>" + tr("URL: {0}", url);
-            if (author != null) {
-                s += "<br>" + tr("Author: {0}", author);
-            }
-            if (link != null) {
-                s += "<br>" + tr("Webpage: {0}", link);
-            }
-            if (description != null) {
-                s += "<br>" + tr("Description: {0}", description);
-            }
-            if (version != null) {
-                s += "<br>" + tr("Version: {0}", version);
-            }
-            return "<html>" + s + "</html>";
-        }
-
-        @Override
-        public String toString() {
-            return "<html><b>" + getDisplayName() + "</b> (" + url + ")</html>";
-        }
-    }
-
-    protected class EditSourceEntryDialog extends ExtendedDialog {
-
-        /**
-         * We call this text field "name", but it is actually the shortdescription.
-         */
-        private JTextField tfName;
-        private JTextField tfURL;
-
-        public EditSourceEntryDialog(Component parent, String title, SourceEntry e) {
-            super(parent,
-                    title,
-                    new String[] {tr("Ok"), tr("Cancel")});
-
-            JPanel p = new JPanel(new GridBagLayout());
-
-            tfName = new JTextField(60);
-            p.add(new JLabel(tr("Name (optional):")), GBC.std().insets(15, 0, 5, 5));
-            p.add(tfName, GBC.eol().insets(0, 0, 5, 5));
-
-            tfURL = new JTextField(60);
-            p.add(new JLabel(tr("URL / File:")), GBC.std().insets(15, 0, 5, 0));
-            p.add(tfURL, GBC.std().insets(0, 0, 5, 0));
-            JButton fileChooser = new JButton(new LaunchFileChooserAction());
-            fileChooser.setMargin(new Insets(0, 0, 0, 0));
-            p.add(fileChooser, GBC.eol().insets(0, 0, 5, 0));
-
-            if (e != null) {
-                if (e.shortdescription != null) {
-                    tfName.setText(e.shortdescription);
-                }
-                tfURL.setText(e.url);
-            }
-
-            setButtonIcons(new String[] {"ok", "cancel"});
-            setContent(p);
-        }
-
-        class LaunchFileChooserAction extends AbstractAction {
-            public LaunchFileChooserAction() {
-                putValue(SMALL_ICON, ImageProvider.get("open"));
-                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
-            }
-
-            protected void prepareFileChooser(String url, JFileChooser fc) {
-                if (url == null || url.trim().length() == 0) return;
-                URL sourceUrl = null;
-                try {
-                    sourceUrl = new URL(url);
-                } catch(MalformedURLException e) {
-                    File f = new File(url);
-                    if (f.isFile()) {
-                        f = f.getParentFile();
-                    }
-                    if (f != null) {
-                        fc.setCurrentDirectory(f);
-                    }
-                    return;
-                }
-                if (sourceUrl.getProtocol().startsWith("file")) {
-                    File f = new File(sourceUrl.getPath());
-                    if (f.isFile()) {
-                        f = f.getParentFile();
-                    }
-                    if (f != null) {
-                        fc.setCurrentDirectory(f);
-                    }
-                }
-            }
-
-            public void actionPerformed(ActionEvent e) {
-                JFileChooser fc= new JFileChooser();
-                prepareFileChooser(tfURL.getText(), fc);
-                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(StyleSourceEditor.this));
-                if (ret != JFileChooser.APPROVE_OPTION)
-                    return;
-                tfURL.setText(fc.getSelectedFile().toString());
-            }
-        }
-
-        public String getShortdescription() {
-            return tfName.getText();
-        }
-
-        public String getURL() {
-            return tfURL.getText();
-        }
-    }
-
-    class NewActiveStyleAction extends AbstractAction {
-        public NewActiveStyleAction() {
-            putValue(NAME, tr("New"));
-            putValue(SHORT_DESCRIPTION, tr("Add a filename or an URL of an active style"));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
-        }
-
-        public void actionPerformed(ActionEvent evt) {
-            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
-                    StyleSourceEditor.this,
-                    getStr(I18nString.NEW_SOURCE_ENTRY),
-                    null);
-            editEntryDialog.showDialog();
-            if (editEntryDialog.getValue() == 1) {
-                activeStylesModel.addStyle(new SourceEntry(
-                        editEntryDialog.getURL(),
-                        null, editEntryDialog.getShortdescription(), true));
-                activeStylesModel.fireTableDataChanged();
-            }
-        }
-    }
-
-    class RemoveActiveStylesAction extends AbstractAction implements ListSelectionListener {
-
-        public RemoveActiveStylesAction() {
-            putValue(NAME, tr("Remove"));
-            putValue(SHORT_DESCRIPTION, getStr(I18nString.REMOVE_SOURCE_TOOLTIP));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
-            updateEnabledState();
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(tblActiveStyles.getSelectedRowCount() > 0);
-        }
-
-        public void valueChanged(ListSelectionEvent e) {
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            activeStylesModel.removeSelected();
-        }
-    }
-
-    class EditActiveStyleAction extends AbstractAction implements ListSelectionListener {
-        public EditActiveStyleAction() {
-            putValue(NAME, tr("Edit"));
-            putValue(SHORT_DESCRIPTION, getStr(I18nString.EDIT_SOURCE_TOOLTIP));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
-            updateEnabledState();
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(tblActiveStyles.getSelectedRowCount() == 1);
-        }
-
-        public void valueChanged(ListSelectionEvent e) {
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent evt) {
-            int pos = tblActiveStyles.getSelectedRow();
-            if (pos < 0 || pos >= tblActiveStyles.getRowCount())
-                return;
-
-            SourceEntry e = activeStylesModel.getValueAt(pos, 0);
-
-            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
-                    StyleSourceEditor.this, tr("Edit source entry:"), e);
-            editEntryDialog.showDialog();
-            if (editEntryDialog.getValue() == 1) {
-                if (e.shortdescription != null || !equal(editEntryDialog.getShortdescription(), "")) {
-                    e.shortdescription = editEntryDialog.getShortdescription();
-                    if (equal(e.shortdescription, "")) {
-                        e.shortdescription = null;
-                    }
-                }
-                e.url = editEntryDialog.getURL();
-                activeStylesModel.fireTableCellUpdated(pos, 0);
-            }
-        }
-    }
-
-    class ActivateStylesAction extends AbstractAction implements ListSelectionListener {
-        public ActivateStylesAction() {
-            putValue(SHORT_DESCRIPTION, getStr(I18nString.ACTIVATE_TOOLTIP));
-            putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
-            updateEnabledState();
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(lstAvailableStyles.getSelectedIndices().length > 0);
-        }
-
-        public void valueChanged(ListSelectionEvent e) {
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            List<StyleSourceInfo> styleSources = availableStylesModel.getSelected();
-            activeStylesModel.addStylesFromSources(styleSources);
-        }
-    }
-
-    class ResetAction extends AbstractAction {
-
-        public ResetAction() {
-            putValue(NAME, tr("Reset"));
-            putValue(SHORT_DESCRIPTION, tr("Reset to default"));
-            putValue(SMALL_ICON, ImageProvider.get("preferences", "reset"));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            activeStylesModel.setActiveStyles(getDefault());
-        }
-    }
-
-    class ReloadStylesAction extends AbstractAction {
-        private String url;
-        public ReloadStylesAction(String url) {
-            putValue(NAME, tr("Reload"));
-            putValue(SHORT_DESCRIPTION, tr(getStr(I18nString.RELOAD_ALL_AVAILABLE), url));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
-            this.url = url;
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            MirroredInputStream.cleanup(url);
-            reloadAvailableStyles(url);
-        }
-    }
-
-    protected static class IconPathTableModel extends AbstractTableModel {
-        private ArrayList<String> data;
-        private DefaultListSelectionModel selectionModel;
-
-        public IconPathTableModel(DefaultListSelectionModel selectionModel) {
-            this.selectionModel = selectionModel;
-            this.data = new ArrayList<String>();
-        }
-
-        public int getColumnCount() {
-            return 1;
-        }
-
-        public int getRowCount() {
-            return data == null ? 0 : data.size();
-        }
-
-        public Object getValueAt(int rowIndex, int columnIndex) {
-            return data.get(rowIndex);
-        }
-
-        @Override
-        public boolean isCellEditable(int rowIndex, int columnIndex) {
-            return true;
-        }
-
-        @Override
-        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
-            updatePath(rowIndex, (String)aValue);
-        }
-
-        public void setIconPaths(Collection<String> styles) {
-            data.clear();
-            if (styles !=null) {
-                data.addAll(styles);
-            }
-            sort();
-            fireTableDataChanged();
-        }
-
-        public void addPath(String path) {
-            if (path == null) return;
-            data.add(path);
-            sort();
-            fireTableDataChanged();
-            int idx = data.indexOf(path);
-            if (idx >= 0) {
-                selectionModel.setSelectionInterval(idx, idx);
-            }
-        }
-
-        public void updatePath(int pos, String path) {
-            if (path == null) return;
-            if (pos < 0 || pos >= getRowCount()) return;
-            data.set(pos, path);
-            sort();
-            fireTableDataChanged();
-            int idx = data.indexOf(path);
-            if (idx >= 0) {
-                selectionModel.setSelectionInterval(idx, idx);
-            }
-        }
-
-        public void removeSelected() {
-            Iterator<String> it = data.iterator();
-            int i=0;
-            while(it.hasNext()) {
-                it.next();
-                if (selectionModel.isSelectedIndex(i)) {
-                    it.remove();
-                }
-                i++;
-            }
-            fireTableDataChanged();
-            selectionModel.clearSelection();
-        }
-
-        protected void sort() {
-            Collections.sort(
-                    data,
-                    new Comparator<String>() {
-                        public int compare(String o1, String o2) {
-                            if (o1.equals("") && o2.equals(""))
-                                return 0;
-                            if (o1.equals("")) return 1;
-                            if (o2.equals("")) return -1;
-                            return o1.compareTo(o2);
-                        }
-                    }
-            );
-        }
-
-        public List<String> getIconPaths() {
-            return new ArrayList<String>(data);
-        }
-    }
-
-    class NewIconPathAction extends AbstractAction {
-        public NewIconPathAction() {
-            putValue(NAME, tr("New"));
-            putValue(SHORT_DESCRIPTION, tr("Add a new icon path"));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            iconPathsModel.addPath("");
-            tblIconPaths.editCellAt(iconPathsModel.getRowCount() -1,0);
-        }
-    }
-
-    class RemoveIconPathAction extends AbstractAction implements ListSelectionListener {
-        public RemoveIconPathAction() {
-            putValue(NAME, tr("Remove"));
-            putValue(SHORT_DESCRIPTION, tr("Remove the selected icon paths"));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
-            updateEnabledState();
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(tblIconPaths.getSelectedRowCount() > 0);
-        }
-
-        public void valueChanged(ListSelectionEvent e) {
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            iconPathsModel.removeSelected();
-        }
-    }
-
-    class EditIconPathAction extends AbstractAction implements ListSelectionListener {
-        public EditIconPathAction() {
-            putValue(NAME, tr("Edit"));
-            putValue(SHORT_DESCRIPTION, tr("Edit the selected icon path"));
-            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
-            updateEnabledState();
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(tblIconPaths.getSelectedRowCount() == 1);
-        }
-
-        public void valueChanged(ListSelectionEvent e) {
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            int row = tblIconPaths.getSelectedRow();
-            tblIconPaths.editCellAt(row, 0);
-        }
-    }
-
-    static class StyleSourceCellRenderer extends JLabel implements ListCellRenderer {
-        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
-                boolean cellHasFocus) {
-            String s = value.toString();
-            setText(s);
-            if (isSelected) {
-                setBackground(list.getSelectionBackground());
-                setForeground(list.getSelectionForeground());
-            } else {
-                setBackground(list.getBackground());
-                setForeground(list.getForeground());
-            }
-            setEnabled(list.isEnabled());
-            setFont(list.getFont());
-            setFont(getFont().deriveFont(Font.PLAIN));
-            setOpaque(true);
-            setToolTipText(((StyleSourceInfo) value).getTooltip());
-            return this;
-        }
-    }
-
-    class StyleSourceLoader extends PleaseWaitRunnable {
-        private String url;
-        private BufferedReader reader;
-        private boolean canceled;
-
-        public StyleSourceLoader(String url) {
-            super(tr(getStr(I18nString.LOADING_SOURCES_FROM), url));
-            this.url = url;
-        }
-
-        @Override
-        protected void cancel() {
-            canceled = true;
-            if (reader!= null) {
-                try {
-                    reader.close();
-                } catch(IOException e) {
-                    // ignore
-                }
-            }
-        }
-
-        @Override
-        protected void finish() {}
-
-        protected void warn(Exception e) {
-            String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
-            emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
-            String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg);
-
-            HelpAwareOptionPane.showOptionDialog(
-                    Main.parent,
-                    msg,
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE,
-                    ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC))
-            );
-        }
-
-        @Override
-        protected void realRun() throws SAXException, IOException, OsmTransferException {
-            LinkedList<StyleSourceInfo> styles = new LinkedList<StyleSourceInfo>();
-            String lang = LanguageInfo.getLanguageCodeXML();
-            try {
-                styles.addAll(getDefault());
-                MirroredInputStream stream = new MirroredInputStream(url);
-                InputStreamReader r;
-                try {
-                    r = new InputStreamReader(stream, "UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    r = new InputStreamReader(stream);
-                }
-                reader = new BufferedReader(r);
-
-                String line;
-                StyleSourceInfo last = null;
-
-                while ((line = reader.readLine()) != null && !canceled) {
-                    if (line.trim().equals("")) {
-                        continue; // skip empty lines
-                    }
-                    if (line.startsWith("\t")) {
-                        Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
-                        if (! m.matches()) {
-                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
-                            continue;
-                        }
-                        if (last != null) {
-                            String key = m.group(1);
-                            String value = m.group(2);
-                            if ("author".equals(key) && last.author == null) {
-                                last.author = value;
-                            } else if ("version".equals(key)) {
-                                last.version = value;
-                            } else if ("link".equals(key) && last.link == null) {
-                                last.link = value;
-                            } else if ("description".equals(key) && last.description == null) {
-                                last.description = value;
-                            } else if ("shortdescription".equals(key) && last.shortdescription == null) {
-                                last.shortdescription = value;
-                            } else if ("name".equals(key) && last.name == null) {
-                                last.name = value;
-                            } else if ((lang + "author").equals(key)) {
-                                last.author = value;
-                            } else if ((lang + "link").equals(key)) {
-                                last.link = value;
-                            } else if ((lang + "description").equals(key)) {
-                                last.description = value;
-                            } else if ((lang + "shortdescription").equals(key)) {
-                                last.shortdescription = value;
-                            }
-                        }
-                    } else {
-                        last = null;
-                        Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
-                        if (m.matches()) {
-                            styles.add(last = new StyleSourceInfo(m.group(1), m.group(2)));
-                        } else {
-                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                if (canceled)
-                    // ignore the exception and return
-                    return;
-                OsmTransferException ex = new OsmTransferException(e);
-                ex.setUrl(url);
-                warn(ex);
-                return;
-            }
-            availableStylesModel.setStyleSources(styles);
-        }
-    }
-    
-    class SourceEntryRenderer extends DefaultTableCellRenderer {
-        @Override
-        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
-            SourceEntry se = (SourceEntry) value;
-            JLabel label = (JLabel)super.getTableCellRendererComponent(table,
-                    fromSourceEntry(se), isSelected, hasFocus, row, column);
-            return label;
-        }
-
-        private String fromSourceEntry(SourceEntry entry) {
-            StringBuilder s = new StringBuilder("<html><b>");
-            if (entry.shortdescription != null) {
-                s.append(entry.shortdescription).append("</b> (");
-            }
-            s.append(entry.url);
-            if (entry.name != null) {
-                s.append(")");
-            }
-            s.append("</html>");
-            return s.toString();
-        }
-    }
-
-    class FileOrUrlCellEditor extends JPanel implements TableCellEditor {
-        private JTextField tfFileName;
-        private CopyOnWriteArrayList<CellEditorListener> listeners;
-        private String value;
-        private JFileChooser fileChooser;
-        private boolean isFile;
-
-        protected JFileChooser getFileChooser() {
-            if (fileChooser == null) {
-                this.fileChooser = new JFileChooser();
-                if(!isFile) {
-                    fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
-                }
-            }
-            return fileChooser;
-        }
-
-        /**
-         * build the GUI
-         */
-        protected void build() {
-            setLayout(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.gridx = 0;
-            gc.gridy = 0;
-            gc.fill = GridBagConstraints.BOTH;
-            gc.weightx = 1.0;
-            gc.weighty = 1.0;
-            add(tfFileName = new JTextField(), gc);
-
-            gc.gridx = 1;
-            gc.gridy = 0;
-            gc.fill = GridBagConstraints.BOTH;
-            gc.weightx = 0.0;
-            gc.weighty = 1.0;
-            add(new JButton(new LaunchFileChooserAction()));
-
-            tfFileName.addFocusListener(
-                    new FocusAdapter() {
-                        @Override
-                        public void focusGained(FocusEvent e) {
-                            tfFileName.selectAll();
-                        }
-                    }
-            );
-        }
-
-        public FileOrUrlCellEditor(boolean isFile) {
-            this.isFile = isFile;
-            listeners = new CopyOnWriteArrayList<CellEditorListener>();
-            build();
-        }
-
-        public void addCellEditorListener(CellEditorListener l) {
-            if (l != null) {
-                listeners.addIfAbsent(l);
-            }
-        }
-
-        protected void fireEditingCanceled() {
-            for (CellEditorListener l: listeners) {
-                l.editingCanceled(new ChangeEvent(this));
-            }
-        }
-
-        protected void fireEditingStopped() {
-            for (CellEditorListener l: listeners) {
-                l.editingStopped(new ChangeEvent(this));
-            }
-        }
-
-        public void cancelCellEditing() {
-            fireEditingCanceled();
-        }
-
-        public Object getCellEditorValue() {
-            return value;
-        }
-
-        public boolean isCellEditable(EventObject anEvent) {
-            if (anEvent instanceof MouseEvent)
-                return ((MouseEvent)anEvent).getClickCount() >= 2;
-                return true;
-        }
-
-        public void removeCellEditorListener(CellEditorListener l) {
-            listeners.remove(l);
-        }
-
-        public boolean shouldSelectCell(EventObject anEvent) {
-            return true;
-        }
-
-        public boolean stopCellEditing() {
-            value = tfFileName.getText();
-            fireEditingStopped();
-            return true;
-        }
-
-        public void setInitialValue(String initialValue) {
-            this.value = initialValue;
-            if (initialValue == null) {
-                this.tfFileName.setText("");
-            } else {
-                this.tfFileName.setText(initialValue);
-            }
-        }
-
-        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
-            setInitialValue((String)value);
-            tfFileName.selectAll();
-            return this;
-        }
-
-        class LaunchFileChooserAction extends AbstractAction {
-            public LaunchFileChooserAction() {
-                putValue(NAME, "...");
-                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
-            }
-
-            protected void prepareFileChooser(String url, JFileChooser fc) {
-                if (url == null || url.trim().length() == 0) return;
-                URL sourceUrl = null;
-                try {
-                    sourceUrl = new URL(url);
-                } catch(MalformedURLException e) {
-                    File f = new File(url);
-                    if (f.isFile()) {
-                        f = f.getParentFile();
-                    }
-                    if (f != null) {
-                        fc.setCurrentDirectory(f);
-                    }
-                    return;
-                }
-                if (sourceUrl.getProtocol().startsWith("file")) {
-                    File f = new File(sourceUrl.getPath());
-                    if (f.isFile()) {
-                        f = f.getParentFile();
-                    }
-                    if (f != null) {
-                        fc.setCurrentDirectory(f);
-                    }
-                }
-            }
-
-            public void actionPerformed(ActionEvent e) {
-                JFileChooser fc = getFileChooser();
-                prepareFileChooser(tfFileName.getText(), fc);
-                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(StyleSourceEditor.this));
-                if (ret != JFileChooser.APPROVE_OPTION)
-                    return;
-                tfFileName.setText(fc.getSelectedFile().toString());
-            }
-        }
-    }
-
-
-    /**
-     * Convert mappaint and preset source preferences from a simple list to
-     * array with one line for each source entry.
-     *
-     * MapPaint:
-     *
-     *    Old format
-     *      key: mappaint.style.sources
-     *      value: list of "<name>=<url>" pairs. The "<name>=" part is optional.
-     *          The style is always active.
-     *      default: empty list
-     *
-     *      key: mappaint.style.enable-defaults
-     *      value: if true, the default style "resource://data/elemstyles.xml" should
-     *          be loaded.
-     *      default: true
-     *
-     *    New format
-     *      key: mappaint.style.sources-list
-     *      value: each line is a list with entries: url, name, shortdescription, active
-     *      default:
-     *          One line: "resource://data/elemstyles.xml", "standard", tr("Internal Style"), true
-     *
-     * Tagging Preset:
-     *
-     *      the same, but "name" and "active" are not needed and omitted
-     *
-     */
-    abstract public static class SourcePrefMigration {
-
-        private final String oldPref;
-        private final String oldPrefEnableDefaults;
-        private final String pref;
-
-        public SourcePrefMigration(String oldPref, String oldPrefEnableDefaults, String pref) {
-            this.oldPref = oldPref;
-            this.oldPrefEnableDefaults = oldPrefEnableDefaults;
-            this.pref = pref;
-        }
-
-        abstract public Collection<StyleSourceInfo> getDefault();
-
-        abstract public Collection<String> serialize(SourceEntry entry);
-
-        abstract public SourceEntry deserialize(List<String> entryStr);
-
-        public List<SourceEntry> get() {
-            List<SourceEntry> entries = readNewFormatImpl();
-            if (entries == null) {
-
-                entries = readOldFormat();
-                put(entries);
-                return entries;
-            }
-            return entries;
-        }
-
-        public boolean put(Collection<? extends SourceEntry> entries) {
-            boolean changed = false;
-            if (entries.isEmpty()) {
-                changed |= Main.pref.put(pref + "._empty_", true);
-                changed |= Main.pref.putArray(pref, null);
-            } else {
-                Collection<Collection<String>> setting = new ArrayList<Collection<String>>();
-                for (SourceEntry e : entries) {
-                    setting.add(serialize(e));
-                }
-                changed |= Main.pref.put(pref + "._empty_", null);
-                changed |= Main.pref.putArray(pref, setting);
-            }
-            return changed;
-        }
-
-        public List<SourceEntry> readOldFormat() {
-            List<SourceEntry> result = new ArrayList<SourceEntry>();
-            if (Main.pref.getBoolean(oldPrefEnableDefaults, true)) {
-                result.addAll(getDefault());
-            }
-
-            List<String> lines = new LinkedList<String>(Main.pref.getCollection(oldPref));
-            for (String line : lines) {
-                String[] a = null;
-                if (line.indexOf("=") >= 0) {
-                    a = line.split("=", 2);
-                } else {
-                    a = new String[] { null, line };
-                }
-                result.add(new SourceEntry(a[1], a[0], null, true));
-            }
-
-            return result;
-        }
-
-        public Collection<? extends SourceEntry> readNewFormat() {
-            List<SourceEntry> entries = readNewFormatImpl();
-            if (entries == null) {
-                return getDefault();
-            }
-            return entries;
-        }
-
-        private List<SourceEntry> readNewFormatImpl() {
-            List<SourceEntry> entries = new ArrayList<SourceEntry>();
-            Collection<Collection<String>> mappaintSrc = Main.pref.getArray(pref, null);
-            if (mappaintSrc == null || mappaintSrc.isEmpty()) {
-                if (Main.pref.getBoolean(pref + "._empty_", false)) {
-                    return Collections.<SourceEntry>emptyList();
-                }
-                return null;
-            }
-
-            for (Collection<String> sourcePref : mappaintSrc) {
-                SourceEntry e = deserialize(new ArrayList<String>(sourcePref));
-                if (e != null) {
-                    entries.add(e);
-                }
-            }
-            return entries;
-        }
-    }
-
-}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java	(revision 3796)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java	(revision 3797)
@@ -28,5 +28,5 @@
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
-import org.openstreetmap.josm.gui.preferences.StyleSourceEditor.StyleSourceInfo;
+import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
 import org.openstreetmap.josm.gui.tagging.TaggingPreset;
 import org.openstreetmap.josm.gui.tagging.TaggingPresetMenu;
@@ -46,14 +46,14 @@
 
     public static Collection<TaggingPreset> taggingPresets;
-    private StyleSourceEditor sources;
+    private SourceEditor sources;
     private JCheckBox sortMenu;
 
     private ValidationListener validationListener = new ValidationListener() {
         public boolean validatePreferences() {
-            if (sources.hasActiveStylesChanged()) {
+            if (sources.hasActiveSourcesChanged()) {
                 List<Integer> sourcesToRemove = new ArrayList<Integer>();
                 int i = -1;
                 SOURCES:
-                    for (SourceEntry source: sources.getActiveStyles()) {
+                    for (SourceEntry source: sources.getActiveSources()) {
                         i++;
                         boolean canLoad = false;
@@ -153,5 +153,5 @@
                     public void stateChanged(ChangeEvent e) {
                         if (gui.mapcontent.getSelectedComponent() == panel) {
-                            sources.initiallyLoadAvailableStyles();
+                            sources.initiallyLoadAvailableSources();
                         }
                     }
@@ -161,5 +161,5 @@
     }
 
-    class TaggingPresetSourceEditor extends StyleSourceEditor {
+    class TaggingPresetSourceEditor extends SourceEditor {
 
         final private String iconpref = "taggingpreset.icon.sources";
@@ -176,5 +176,5 @@
         @Override
         public boolean finish() {
-            List<SourceEntry> activeStyles = activeStylesModel.getStyles();
+            List<SourceEntry> activeStyles = activeSourcesModel.getSources();
 
             boolean changed = (new PresetPrefMigration()).put(activeStyles);
@@ -195,5 +195,5 @@
 
         @Override
-        public Collection<StyleSourceInfo> getDefault() {
+        public Collection<ExtendedSourceEntry> getDefault() {
             return (new PresetPrefMigration()).getDefault();
         }
@@ -211,4 +211,6 @@
                 case ACTIVE_SOURCES:
                     return tr("Active presets:");
+                case NEW_SOURCE_ENTRY_TOOLTIP:
+                     return tr("Add a new preset by entering filename or URL");
                 case NEW_SOURCE_ENTRY:
                     return tr("New preset entry:");
@@ -287,5 +289,5 @@
     }
 
-    public static class PresetPrefMigration extends StyleSourceEditor.SourcePrefMigration {
+    public static class PresetPrefMigration extends SourceEditor.SourcePrefMigration {
 
         public PresetPrefMigration() {
@@ -296,6 +298,6 @@
 
         @Override
-        public Collection<StyleSourceInfo> getDefault() {
-            StyleSourceInfo i = new StyleSourceInfo("defaultpresets.xml", "resource://data/defaultpresets.xml");
+        public Collection<ExtendedSourceEntry> getDefault() {
+            ExtendedSourceEntry i = new ExtendedSourceEntry("defaultpresets.xml", "resource://data/defaultpresets.xml");
             i.shortdescription = tr("Internal Preset");
             i.description = tr("The default preset for JOSM");
