Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 6552)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 6553)
@@ -4,8 +4,12 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -16,4 +20,5 @@
 import java.util.regex.Pattern;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.ChangePropertyCommand;
 import org.openstreetmap.josm.command.ChangePropertyKeyCommand;
@@ -25,4 +30,5 @@
 import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.preferences.CollectionProperty;
 import org.openstreetmap.josm.data.validation.FixableTestError;
 import org.openstreetmap.josm.data.validation.Severity;
@@ -38,8 +44,11 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
+import org.openstreetmap.josm.gui.widgets.EditableList;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.Utils;
 
+import javax.swing.JLabel;
 import javax.swing.JPanel;
 
@@ -384,13 +393,31 @@
         addMapCSS("power");
         addMapCSS("geometry");
-    }
+        for (final String i : sourcesProperty.get()) {
+            final String file = new File(i).getAbsolutePath();
+            try {
+                Main.info(tr("Adding {0} to tag checker", file));
+                addMapCSS(new BufferedReader(new InputStreamReader(new FileInputStream(i), Utils.UTF_8)));
+            } catch (Exception ex) {
+                Main.warn(new RuntimeException(tr("Failed to add {0} to tag checker", file), ex));
+            }
+        }
+    }
+
+    protected EditableList sourcesList;
+    protected final CollectionProperty sourcesProperty = new CollectionProperty(
+            "validator." + this.getClass().getName() + ".sources", Collections.<String>emptyList());
 
     @Override
     public void addGui(JPanel testPanel) {
         super.addGui(testPanel);
+        sourcesList = new EditableList(tr("TagChecker source"));
+        sourcesList.setItems(sourcesProperty.get());
+        testPanel.add(new JLabel(tr("Data sources ({0})", "*.mapcss")), GBC.eol().insets(23, 0, 0, 0));
+        testPanel.add(sourcesList, GBC.eol().fill(GBC.HORIZONTAL).insets(23, 0, 0, 0));
     }
 
     @Override
     public boolean ok() {
+        sourcesProperty.put(sourcesList.getItems());
         return super.ok();
     }
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 6552)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 6553)
@@ -5,7 +5,7 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -18,4 +18,5 @@
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -27,12 +28,7 @@
 import java.util.regex.PatternSyntaxException;
 
-import javax.swing.DefaultListModel;
-import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
-import javax.swing.JScrollPane;
 
 import org.openstreetmap.josm.Main;
@@ -59,4 +55,5 @@
 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.CheckGroup;
 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.KeyedItem;
+import org.openstreetmap.josm.gui.widgets.EditableList;
 import org.openstreetmap.josm.io.MirroredInputStream;
 import org.openstreetmap.josm.tools.GBC;
@@ -128,8 +125,4 @@
     protected JCheckBox prefUseSpellFile;
 
-    protected JButton addSrcButton;
-    protected JButton editSrcButton;
-    protected JButton deleteSrcButton;
-
     protected static final int EMPTY_VALUES      = 1200;
     protected static final int INVALID_KEY       = 1201;
@@ -145,6 +138,5 @@
     /** 1250 and up is used by tagcheck */
 
-    /** List of sources for spellcheck data */
-    protected JList sourcesList;
+    protected EditableList sourcesList;
 
     protected static final Entities entities = new Entities();
@@ -533,92 +525,9 @@
         testPanel.add(prefCheckComplexBeforeUpload, a);
 
-        sourcesList = new JList(new DefaultListModel());
-
-        String sources = Main.pref.get( PREF_SOURCES );
-        if (sources != null && sources.length() > 0) {
-            for (String source : sources.split(";")) {
-                ((DefaultListModel)sourcesList.getModel()).addElement(source);
-            }
-        }
-
-        addSrcButton = new JButton(tr("Add"));
-        addSrcButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                String source = JOptionPane.showInputDialog(
-                        Main.parent,
-                        tr("TagChecker source"),
-                        tr("TagChecker source"),
-                        JOptionPane.QUESTION_MESSAGE);
-                if (source != null) {
-                    ((DefaultListModel)sourcesList.getModel()).addElement(source);
-                }
-                sourcesList.clearSelection();
-            }
-        });
-
-        editSrcButton = new JButton(tr("Edit"));
-        editSrcButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                int row = sourcesList.getSelectedIndex();
-                if (row == -1 && sourcesList.getModel().getSize() == 1) {
-                    sourcesList.setSelectedIndex(0);
-                    row = 0;
-                }
-                if (row == -1) {
-                    if (sourcesList.getModel().getSize() == 0) {
-                        String source = JOptionPane.showInputDialog(Main.parent, tr("TagChecker source"), tr("TagChecker source"), JOptionPane.QUESTION_MESSAGE);
-                        if (source != null) {
-                            ((DefaultListModel)sourcesList.getModel()).addElement(source);
-                        }
-                    } else {
-                        JOptionPane.showMessageDialog(
-                                Main.parent,
-                                tr("Please select the row to edit."),
-                                tr("Information"),
-                                JOptionPane.INFORMATION_MESSAGE
-                                );
-                    }
-                } else {
-                    String source = (String)JOptionPane.showInputDialog(Main.parent,
-                            tr("TagChecker source"),
-                            tr("TagChecker source"),
-                            JOptionPane.QUESTION_MESSAGE, null, null,
-                            sourcesList.getSelectedValue());
-                    if (source != null) {
-                        ((DefaultListModel)sourcesList.getModel()).setElementAt(source, row);
-                    }
-                }
-                sourcesList.clearSelection();
-            }
-        });
-
-        deleteSrcButton = new JButton(tr("Delete"));
-        deleteSrcButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                if (sourcesList.getSelectedIndex() == -1) {
-                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."), tr("Information"), JOptionPane.QUESTION_MESSAGE);
-                } else {
-                    ((DefaultListModel)sourcesList.getModel()).remove(sourcesList.getSelectedIndex());
-                }
-            }
-        });
-        sourcesList.setMinimumSize(new Dimension(300,50));
-        sourcesList.setVisibleRowCount(3);
-
-        sourcesList.setToolTipText(tr("The sources (URL or filename) of spell check (see http://wiki.openstreetmap.org/index.php/User:JLS/speller) or tag checking data files."));
-        addSrcButton.setToolTipText(tr("Add a new source to the list."));
-        editSrcButton.setToolTipText(tr("Edit the selected source."));
-        deleteSrcButton.setToolTipText(tr("Delete the selected source from the list."));
-
-        testPanel.add(new JLabel(tr("Data sources")), GBC.eol().insets(23,0,0,0));
-        testPanel.add(new JScrollPane(sourcesList), GBC.eol().insets(23,0,0,0).fill(GridBagConstraints.HORIZONTAL));
-        final JPanel buttonPanel = new JPanel(new GridBagLayout());
-        testPanel.add(buttonPanel, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
-        buttonPanel.add(addSrcButton, GBC.std().insets(0,5,0,0));
-        buttonPanel.add(editSrcButton, GBC.std().insets(5,5,5,0));
-        buttonPanel.add(deleteSrcButton, GBC.std().insets(0,5,0,0));
+        final String sources = Main.pref.get(PREF_SOURCES);
+        sourcesList = new EditableList(tr("TagChecker source"));
+        sourcesList.setItems(sources != null ? Arrays.asList(sources.split(";")) : Collections.<String>emptyList());
+        testPanel.add(new JLabel(tr("Data sources ({0})", "*.cfg")), GBC.eol().insets(23, 0, 0, 0));
+        testPanel.add(sourcesList, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(23, 0, 0, 0));
 
         ActionListener disableCheckActionListener = new ActionListener() {
@@ -667,8 +576,5 @@
         boolean selected = prefCheckKeys.isSelected() || prefCheckKeysBeforeUpload.isSelected()
                 || prefCheckComplex.isSelected() || prefCheckComplexBeforeUpload.isSelected();
-        sourcesList.setEnabled( selected );
-        addSrcButton.setEnabled(selected);
-        editSrcButton.setEnabled(selected);
-        deleteSrcButton.setEnabled(selected);
+        sourcesList.setEnabled(selected);
     }
 
@@ -690,14 +596,6 @@
         Main.pref.put(PREF_USE_IGNORE_FILE, prefUseIgnoreFile.isSelected());
         Main.pref.put(PREF_USE_SPELL_FILE, prefUseSpellFile.isSelected());
-        StringBuilder sources = new StringBuilder();
-        if (sourcesList.getModel().getSize() > 0) {
-            for (int i = 0; i < sourcesList.getModel().getSize(); ++i) {
-                if (sources.length() > 0) {
-                    sources.append(";");
-                }
-                sources.append(sourcesList.getModel().getElementAt(i));
-            }
-        }
-        return Main.pref.put(PREF_SOURCES, sources.length() > 0 ? sources.toString() : null);
+        final List<String> sources = sourcesList.getItems();
+        return Main.pref.put(PREF_SOURCES, sources.isEmpty() ? null : Utils.join(";", sources));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/widgets/EditableList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/EditableList.java	(revision 6553)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/EditableList.java	(revision 6553)
@@ -0,0 +1,143 @@
+// License: GPL. See LICENSE file for details.
+package org.openstreetmap.josm.gui.widgets;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.GBC;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * A {@link JList} containing items, and {@link JButton}s to add/edit/delete items.
+ */
+public class EditableList extends JPanel {
+
+    public final String title;
+    public final JList sourcesList = new JList(new DefaultListModel());
+    public final JButton addSrcButton = new JButton(tr("Add"));
+    public final JButton editSrcButton = new JButton(tr("Edit"));
+    public final JButton deleteSrcButton = new JButton(tr("Delete"));
+
+    public EditableList(String title) {
+        this.title = title;
+        build();
+    }
+
+    protected void build() {
+
+        //setLayout(new GridBagLayout());
+        setLayout(new BorderLayout());
+
+        addSrcButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                String source = JOptionPane.showInputDialog(
+                        Main.parent,
+                        title,
+                        title,
+                        JOptionPane.QUESTION_MESSAGE);
+                if (source != null) {
+                    ((DefaultListModel) sourcesList.getModel()).addElement(source);
+                }
+                sourcesList.clearSelection();
+            }
+        });
+
+        editSrcButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                int row = sourcesList.getSelectedIndex();
+                if (row == -1 && sourcesList.getModel().getSize() == 1) {
+                    sourcesList.setSelectedIndex(0);
+                    row = 0;
+                }
+                if (row == -1) {
+                    if (sourcesList.getModel().getSize() == 0) {
+                        String source = JOptionPane.showInputDialog(Main.parent, title, title, JOptionPane.QUESTION_MESSAGE);
+                        if (source != null) {
+                            ((DefaultListModel) sourcesList.getModel()).addElement(source);
+                        }
+                    } else {
+                        JOptionPane.showMessageDialog(
+                                Main.parent,
+                                tr("Please select the row to edit."),
+                                tr("Information"),
+                                JOptionPane.INFORMATION_MESSAGE
+                        );
+                    }
+                } else {
+                    String source = (String) JOptionPane.showInputDialog(Main.parent,
+                            title,
+                            title,
+                            JOptionPane.QUESTION_MESSAGE, null, null,
+                            sourcesList.getSelectedValue());
+                    if (source != null) {
+                        ((DefaultListModel) sourcesList.getModel()).setElementAt(source, row);
+                    }
+                }
+                sourcesList.clearSelection();
+            }
+        });
+
+        deleteSrcButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                if (sourcesList.getSelectedIndex() == -1) {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."), tr("Information"), JOptionPane.QUESTION_MESSAGE);
+                } else {
+                    ((DefaultListModel) sourcesList.getModel()).remove(sourcesList.getSelectedIndex());
+                }
+            }
+        });
+        sourcesList.setMinimumSize(new Dimension(300, 50));
+        sourcesList.setVisibleRowCount(3);
+
+        addSrcButton.setToolTipText(tr("Add a new source to the list."));
+        editSrcButton.setToolTipText(tr("Edit the selected source."));
+        deleteSrcButton.setToolTipText(tr("Delete the selected source from the list."));
+
+        final JPanel buttonPanel = new JPanel(new GridBagLayout());
+        buttonPanel.add(addSrcButton, GBC.std().insets(0, 5, 0, 0));
+        buttonPanel.add(editSrcButton, GBC.std().insets(5, 5, 5, 0));
+        buttonPanel.add(deleteSrcButton, GBC.std().insets(0, 5, 0, 0));
+
+        add(new JScrollPane(sourcesList), BorderLayout.CENTER);
+        add(buttonPanel, BorderLayout.SOUTH);
+        setPreferredSize(new Dimension(300, 50 + (int) buttonPanel.getPreferredSize().getHeight()));
+
+    }
+
+    public void setItems(final Iterable<String> items) {
+        for (String source : items) {
+            ((DefaultListModel) sourcesList.getModel()).addElement(source);
+        }
+    }
+
+    public List<String> getItems() {
+        final List<String> items = new ArrayList<String>(sourcesList.getModel().getSize());
+        for (int i = 0; i < sourcesList.getModel().getSize(); ++i) {
+            items.add((String) sourcesList.getModel().getElementAt(i));
+        }
+        return items;
+    }
+
+    public void setEnabled(boolean enabled) {
+        sourcesList.setEnabled(enabled);
+        addSrcButton.setEnabled(enabled);
+        editSrcButton.setEnabled(enabled);
+        deleteSrcButton.setEnabled(enabled);
+    }
+}
