Changeset 3796 in josm


Ignore:
Timestamp:
2011-01-21T12:42:29+01:00 (13 years ago)
Author:
bastiK
Message:

Changed the mappaint and tagging preset preference format from a list of URLs to array preference to store more parameters for each source entry. (The flag "active" will be used for a dialog to quickly switch mappaint styles on and off.)
Removed the "mappaint.style.enable-defaults" and "taggingpreset.enable-defaults" property - the default (internal) source is now included in the normal list of sources. (The old preference entries are not removed yet, this should probably be changed, when the migration has been tested a little.)
Rearranged gui elements in mappaint and preset pref dialog to use the available space better and make the structure more clear. Strings that have been the same in mappaint and preset pref dialog are now different for each dialog.

Location:
trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java

    r3719 r3796  
    1616
    1717import org.openstreetmap.josm.Main;
     18import org.openstreetmap.josm.gui.preferences.SourceEntry;
     19import org.openstreetmap.josm.gui.preferences.MapPaintPreference.MapPaintPrefMigration;
    1820import org.openstreetmap.josm.io.MirroredInputStream;
    1921import org.openstreetmap.josm.tools.ImageProvider;
     
    7173        }
    7274
    73         Collection<String> files = Main.pref.getCollection("mappaint.style.sources", Collections.<String>emptySet());
    74         if (Main.pref.getBoolean("mappaint.style.enable-defaults", true)) {
    75             LinkedList<String> f = new LinkedList<String>();
    76             f.add("resource://data/elemstyles.xml");
    77             f.addAll(files);
    78             files = f;
    79         }
     75        Collection<? extends SourceEntry> sourceEntries = (new MapPaintPrefMigration()).get();
    8076
    81         for (String file : files) {
    82             String[] a = null;
     77        for (SourceEntry entry : sourceEntries) {
    8378            try {
    84                 if (file.indexOf("=") >= 0) {
    85                     a = file.split("=", 2);
    86                 } else {
    87                     a = new String[] { null, file };
    88                 }
    89                 XmlObjectParser parser = new XmlObjectParser(new ElemStyleHandler(a[0]));
    90                 MirroredInputStream in = new MirroredInputStream(a[1]);
     79                XmlObjectParser parser = new XmlObjectParser(new ElemStyleHandler(entry.name));
     80                MirroredInputStream in = new MirroredInputStream(entry.url);
    9181                InputStream zip = in.getZipEntry("xml","style");
    9282                InputStreamReader ins;
     
    10393                }
    10494            } catch(IOException e) {
    105                 System.err.println(tr("Warning: failed to load Mappaint styles from ''{0}''. Exception was: {1}", a[1], e.toString()));
     95                System.err.println(tr("Warning: failed to load Mappaint styles from ''{0}''. Exception was: {1}", entry.url, e.toString()));
    10696                e.printStackTrace();
    10797            } catch(SAXParseException e) {
    108                 System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: [{1}:{2}] {3}", a[1], e.getLineNumber(), e.getColumnNumber(), e.getMessage()));
     98                System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: [{1}:{2}] {3}", entry.url, e.getLineNumber(), e.getColumnNumber(), e.getMessage()));
    10999                e.printStackTrace();
    110100            } catch(SAXException e) {
    111                 System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: {1}", a[1], e.getMessage()));
     101                System.err.println(tr("Warning: failed to parse Mappaint styles from ''{0}''. Error was: {1}", entry.url, e.getMessage()));
    112102                e.printStackTrace();
    113103            }
  • trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java

    r3467 r3796  
    22package org.openstreetmap.josm.gui.preferences;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
    67import java.awt.GridBagLayout;
     8import java.util.Arrays;
    79import java.util.Collection;
     10import java.util.Collections;
     11import java.util.List;
    812import java.util.TreeSet;
    913
     
    2024import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2125import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
     26import org.openstreetmap.josm.gui.preferences.StyleSourceEditor.StyleSourceInfo;
    2227import org.openstreetmap.josm.tools.GBC;
    2328
     
    4045                Main.pref.getBoolean("mappaint.icon.enable-defaults", true));
    4146
    42         sources = new StyleSourceEditor("mappaint.style.sources", "mappaint.icon.sources",
    43         "http://josm.openstreetmap.de/styles");
     47        sources = new MapPaintSourceEditor();
    4448
    4549        Collection<String> styles = new TreeSet<String>(MapPaintStyles.getStyles().getStyleNames());
     
    6064        final JPanel panel = new JPanel(new GridBagLayout());
    6165        panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    62         panel.add(enableDefault, GBC.std().insets(5,5,5,0));
    63         panel.add(enableIconDefault, GBC.eol().insets(5,5,5,0));
    6466
    6567        panel.add(new JLabel(tr("Used style")), GBC.std().insets(5,5,0,5));
     
    6870
    6971        panel.add(sources, GBC.eol().fill(GBC.BOTH));
     72        panel.add(enableIconDefault, GBC.eol().insets(11,2,5,0));
     73
    7074        gui.mapcontent.addTab(tr("Map Paint Styles"), panel);
    7175
     
    8488    }
    8589
     90    class MapPaintSourceEditor extends StyleSourceEditor {
     91
     92        final private String iconpref = "mappaint.icon.sources";
     93
     94        public MapPaintSourceEditor() {
     95            super("http://josm.openstreetmap.de/styles");
     96        }
     97
     98        @Override
     99        public Collection<? extends SourceEntry> getInitialSourcesList() {
     100            return (new MapPaintPrefMigration()).get();
     101        }
     102
     103        @Override
     104        public boolean finish() {
     105            List<SourceEntry> activeStyles = activeStylesModel.getStyles();
     106
     107            boolean changed = (new MapPaintPrefMigration()).put(activeStyles);
     108
     109            if (tblIconPaths != null) {
     110                List<String> iconPaths = iconPathsModel.getIconPaths();
     111
     112                if (!iconPaths.isEmpty()) {
     113                    if (Main.pref.putCollection(iconpref, iconPaths)) {
     114                        changed = true;
     115                    }
     116                } else if (Main.pref.putCollection(iconpref, null)) {
     117                    changed = true;
     118                }
     119            }
     120            return changed;
     121        }
     122
     123        @Override
     124        public Collection<StyleSourceInfo> getDefault() {
     125            return (new MapPaintPrefMigration()).getDefault();
     126        }
     127
     128        @Override
     129        public Collection<String> getInitialIconPathsList() {
     130            return Main.pref.getCollection(iconpref, null);
     131        }
     132
     133        @Override
     134        public String getStr(I18nString ident) {
     135            switch (ident) {
     136                case AVAILABLE_SOURCES:
     137                    return tr("Available styles:");
     138                case ACTIVE_SOURCES:
     139                    return tr("Active styles:");
     140                case NEW_SOURCE_ENTRY:
     141                    return tr("New style entry:");
     142                case REMOVE_SOURCE_TOOLTIP:
     143                    return tr("Remove the selected styles from the list of active styles");
     144                case EDIT_SOURCE_TOOLTIP:
     145                    return tr("Edit the filename or URL for the selected active style");
     146                case ACTIVATE_TOOLTIP:
     147                    return tr("Add the selected available styles to the list of active styles");
     148                case RELOAD_ALL_AVAILABLE:
     149                    return marktr("Reloads the list of available styles from ''{0}''");
     150                case LOADING_SOURCES_FROM:
     151                    return marktr("Loading style sources from ''{0}''");
     152                case FAILED_TO_LOAD_SOURCES_FROM:
     153                    return marktr("<html>Failed to load the list of style sources from<br>"
     154                            + "''{0}''.<br>"
     155                            + "<br>"
     156                            + "Details (untranslated):<br>{1}</html>");
     157                case FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC:
     158                    return "/Preferences/Styles#FailedToLoadStyleSources";
     159                case ILLEGAL_FORMAT_OF_ENTRY:
     160                    return marktr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''");
     161                default: throw new AssertionError();
     162            }
     163        }
     164
     165    }
     166
    86167    public boolean ok() {
    87168        Boolean restart = Main.pref.put("mappaint.style.enable-defaults", enableDefault.isSelected());
     
    112193        MapPaintStyles.readFromPreferences();
    113194    }
     195
     196    public static class MapPaintPrefMigration extends StyleSourceEditor.SourcePrefMigration {
     197
     198        public MapPaintPrefMigration() {
     199            super("mappaint.style.sources",
     200                  "mappaint.style.enable-defaults",
     201                  "mappaint.style.sources-list");
     202        }
     203
     204        @Override
     205        public Collection<StyleSourceInfo> getDefault() {
     206            StyleSourceInfo i = new StyleSourceInfo("elemstyles.xml", "resource://data/elemstyles.xml");
     207            i.name = "standard";
     208            i.shortdescription = tr("Internal Style");
     209            i.description = tr("Internal style to be used as base for runtime switchable overlay styles");
     210            return Collections.singletonList(i);
     211        }
     212
     213        @Override
     214        public Collection<String> serialize(SourceEntry entry) {
     215            return Arrays.asList(new String[] {entry.url, entry.name, entry.shortdescription, Boolean.toString(entry.active)});
     216        }
     217
     218        @Override
     219        public SourceEntry deserialize(List<String> entryStr) {
     220            if (entryStr.size() < 4)
     221                return null;
     222            String url = entryStr.get(0);
     223            String name = entryStr.get(1);
     224            String shortdescription = entryStr.get(2);
     225            boolean active = Boolean.parseBoolean(entryStr.get(3));
     226            return new SourceEntry(url, name, shortdescription, active);
     227        }
     228    }
    114229}
  • trunk/src/org/openstreetmap/josm/gui/preferences/StyleSourceEditor.java

    r3754 r3796  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
     6import static org.openstreetmap.josm.tools.Utils.equal;
    67
    78import java.awt.Component;
     9import java.awt.Dimension;
     10import java.awt.Font;
    811import java.awt.GridBagConstraints;
    912import java.awt.GridBagLayout;
     13import java.awt.Insets;
    1014import java.awt.event.ActionEvent;
    1115import java.awt.event.FocusAdapter;
    1216import java.awt.event.FocusEvent;
    1317import java.awt.event.KeyEvent;
     18import java.awt.event.MouseAdapter;
    1419import java.awt.event.MouseEvent;
    1520import java.io.BufferedReader;
     
    3439import javax.swing.AbstractAction;
    3540import javax.swing.BorderFactory;
     41import javax.swing.Box;
    3642import javax.swing.DefaultListModel;
    3743import javax.swing.DefaultListSelectionModel;
     
    5561import javax.swing.event.ListSelectionEvent;
    5662import javax.swing.event.ListSelectionListener;
     63import javax.swing.event.TableModelEvent;
     64import javax.swing.event.TableModelListener;
    5765import javax.swing.table.AbstractTableModel;
     66import javax.swing.table.DefaultTableCellRenderer;
    5867import javax.swing.table.TableCellEditor;
     68import javax.swing.table.TableCellRenderer;
    5969
    6070import org.openstreetmap.josm.Main;
     71import org.openstreetmap.josm.gui.ExtendedDialog;
    6172import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    6273import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     
    6879import org.xml.sax.SAXException;
    6980
    70 public class StyleSourceEditor extends JPanel {
    71     private JTable tblActiveStyles;
    72     private ActiveStylesModel activeStylesModel;
    73     private JList lstAvailableStyles;
    74     private AvailableStylesListModel availableStylesModel;
    75     private JTable tblIconPaths = null;
    76     private IconPathTableModel iconPathsModel;
    77     private String pref;
    78     private String iconpref;
    79     private boolean stylesInitiallyLoaded;
    80     private String availableStylesUrl;
     81public abstract class StyleSourceEditor extends JPanel {
     82
     83    protected JTable tblActiveStyles;
     84    protected ActiveStylesModel activeStylesModel;
     85    protected JList lstAvailableStyles;
     86    protected AvailableStylesListModel availableStylesModel;
     87    protected JTable tblIconPaths = null;
     88    protected IconPathTableModel iconPathsModel;
     89    protected boolean stylesInitiallyLoaded;
     90    protected String availableStylesUrl;
    8191
    8292    /**
    83      *
    84      * @param stylesPreferencesKey the preferences key with the list of active style sources (filenames and URLs)
    85      * @param iconsPreferenceKey the preference key with the list of icon sources (can be null)
     93     * constructor
    8694     * @param availableStylesUrl the URL to the list of available style sources
    8795     */
    88     public StyleSourceEditor(String stylesPreferencesKey, String iconsPreferenceKey, final String availableStylesUrl) {
     96    public StyleSourceEditor(final String availableStylesUrl) {
    8997
    9098        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
     99        lstAvailableStyles = new JList(availableStylesModel = new AvailableStylesListModel(selectionModel));
     100        lstAvailableStyles.setSelectionModel(selectionModel);
     101        lstAvailableStyles.setCellRenderer(new StyleSourceCellRenderer());
     102        this.availableStylesUrl = availableStylesUrl;
     103
     104        selectionModel = new DefaultListSelectionModel();
    91105        tblActiveStyles = new JTable(activeStylesModel = new ActiveStylesModel(selectionModel));
    92106        tblActiveStyles.putClientProperty("terminateEditOnFocusLost", true);
    93107        tblActiveStyles.setSelectionModel(selectionModel);
    94108        tblActiveStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     109        tblActiveStyles.setShowGrid(false);
     110        tblActiveStyles.setIntercellSpacing(new Dimension(0, 0));
    95111        tblActiveStyles.setTableHeader(null);
    96         tblActiveStyles.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(true));
    97         tblActiveStyles.setRowHeight(20);
    98         activeStylesModel.setActiveStyles(Main.pref.getCollection(stylesPreferencesKey, null));
    99 
    100         selectionModel = new DefaultListSelectionModel();
    101         lstAvailableStyles = new JList(availableStylesModel =new AvailableStylesListModel(selectionModel));
    102         lstAvailableStyles.setSelectionModel(selectionModel);
    103         lstAvailableStyles.setCellRenderer(new StyleSourceCellRenderer());
    104         this.availableStylesUrl = availableStylesUrl;
    105 
    106         this.pref = stylesPreferencesKey;
    107         this.iconpref = iconsPreferenceKey;
    108 
    109         EditActiveStyleAction editActiveStyleAction = new EditActiveStyleAction();
     112        tblActiveStyles.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
     113        SourceEntryRenderer sourceEntryRenderer = new SourceEntryRenderer();
     114        tblActiveStyles.getColumnModel().getColumn(0).setCellRenderer(sourceEntryRenderer);
     115        activeStylesModel.addTableModelListener(new TableModelListener() {
     116            // Force swing to show horizontal scrollbars for the JTable
     117            // Yes, this is a little ugly, but should work
     118            @Override
     119            public void tableChanged(TableModelEvent e) {
     120                adjustColumnWidth(tblActiveStyles, 0);
     121            }
     122        });
     123        activeStylesModel.setActiveStyles(getInitialSourcesList());
     124       
     125        final EditActiveStyleAction editActiveStyleAction = new EditActiveStyleAction();
    110126        tblActiveStyles.getSelectionModel().addListSelectionListener(editActiveStyleAction);
     127        tblActiveStyles.addMouseListener(new MouseAdapter() {
     128            @Override
     129            public void mouseClicked(MouseEvent e) {
     130                if (e.getClickCount() == 2) {
     131                    int row = tblActiveStyles.rowAtPoint(e.getPoint());
     132                    if (row < 0 || row >= tblActiveStyles.getRowCount())
     133                        return;
     134                    editActiveStyleAction.actionPerformed(null);
     135                }
     136            }
     137        });
    111138
    112139        RemoveActiveStylesAction removeActiveStylesAction = new RemoveActiveStylesAction();
     
    121148        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    122149        setLayout(new GridBagLayout());
    123         add(new JLabel(tr("Active styles:")), GBC.eol().insets(11, 5, 5, 0));
     150
     151        GridBagConstraints gbc = new GridBagConstraints();
     152        gbc.gridx = 0;
     153        gbc.gridy = 0;
     154        gbc.weightx = 0.5;
     155        gbc.gridwidth = 2;
     156        gbc.anchor = GBC.WEST;
     157        gbc.insets = new Insets(5, 11, 0, 0);
     158
     159        add(new JLabel(getStr(I18nString.AVAILABLE_SOURCES)), gbc);
     160
     161        gbc.gridx = 2;
     162        gbc.insets = new Insets(5, 0, 0, 6);
     163
     164        add(new JLabel(getStr(I18nString.ACTIVE_SOURCES)), gbc);
     165
     166        gbc.gridwidth = 1;
     167        gbc.gridx = 0;
     168        gbc.gridy++;
     169        gbc.weighty = 0.8;
     170        gbc.fill = GBC.BOTH;
     171        gbc.anchor = GBC.CENTER;
     172        gbc.insets = new Insets(0, 11, 0, 0);
     173
     174        JScrollPane sp1 = new JScrollPane(lstAvailableStyles);
     175        add(sp1, gbc);
     176
     177        gbc.gridx = 1;
     178        gbc.weightx = 0.0;
     179        gbc.fill = GBC.VERTICAL;
     180        gbc.insets = new Insets(0, 0, 0, 0);
     181
     182        JToolBar middleTB = new JToolBar();
     183        middleTB.setFloatable(false);
     184        middleTB.setBorderPainted(false);
     185        middleTB.setOpaque(false);
     186        middleTB.add(Box.createHorizontalGlue());
     187        middleTB.add(activate);
     188        middleTB.add(Box.createHorizontalGlue());
     189        add(middleTB, gbc);
     190
     191        gbc.gridx++;
     192        gbc.weightx = 0.5;
     193        gbc.fill = GBC.BOTH;
     194
    124195        JScrollPane sp = new JScrollPane(tblActiveStyles);
    125         add(sp, GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
     196        add(sp, gbc);
    126197        sp.setColumnHeaderView(null);
     198
     199        gbc.gridx++;
     200        gbc.weightx = 0.0;
     201        gbc.fill = GBC.VERTICAL;
     202        gbc.insets = new Insets(0, 0, 0, 6);
    127203
    128204        JToolBar sideButtonTB = new JToolBar(JToolBar.VERTICAL);
     
    133209        sideButtonTB.add(editActiveStyleAction);
    134210        sideButtonTB.add(removeActiveStylesAction);
    135         add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
    136 
    137         JToolBar bottomButtonTB = new JToolBar();
    138         bottomButtonTB.setFloatable(false);
    139         bottomButtonTB.setBorderPainted(false);
    140         bottomButtonTB.setOpaque(false);
    141         bottomButtonTB.add(activate);
    142         add(bottomButtonTB, GBC.eol().insets(12, 4, 5, 4).fill(GBC.HORIZONTAL));
    143 
    144         add(new JLabel(tr("Available styles (from {0}):", availableStylesUrl)), GBC.eol().insets(11, 0, 5, 0));
    145         add(new JScrollPane(lstAvailableStyles), GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
    146 
    147         sideButtonTB = new JToolBar(JToolBar.VERTICAL);
    148         sideButtonTB.setFloatable(false);
    149         sideButtonTB.setBorderPainted(false);
    150         sideButtonTB.setOpaque(false);
    151         sideButtonTB.add(new ReloadStylesAction(availableStylesUrl));
    152         add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
    153        
    154         if (iconsPreferenceKey != null) {
    155             selectionModel = new DefaultListSelectionModel();
    156             tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
    157             tblIconPaths.setSelectionModel(selectionModel);
    158             tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    159             tblIconPaths.setTableHeader(null);
    160             tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(false));
    161             tblIconPaths.setRowHeight(20);
    162             iconPathsModel.setIconPaths(Main.pref.getCollection(iconsPreferenceKey, null));
    163 
    164             EditIconPathAction editIconPathAction = new EditIconPathAction();
    165             tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
    166 
    167             RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
    168             tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
    169             tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
    170             tblIconPaths.getActionMap().put("delete", removeIconPathAction);
    171 
    172             add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 10, 5, 10));
    173             add(new JLabel(tr("Icon paths:")), GBC.eol().insets(11, 0, 5, 0));
    174             add(sp = new JScrollPane(tblIconPaths), GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
    175             sp.setColumnHeaderView(null);
    176             sideButtonTB = new JToolBar(JToolBar.VERTICAL);
    177             sideButtonTB.setFloatable(false);
    178             sideButtonTB.setBorderPainted(false);
    179             sideButtonTB.setOpaque(false);
    180             add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
    181             sideButtonTB.add(new NewIconPathAction());
    182             sideButtonTB.add(editIconPathAction);
    183             sideButtonTB.add(removeIconPathAction);
    184         }
     211        add(sideButtonTB, gbc);
     212
     213        gbc.gridx = 0;
     214        gbc.gridy++;
     215        gbc.weighty = 0.0;
     216        gbc.weightx = 0.5;
     217        gbc.fill = GBC.HORIZONTAL;
     218        gbc.anchor = GBC.WEST;
     219        gbc.insets = new Insets(0, 11, 0, 0);
     220
     221        JToolBar bottomLeftTB = new JToolBar(JToolBar.VERTICAL);
     222        bottomLeftTB.setFloatable(false);
     223        bottomLeftTB.setBorderPainted(false);
     224        bottomLeftTB.setOpaque(false);
     225        bottomLeftTB.add(new ReloadStylesAction(availableStylesUrl));
     226        middleTB.add(Box.createHorizontalGlue());
     227        add(bottomLeftTB, gbc);
     228
     229        gbc.gridx = 2;
     230        gbc.anchor = GBC.CENTER;
     231        gbc.insets = new Insets(0, 0, 0, 0);
     232
     233        JToolBar bottomRightTB = new JToolBar();
     234        bottomRightTB.setFloatable(false);
     235        bottomRightTB.setBorderPainted(false);
     236        bottomRightTB.setOpaque(false);
     237        bottomRightTB.add(Box.createHorizontalGlue());
     238        bottomRightTB.add(new JButton(new ResetAction()));
     239        add(bottomRightTB, gbc);
     240
     241        /***
     242         * Icon configuration
     243         **/
     244
     245        selectionModel = new DefaultListSelectionModel();
     246        tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
     247        tblIconPaths.setSelectionModel(selectionModel);
     248        tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     249        tblIconPaths.setTableHeader(null);
     250        tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(false));
     251        tblIconPaths.setRowHeight(20);
     252        tblIconPaths.putClientProperty("terminateEditOnFocusLost", true);
     253        iconPathsModel.setIconPaths(getInitialIconPathsList());
     254
     255        EditIconPathAction editIconPathAction = new EditIconPathAction();
     256        tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
     257
     258        RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
     259        tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
     260        tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
     261        tblIconPaths.getActionMap().put("delete", removeIconPathAction);
     262
     263        gbc.gridx = 0;
     264        gbc.gridy++;
     265        gbc.weightx = 1.0;
     266        gbc.gridwidth = GBC.REMAINDER;
     267        gbc.insets = new Insets(8, 11, 8, 6);
     268
     269        add(new JSeparator(), gbc);
     270
     271        gbc.gridy++;
     272        gbc.insets = new Insets(0, 11, 0, 6);
     273
     274        add(new JLabel(tr("Icon paths:")), gbc);
     275
     276        gbc.gridy++;
     277        gbc.weighty = 0.2;
     278        gbc.gridwidth = 3;
     279        gbc.fill = GBC.BOTH;
     280        gbc.insets = new Insets(0, 11, 0, 0);
     281
     282        add(sp = new JScrollPane(tblIconPaths), gbc);
     283        sp.setColumnHeaderView(null);
     284
     285        gbc.gridx = 3;
     286        gbc.gridwidth = 1;
     287        gbc.weightx = 0.0;
     288        gbc.fill = GBC.VERTICAL;
     289        gbc.insets = new Insets(0, 0, 0, 6);
     290
     291        JToolBar sideButtonTBIcons = new JToolBar(JToolBar.VERTICAL);
     292        sideButtonTBIcons.setFloatable(false);
     293        sideButtonTBIcons.setBorderPainted(false);
     294        sideButtonTBIcons.setOpaque(false);
     295        sideButtonTBIcons.add(new NewIconPathAction());
     296        sideButtonTBIcons.add(editIconPathAction);
     297        sideButtonTBIcons.add(removeIconPathAction);
     298        add(sideButtonTBIcons, gbc);
     299    }
     300
     301    /**
     302     * Load the list of source entries that the user has configured.
     303     */
     304    abstract public Collection<? extends SourceEntry> getInitialSourcesList();
     305
     306    /**
     307     * Load the list of configured icon paths.
     308     */
     309    abstract public Collection<String> getInitialIconPathsList();
     310
     311    /**
     312     * Get the default list of entries (used when resetting the list).
     313     */
     314    abstract public Collection<StyleSourceInfo> getDefault();
     315
     316    /**
     317     * Save the settings after user clicked "Ok".
     318     * @return true if restart is required
     319     */
     320    abstract public boolean finish();
     321
     322    /**
     323     * Provide the GUI strings. (There are differences for MapPaint and Preset)
     324     */
     325    abstract protected String getStr(I18nString ident);
     326
     327    /**
     328     * Identifiers for strings that need to be provided.
     329     */
     330    protected enum I18nString { AVAILABLE_SOURCES, ACTIVE_SOURCES, NEW_SOURCE_ENTRY,
     331            REMOVE_SOURCE_TOOLTIP, EDIT_SOURCE_TOOLTIP, ACTIVATE_TOOLTIP, RELOAD_ALL_AVAILABLE,
     332            LOADING_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM, FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC,
     333            ILLEGAL_FORMAT_OF_ENTRY }
     334
     335    /**
     336     * adjust the preferred width of column col to the maximum preferred width of the cells
     337     * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
     338     */
     339    private static void adjustColumnWidth(JTable tbl, int col) {
     340        int maxwidth = 0;
     341                for (int row=0; row<tbl.getRowCount(); row++) {
     342            TableCellRenderer tcr = tbl.getCellRenderer(row, col);
     343                Object val = tbl.getValueAt(row, col);
     344                Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col);
     345                maxwidth = Math.max(comp.getPreferredSize().width, maxwidth);
     346                }
     347                tbl.getColumnModel().getColumn(col).setPreferredWidth(maxwidth);
    185348    }
    186349
    187350    public boolean hasActiveStylesChanged() {
    188         return !activeStylesModel.getStyles().equals(Main.pref.getCollection(pref, Collections.<String>emptyList()));
    189     }
    190 
    191     public Collection<String> getActiveStyles() {
     351        Collection<? extends SourceEntry> prev = getInitialSourcesList();
     352        List<SourceEntry> cur = activeStylesModel.getStyles();
     353        if (prev.size() != cur.size())
     354            return true;
     355        Iterator<? extends SourceEntry> p = prev.iterator();
     356        Iterator<SourceEntry> c = cur.iterator();
     357        while (p.hasNext()) {
     358            SourceEntry pe = p.next();
     359            SourceEntry ce = c.next();
     360            if (!equal(pe.url, ce.url) || !equal(pe.name, ce.name) || pe.active != ce.active)
     361                return true;
     362        }
     363        return false;
     364    }
     365
     366    public Collection<SourceEntry> getActiveStyles() {
    192367        return activeStylesModel.getStyles();
    193368    }
    194369
    195     public void removeSource(String source) {
    196         activeStylesModel.remove(source);
    197     }
    198 
    199     public boolean finish() {
    200         boolean changed = false;
    201         List<String> activeStyles = activeStylesModel.getStyles();
    202 
    203         if (activeStyles.size() > 0) {
    204             if (Main.pref.putCollection(pref, activeStyles)) {
    205                 changed = true;
    206             }
    207         } else if (Main.pref.putCollection(pref, null)) {
    208             changed = true;
    209         }
    210 
    211         if (tblIconPaths != null) {
    212             List<String> iconPaths = iconPathsModel.getIconPaths();
    213 
    214             if (!iconPaths.isEmpty()) {
    215                 if (Main.pref.putCollection(iconpref, iconPaths)) {
    216                     changed = true;
    217                 }
    218             } else if (Main.pref.putCollection(iconpref, null)) {
    219                 changed = true;
    220             }
    221         }
    222         return changed;
     370    public void removeSources(Collection<Integer> idxs) {
     371        activeStylesModel.removeIdxs(idxs);
    223372    }
    224373
     
    234383    }
    235384
    236     static class AvailableStylesListModel extends DefaultListModel {
     385    protected static class AvailableStylesListModel extends DefaultListModel {
    237386        private ArrayList<StyleSourceInfo> data;
    238387        private DefaultListSelectionModel selectionModel;
     
    286435    }
    287436
    288     static class ActiveStylesModel extends AbstractTableModel {
    289         private ArrayList<String> data;
     437    protected static class ActiveStylesModel extends AbstractTableModel {
     438        private List<SourceEntry> data;
    290439        private DefaultListSelectionModel selectionModel;
    291440
    292441        public ActiveStylesModel(DefaultListSelectionModel selectionModel) {
    293442            this.selectionModel = selectionModel;
    294             this.data = new ArrayList<String>();
     443            this.data = new ArrayList<SourceEntry>();
    295444        }
    296445
     
    303452        }
    304453
    305         public Object getValueAt(int rowIndex, int columnIndex) {
     454        @Override
     455        public SourceEntry getValueAt(int rowIndex, int columnIndex) {
    306456            return data.get(rowIndex);
    307457        }
     
    309459        @Override
    310460        public boolean isCellEditable(int rowIndex, int columnIndex) {
    311             return true;
     461            return false;
    312462        }
    313463
     
    317467        }
    318468
    319         public void setActiveStyles(Collection<String> styles) {
     469        public void setActiveStyles(Collection<? extends SourceEntry> sources) {
     470            //abstract public Collection<? extends StyleSourceEntry> getInitialStyleSources();
    320471            data.clear();
    321             if (styles !=null) {
    322                 data.addAll(styles);
    323             }
    324             sort();
     472            if (sources != null) {
     473                data.addAll(sources);
     474            }
    325475            fireTableDataChanged();
    326476        }
    327477
    328         public void addStyle(String style) {
     478        public void addStyle(SourceEntry style) {
    329479            if (style == null) return;
    330480            data.add(style);
    331             sort();
    332481            fireTableDataChanged();
    333482            int idx = data.indexOf(style);
     
    340489            if (style == null) return;
    341490            if (pos < 0 || pos >= getRowCount()) return;
    342             data.set(pos, style);
     491            data.get(pos).url = style;
     492            fireTableDataChanged();
     493            int idx = data.indexOf(style);
     494            if (idx >= 0) {
     495                selectionModel.setSelectionInterval(idx, idx);
     496            }
     497        }
     498
     499        public void removeSelected() {
     500            Iterator<SourceEntry> it = data.iterator();
     501            int i=0;
     502            while(it.hasNext()) {
     503                it.next();
     504                if (selectionModel.isSelectedIndex(i)) {
     505                    it.remove();
     506                }
     507                i++;
     508            }
     509            fireTableDataChanged();
     510        }
     511
     512        public void removeIdxs(Collection<Integer> idxs) {
     513            List<SourceEntry> newData = new ArrayList<SourceEntry>();
     514            for (int i=0; i<data.size(); ++i) {
     515                if (!idxs.contains(i)) {
     516                    newData.add(data.get(i));
     517                }
     518            }
     519            data = newData;
     520            fireTableDataChanged();
     521        }
     522
     523        public void addStylesFromSources(List<StyleSourceInfo> sources) {
     524            if (sources == null) return;
     525            for (StyleSourceInfo info: sources) {
     526                data.add(new SourceEntry(info.url, info.name, info.getDisplayName(), true));
     527            }
     528            fireTableDataChanged();
     529            selectionModel.clearSelection();
     530            for (StyleSourceInfo info: sources) {
     531                int pos = data.indexOf(info);
     532                if (pos >=0) {
     533                    selectionModel.addSelectionInterval(pos, pos);
     534                }
     535            }
     536        }
     537
     538        public List<SourceEntry> getStyles() {
     539            return new ArrayList<SourceEntry>(data);
     540        }
     541    }
     542
     543    public static class StyleSourceInfo extends SourceEntry {
     544        public String simpleFileName;
     545        public String version;
     546        public String author;
     547        public String link;
     548        public String description;
     549
     550        public StyleSourceInfo(String simpleFileName, String url) {
     551            super(url, null, null, true);
     552            this.simpleFileName = simpleFileName;
     553            version = author = link = description = shortdescription = null;
     554        }
     555
     556        /**
     557         * @return string representation for GUI list or menu entry
     558         */
     559        public String getDisplayName() {
     560            return shortdescription == null ? simpleFileName : shortdescription;
     561        }
     562
     563        public String getTooltip() {
     564            String s = tr("Short Description: {0}", getDisplayName()) + "<br>" + tr("URL: {0}", url);
     565            if (author != null) {
     566                s += "<br>" + tr("Author: {0}", author);
     567            }
     568            if (link != null) {
     569                s += "<br>" + tr("Webpage: {0}", link);
     570            }
     571            if (description != null) {
     572                s += "<br>" + tr("Description: {0}", description);
     573            }
     574            if (version != null) {
     575                s += "<br>" + tr("Version: {0}", version);
     576            }
     577            return "<html>" + s + "</html>";
     578        }
     579
     580        @Override
     581        public String toString() {
     582            return "<html><b>" + getDisplayName() + "</b> (" + url + ")</html>";
     583        }
     584    }
     585
     586    protected class EditSourceEntryDialog extends ExtendedDialog {
     587
     588        /**
     589         * We call this text field "name", but it is actually the shortdescription.
     590         */
     591        private JTextField tfName;
     592        private JTextField tfURL;
     593
     594        public EditSourceEntryDialog(Component parent, String title, SourceEntry e) {
     595            super(parent,
     596                    title,
     597                    new String[] {tr("Ok"), tr("Cancel")});
     598
     599            JPanel p = new JPanel(new GridBagLayout());
     600
     601            tfName = new JTextField(60);
     602            p.add(new JLabel(tr("Name (optional):")), GBC.std().insets(15, 0, 5, 5));
     603            p.add(tfName, GBC.eol().insets(0, 0, 5, 5));
     604
     605            tfURL = new JTextField(60);
     606            p.add(new JLabel(tr("URL / File:")), GBC.std().insets(15, 0, 5, 0));
     607            p.add(tfURL, GBC.std().insets(0, 0, 5, 0));
     608            JButton fileChooser = new JButton(new LaunchFileChooserAction());
     609            fileChooser.setMargin(new Insets(0, 0, 0, 0));
     610            p.add(fileChooser, GBC.eol().insets(0, 0, 5, 0));
     611
     612            if (e != null) {
     613                if (e.shortdescription != null) {
     614                    tfName.setText(e.shortdescription);
     615                }
     616                tfURL.setText(e.url);
     617            }
     618
     619            setButtonIcons(new String[] {"ok", "cancel"});
     620            setContent(p);
     621        }
     622
     623        class LaunchFileChooserAction extends AbstractAction {
     624            public LaunchFileChooserAction() {
     625                putValue(SMALL_ICON, ImageProvider.get("open"));
     626                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
     627            }
     628
     629            protected void prepareFileChooser(String url, JFileChooser fc) {
     630                if (url == null || url.trim().length() == 0) return;
     631                URL sourceUrl = null;
     632                try {
     633                    sourceUrl = new URL(url);
     634                } catch(MalformedURLException e) {
     635                    File f = new File(url);
     636                    if (f.isFile()) {
     637                        f = f.getParentFile();
     638                    }
     639                    if (f != null) {
     640                        fc.setCurrentDirectory(f);
     641                    }
     642                    return;
     643                }
     644                if (sourceUrl.getProtocol().startsWith("file")) {
     645                    File f = new File(sourceUrl.getPath());
     646                    if (f.isFile()) {
     647                        f = f.getParentFile();
     648                    }
     649                    if (f != null) {
     650                        fc.setCurrentDirectory(f);
     651                    }
     652                }
     653            }
     654
     655            public void actionPerformed(ActionEvent e) {
     656                JFileChooser fc= new JFileChooser();
     657                prepareFileChooser(tfURL.getText(), fc);
     658                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(StyleSourceEditor.this));
     659                if (ret != JFileChooser.APPROVE_OPTION)
     660                    return;
     661                tfURL.setText(fc.getSelectedFile().toString());
     662            }
     663        }
     664
     665        public String getShortdescription() {
     666            return tfName.getText();
     667        }
     668
     669        public String getURL() {
     670            return tfURL.getText();
     671        }
     672    }
     673
     674    class NewActiveStyleAction extends AbstractAction {
     675        public NewActiveStyleAction() {
     676            putValue(NAME, tr("New"));
     677            putValue(SHORT_DESCRIPTION, tr("Add a filename or an URL of an active style"));
     678            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
     679        }
     680
     681        public void actionPerformed(ActionEvent evt) {
     682            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
     683                    StyleSourceEditor.this,
     684                    getStr(I18nString.NEW_SOURCE_ENTRY),
     685                    null);
     686            editEntryDialog.showDialog();
     687            if (editEntryDialog.getValue() == 1) {
     688                activeStylesModel.addStyle(new SourceEntry(
     689                        editEntryDialog.getURL(),
     690                        null, editEntryDialog.getShortdescription(), true));
     691                activeStylesModel.fireTableDataChanged();
     692            }
     693        }
     694    }
     695
     696    class RemoveActiveStylesAction extends AbstractAction implements ListSelectionListener {
     697
     698        public RemoveActiveStylesAction() {
     699            putValue(NAME, tr("Remove"));
     700            putValue(SHORT_DESCRIPTION, getStr(I18nString.REMOVE_SOURCE_TOOLTIP));
     701            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
     702            updateEnabledState();
     703        }
     704
     705        protected void updateEnabledState() {
     706            setEnabled(tblActiveStyles.getSelectedRowCount() > 0);
     707        }
     708
     709        public void valueChanged(ListSelectionEvent e) {
     710            updateEnabledState();
     711        }
     712
     713        public void actionPerformed(ActionEvent e) {
     714            activeStylesModel.removeSelected();
     715        }
     716    }
     717
     718    class EditActiveStyleAction extends AbstractAction implements ListSelectionListener {
     719        public EditActiveStyleAction() {
     720            putValue(NAME, tr("Edit"));
     721            putValue(SHORT_DESCRIPTION, getStr(I18nString.EDIT_SOURCE_TOOLTIP));
     722            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
     723            updateEnabledState();
     724        }
     725
     726        protected void updateEnabledState() {
     727            setEnabled(tblActiveStyles.getSelectedRowCount() == 1);
     728        }
     729
     730        public void valueChanged(ListSelectionEvent e) {
     731            updateEnabledState();
     732        }
     733
     734        public void actionPerformed(ActionEvent evt) {
     735            int pos = tblActiveStyles.getSelectedRow();
     736            if (pos < 0 || pos >= tblActiveStyles.getRowCount())
     737                return;
     738
     739            SourceEntry e = activeStylesModel.getValueAt(pos, 0);
     740
     741            EditSourceEntryDialog editEntryDialog = new EditSourceEntryDialog(
     742                    StyleSourceEditor.this, tr("Edit source entry:"), e);
     743            editEntryDialog.showDialog();
     744            if (editEntryDialog.getValue() == 1) {
     745                if (e.shortdescription != null || !equal(editEntryDialog.getShortdescription(), "")) {
     746                    e.shortdescription = editEntryDialog.getShortdescription();
     747                    if (equal(e.shortdescription, "")) {
     748                        e.shortdescription = null;
     749                    }
     750                }
     751                e.url = editEntryDialog.getURL();
     752                activeStylesModel.fireTableCellUpdated(pos, 0);
     753            }
     754        }
     755    }
     756
     757    class ActivateStylesAction extends AbstractAction implements ListSelectionListener {
     758        public ActivateStylesAction() {
     759            putValue(SHORT_DESCRIPTION, getStr(I18nString.ACTIVATE_TOOLTIP));
     760            putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
     761            updateEnabledState();
     762        }
     763
     764        protected void updateEnabledState() {
     765            setEnabled(lstAvailableStyles.getSelectedIndices().length > 0);
     766        }
     767
     768        public void valueChanged(ListSelectionEvent e) {
     769            updateEnabledState();
     770        }
     771
     772        public void actionPerformed(ActionEvent e) {
     773            List<StyleSourceInfo> styleSources = availableStylesModel.getSelected();
     774            activeStylesModel.addStylesFromSources(styleSources);
     775        }
     776    }
     777
     778    class ResetAction extends AbstractAction {
     779
     780        public ResetAction() {
     781            putValue(NAME, tr("Reset"));
     782            putValue(SHORT_DESCRIPTION, tr("Reset to default"));
     783            putValue(SMALL_ICON, ImageProvider.get("preferences", "reset"));
     784        }
     785
     786        public void actionPerformed(ActionEvent e) {
     787            activeStylesModel.setActiveStyles(getDefault());
     788        }
     789    }
     790
     791    class ReloadStylesAction extends AbstractAction {
     792        private String url;
     793        public ReloadStylesAction(String url) {
     794            putValue(NAME, tr("Reload"));
     795            putValue(SHORT_DESCRIPTION, tr(getStr(I18nString.RELOAD_ALL_AVAILABLE), url));
     796            putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
     797            this.url = url;
     798        }
     799
     800        public void actionPerformed(ActionEvent e) {
     801            MirroredInputStream.cleanup(url);
     802            reloadAvailableStyles(url);
     803        }
     804    }
     805
     806    protected static class IconPathTableModel extends AbstractTableModel {
     807        private ArrayList<String> data;
     808        private DefaultListSelectionModel selectionModel;
     809
     810        public IconPathTableModel(DefaultListSelectionModel selectionModel) {
     811            this.selectionModel = selectionModel;
     812            this.data = new ArrayList<String>();
     813        }
     814
     815        public int getColumnCount() {
     816            return 1;
     817        }
     818
     819        public int getRowCount() {
     820            return data == null ? 0 : data.size();
     821        }
     822
     823        public Object getValueAt(int rowIndex, int columnIndex) {
     824            return data.get(rowIndex);
     825        }
     826
     827        @Override
     828        public boolean isCellEditable(int rowIndex, int columnIndex) {
     829            return true;
     830        }
     831
     832        @Override
     833        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
     834            updatePath(rowIndex, (String)aValue);
     835        }
     836
     837        public void setIconPaths(Collection<String> styles) {
     838            data.clear();
     839            if (styles !=null) {
     840                data.addAll(styles);
     841            }
    343842            sort();
    344843            fireTableDataChanged();
    345             int idx = data.indexOf(style);
     844        }
     845
     846        public void addPath(String path) {
     847            if (path == null) return;
     848            data.add(path);
     849            sort();
     850            fireTableDataChanged();
     851            int idx = data.indexOf(path);
     852            if (idx >= 0) {
     853                selectionModel.setSelectionInterval(idx, idx);
     854            }
     855        }
     856
     857        public void updatePath(int pos, String path) {
     858            if (path == null) return;
     859            if (pos < 0 || pos >= getRowCount()) return;
     860            data.set(pos, path);
     861            sort();
     862            fireTableDataChanged();
     863            int idx = data.indexOf(path);
    346864            if (idx >= 0) {
    347865                selectionModel.setSelectionInterval(idx, idx);
     
    360878            }
    361879            fireTableDataChanged();
    362         }
    363 
    364         public void remove(String source) {
    365             data.remove(source);
    366             fireTableDataChanged();
     880            selectionModel.clearSelection();
    367881        }
    368882
     
    382896        }
    383897
    384         public void addStylesFromSources(List<StyleSourceInfo> sources) {
    385             if (sources == null) return;
    386             for (StyleSourceInfo info: sources) {
    387                 data.add(info.url);
    388             }
    389             sort();
    390             fireTableDataChanged();
    391             selectionModel.clearSelection();
    392             for (StyleSourceInfo info: sources) {
    393                 int pos = data.indexOf(info.url);
    394                 if (pos >=0) {
    395                     selectionModel.addSelectionInterval(pos, pos);
    396                 }
    397             }
    398         }
    399 
    400         public List<String> getStyles() {
    401             return new ArrayList<String>(data);
    402         }
    403 
    404         public String getStyle(int pos) {
    405             return data.get(pos);
    406         }
    407     }
    408 
    409     public static class StyleSourceInfo {
    410         String version;
    411         String name;
    412         String url;
    413         String author;
    414         String link;
    415         String description;
    416         String shortdescription;
    417 
    418         public StyleSourceInfo(String name, String url) {
    419             this.name = name;
    420             this.url = url;
    421             version = author = link = description = shortdescription = null;
    422         }
    423 
    424         public String getName() {
    425             return shortdescription == null ? name : shortdescription;
    426         }
    427 
    428         public String getTooltip() {
    429             String s = tr("Short Description: {0}", getName()) + "<br>" + tr("URL: {0}", url);
    430             if (author != null) {
    431                 s += "<br>" + tr("Author: {0}", author);
    432             }
    433             if (link != null) {
    434                 s += "<br>" + tr("Webpage: {0}", link);
    435             }
    436             if (description != null) {
    437                 s += "<br>" + tr("Description: {0}", description);
    438             }
    439             if (version != null) {
    440                 s += "<br>" + tr("Version: {0}", version);
    441             }
    442             return "<html>" + s + "</html>";
    443         }
    444 
    445         @Override
    446         public String toString() {
    447             return getName() + " (" + url + ")";
    448         }
    449     }
    450 
    451     class NewActiveStyleAction extends AbstractAction {
    452         public NewActiveStyleAction() {
    453             putValue(NAME, tr("New"));
    454             putValue(SHORT_DESCRIPTION, tr("Add a filename or an URL of an active style"));
    455             putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
    456         }
    457 
    458         public void actionPerformed(ActionEvent e) {
    459             activeStylesModel.addStyle("");
    460             tblActiveStyles.requestFocusInWindow();
    461             tblActiveStyles.editCellAt(activeStylesModel.getRowCount()-1, 0);
    462         }
    463     }
    464 
    465     class RemoveActiveStylesAction extends AbstractAction implements ListSelectionListener {
    466 
    467         public RemoveActiveStylesAction() {
    468             putValue(NAME, tr("Remove"));
    469             putValue(SHORT_DESCRIPTION, tr("Remove the selected styles from the list of active styles"));
    470             putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
    471             updateEnabledState();
    472         }
    473 
    474         protected void updateEnabledState() {
    475             setEnabled(tblActiveStyles.getSelectedRowCount() > 0);
    476         }
    477 
    478         public void valueChanged(ListSelectionEvent e) {
    479             updateEnabledState();
    480         }
    481 
    482         public void actionPerformed(ActionEvent e) {
    483             activeStylesModel.removeSelected();
    484         }
    485     }
    486 
    487     class EditActiveStyleAction extends AbstractAction implements ListSelectionListener {
    488         public EditActiveStyleAction() {
    489             putValue(NAME, tr("Edit"));
    490             putValue(SHORT_DESCRIPTION, tr("Edit the filename or URL for the selected active style"));
    491             putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
    492             updateEnabledState();
    493         }
    494 
    495         protected void updateEnabledState() {
    496             setEnabled(tblActiveStyles.getSelectedRowCount() == 1);
    497         }
    498 
    499         public void valueChanged(ListSelectionEvent e) {
    500             updateEnabledState();
    501         }
    502 
    503         public void actionPerformed(ActionEvent e) {
    504             int pos = tblActiveStyles.getSelectedRow();
    505             tblActiveStyles.editCellAt(pos, 0);
    506         }
    507     }
    508 
    509     class ActivateStylesAction extends AbstractAction implements ListSelectionListener {
    510         public ActivateStylesAction() {
    511             putValue(NAME, tr("Activate"));
    512             putValue(SHORT_DESCRIPTION, tr("Add the selected available styles to the list of active styles"));
    513             putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
    514             updateEnabledState();
    515         }
    516 
    517         protected void updateEnabledState() {
    518             setEnabled(lstAvailableStyles.getSelectedIndices().length > 0);
    519         }
    520 
    521         public void valueChanged(ListSelectionEvent e) {
    522             updateEnabledState();
    523         }
    524 
    525         public void actionPerformed(ActionEvent e) {
    526             List<StyleSourceInfo> styleSources = availableStylesModel.getSelected();
    527             activeStylesModel.addStylesFromSources(styleSources);
    528         }
    529     }
    530 
    531     class ReloadStylesAction extends AbstractAction {
    532         private String url;
    533         public ReloadStylesAction(String url) {
    534             putValue(NAME, tr("Reload"));
    535             putValue(SHORT_DESCRIPTION, tr("Reloads the list of available styles from ''{0}''", url));
    536             putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
    537             this.url = url;
    538         }
    539 
    540         public void actionPerformed(ActionEvent e) {
    541             MirroredInputStream.cleanup(url);
    542             reloadAvailableStyles(url);
    543         }
    544     }
    545 
    546     static class IconPathTableModel extends AbstractTableModel {
    547         private ArrayList<String> data;
    548         private DefaultListSelectionModel selectionModel;
    549 
    550         public IconPathTableModel(DefaultListSelectionModel selectionModel) {
    551             this.selectionModel = selectionModel;
    552             this.data = new ArrayList<String>();
    553         }
    554 
    555         public int getColumnCount() {
    556             return 1;
    557         }
    558 
    559         public int getRowCount() {
    560             return data == null ? 0 : data.size();
    561         }
    562 
    563         public Object getValueAt(int rowIndex, int columnIndex) {
    564             return data.get(rowIndex);
    565         }
    566 
    567         @Override
    568         public boolean isCellEditable(int rowIndex, int columnIndex) {
    569             return true;
    570         }
    571 
    572         @Override
    573         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    574             updatePath(rowIndex, (String)aValue);
    575         }
    576 
    577         public void setIconPaths(Collection<String> styles) {
    578             data.clear();
    579             if (styles !=null) {
    580                 data.addAll(styles);
    581             }
    582             sort();
    583             fireTableDataChanged();
    584         }
    585 
    586         public void addPath(String path) {
    587             if (path == null) return;
    588             data.add(path);
    589             sort();
    590             fireTableDataChanged();
    591             int idx = data.indexOf(path);
    592             if (idx >= 0) {
    593                 selectionModel.setSelectionInterval(idx, idx);
    594             }
    595         }
    596 
    597         public void updatePath(int pos, String path) {
    598             if (path == null) return;
    599             if (pos < 0 || pos >= getRowCount()) return;
    600             data.set(pos, path);
    601             sort();
    602             fireTableDataChanged();
    603             int idx = data.indexOf(path);
    604             if (idx >= 0) {
    605                 selectionModel.setSelectionInterval(idx, idx);
    606             }
    607         }
    608 
    609         public void removeSelected() {
    610             Iterator<String> it = data.iterator();
    611             int i=0;
    612             while(it.hasNext()) {
    613                 it.next();
    614                 if (selectionModel.isSelectedIndex(i)) {
    615                     it.remove();
    616                 }
    617                 i++;
    618             }
    619             fireTableDataChanged();
    620             selectionModel.clearSelection();
    621         }
    622 
    623         protected void sort() {
    624             Collections.sort(
    625                     data,
    626                     new Comparator<String>() {
    627                         public int compare(String o1, String o2) {
    628                             if (o1.equals("") && o2.equals(""))
    629                                 return 0;
    630                             if (o1.equals("")) return 1;
    631                             if (o2.equals("")) return -1;
    632                             return o1.compareTo(o2);
    633                         }
    634                     }
    635             );
    636         }
    637 
    638898        public List<String> getIconPaths() {
    639899            return new ArrayList<String>(data);
     
    711971            setEnabled(list.isEnabled());
    712972            setFont(list.getFont());
     973            setFont(getFont().deriveFont(Font.PLAIN));
    713974            setOpaque(true);
    714975            setToolTipText(((StyleSourceInfo) value).getTooltip());
     
    723984
    724985        public StyleSourceLoader(String url) {
    725             super(tr("Loading style sources from ''{0}''", url));
     986            super(tr(getStr(I18nString.LOADING_SOURCES_FROM), url));
    726987            this.url = url;
    727988        }
     
    7451006            String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
    7461007            emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    747             String msg = tr("<html>Failed to load the list of style sources from<br>"
    748                     + "''{0}''.<br>"
    749                     + "<br>"
    750                     + "Details (untranslated):<br>{1}</html>",
    751                     url, emsg
    752             );
     1008            String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg);
    7531009
    7541010            HelpAwareOptionPane.showOptionDialog(
     
    7571013                    tr("Error"),
    7581014                    JOptionPane.ERROR_MESSAGE,
    759                     ht("/Preferences/Styles#FailedToLoadStyleSources")
     1015                    ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC))
    7601016            );
    7611017        }
     
    7661022            String lang = LanguageInfo.getLanguageCodeXML();
    7671023            try {
    768                 StyleSourceInfo i = new StyleSourceInfo("elemstyles.xml", "resource://data/elemstyles.xml");
    769                 i.shortdescription = tr("Internal style");
    770                 i.description = tr("Internal style to be used as base for runtime switchable overlay styles");
    771                 styles.add(i);
     1024                styles.addAll(getDefault());
    7721025                MirroredInputStream stream = new MirroredInputStream(url);
    7731026                InputStreamReader r;
     
    7891042                        Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
    7901043                        if (! m.matches()) {
    791                             System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
     1044                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
    7921045                            continue;
    7931046                        }
     
    8051058                            } else if ("shortdescription".equals(key) && last.shortdescription == null) {
    8061059                                last.shortdescription = value;
     1060                            } else if ("name".equals(key) && last.name == null) {
     1061                                last.name = value;
    8071062                            } else if ((lang + "author").equals(key)) {
    8081063                                last.author = value;
     
    8211076                            styles.add(last = new StyleSourceInfo(m.group(1), m.group(2)));
    8221077                        } else {
    823                             System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
     1078                            System.err.println(tr(getStr(I18nString.ILLEGAL_FORMAT_OF_ENTRY), url, line));
    8241079                        }
    8251080                    }
     
    8351090            }
    8361091            availableStylesModel.setStyleSources(styles);
     1092        }
     1093    }
     1094   
     1095    class SourceEntryRenderer extends DefaultTableCellRenderer {
     1096        @Override
     1097        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     1098            SourceEntry se = (SourceEntry) value;
     1099            JLabel label = (JLabel)super.getTableCellRendererComponent(table,
     1100                    fromSourceEntry(se), isSelected, hasFocus, row, column);
     1101            return label;
     1102        }
     1103
     1104        private String fromSourceEntry(SourceEntry entry) {
     1105            StringBuilder s = new StringBuilder("<html><b>");
     1106            if (entry.shortdescription != null) {
     1107                s.append(entry.shortdescription).append("</b> (");
     1108            }
     1109            s.append(entry.url);
     1110            if (entry.name != null) {
     1111                s.append(")");
     1112            }
     1113            s.append("</html>");
     1114            return s.toString();
    8371115        }
    8381116    }
     
    9951273    }
    9961274
     1275
     1276    /**
     1277     * Convert mappaint and preset source preferences from a simple list to
     1278     * array with one line for each source entry.
     1279     *
     1280     * MapPaint:
     1281     *
     1282     *    Old format
     1283     *      key: mappaint.style.sources
     1284     *      value: list of "<name>=<url>" pairs. The "<name>=" part is optional.
     1285     *          The style is always active.
     1286     *      default: empty list
     1287     *
     1288     *      key: mappaint.style.enable-defaults
     1289     *      value: if true, the default style "resource://data/elemstyles.xml" should
     1290     *          be loaded.
     1291     *      default: true
     1292     *
     1293     *    New format
     1294     *      key: mappaint.style.sources-list
     1295     *      value: each line is a list with entries: url, name, shortdescription, active
     1296     *      default:
     1297     *          One line: "resource://data/elemstyles.xml", "standard", tr("Internal Style"), true
     1298     *
     1299     * Tagging Preset:
     1300     *
     1301     *      the same, but "name" and "active" are not needed and omitted
     1302     *
     1303     */
     1304    abstract public static class SourcePrefMigration {
     1305
     1306        private final String oldPref;
     1307        private final String oldPrefEnableDefaults;
     1308        private final String pref;
     1309
     1310        public SourcePrefMigration(String oldPref, String oldPrefEnableDefaults, String pref) {
     1311            this.oldPref = oldPref;
     1312            this.oldPrefEnableDefaults = oldPrefEnableDefaults;
     1313            this.pref = pref;
     1314        }
     1315
     1316        abstract public Collection<StyleSourceInfo> getDefault();
     1317
     1318        abstract public Collection<String> serialize(SourceEntry entry);
     1319
     1320        abstract public SourceEntry deserialize(List<String> entryStr);
     1321
     1322        public List<SourceEntry> get() {
     1323            List<SourceEntry> entries = readNewFormatImpl();
     1324            if (entries == null) {
     1325
     1326                entries = readOldFormat();
     1327                put(entries);
     1328                return entries;
     1329            }
     1330            return entries;
     1331        }
     1332
     1333        public boolean put(Collection<? extends SourceEntry> entries) {
     1334            boolean changed = false;
     1335            if (entries.isEmpty()) {
     1336                changed |= Main.pref.put(pref + "._empty_", true);
     1337                changed |= Main.pref.putArray(pref, null);
     1338            } else {
     1339                Collection<Collection<String>> setting = new ArrayList<Collection<String>>();
     1340                for (SourceEntry e : entries) {
     1341                    setting.add(serialize(e));
     1342                }
     1343                changed |= Main.pref.put(pref + "._empty_", null);
     1344                changed |= Main.pref.putArray(pref, setting);
     1345            }
     1346            return changed;
     1347        }
     1348
     1349        public List<SourceEntry> readOldFormat() {
     1350            List<SourceEntry> result = new ArrayList<SourceEntry>();
     1351            if (Main.pref.getBoolean(oldPrefEnableDefaults, true)) {
     1352                result.addAll(getDefault());
     1353            }
     1354
     1355            List<String> lines = new LinkedList<String>(Main.pref.getCollection(oldPref));
     1356            for (String line : lines) {
     1357                String[] a = null;
     1358                if (line.indexOf("=") >= 0) {
     1359                    a = line.split("=", 2);
     1360                } else {
     1361                    a = new String[] { null, line };
     1362                }
     1363                result.add(new SourceEntry(a[1], a[0], null, true));
     1364            }
     1365
     1366            return result;
     1367        }
     1368
     1369        public Collection<? extends SourceEntry> readNewFormat() {
     1370            List<SourceEntry> entries = readNewFormatImpl();
     1371            if (entries == null) {
     1372                return getDefault();
     1373            }
     1374            return entries;
     1375        }
     1376
     1377        private List<SourceEntry> readNewFormatImpl() {
     1378            List<SourceEntry> entries = new ArrayList<SourceEntry>();
     1379            Collection<Collection<String>> mappaintSrc = Main.pref.getArray(pref, null);
     1380            if (mappaintSrc == null || mappaintSrc.isEmpty()) {
     1381                if (Main.pref.getBoolean(pref + "._empty_", false)) {
     1382                    return Collections.<SourceEntry>emptyList();
     1383                }
     1384                return null;
     1385            }
     1386
     1387            for (Collection<String> sourcePref : mappaintSrc) {
     1388                SourceEntry e = deserialize(new ArrayList<String>(sourcePref));
     1389                if (e != null) {
     1390                    entries.add(e);
     1391                }
     1392            }
     1393            return entries;
     1394        }
     1395    }
     1396
    9971397}
  • trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java

    r3534 r3796  
    22package org.openstreetmap.josm.gui.preferences;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
     
    78import java.io.IOException;
    89import java.util.ArrayList;
     10import java.util.Arrays;
    911import java.util.Collection;
     12import java.util.Collections;
    1013import java.util.HashMap;
    1114import java.util.List;
     
    2528import org.openstreetmap.josm.gui.ExtendedDialog;
    2629import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
     30import org.openstreetmap.josm.gui.preferences.StyleSourceEditor.StyleSourceInfo;
    2731import org.openstreetmap.josm.gui.tagging.TaggingPreset;
    2832import org.openstreetmap.josm.gui.tagging.TaggingPresetMenu;
     
    4448    private StyleSourceEditor sources;
    4549    private JCheckBox sortMenu;
    46     private JCheckBox enableDefault;
    47 
    4850
    4951    private ValidationListener validationListener = new ValidationListener() {
    5052        public boolean validatePreferences() {
    5153            if (sources.hasActiveStylesChanged()) {
    52                 List<String> sourcesToRemove = new ArrayList<String>();
     54                List<Integer> sourcesToRemove = new ArrayList<Integer>();
     55                int i = -1;
    5356                SOURCES:
    54                     for (String source: sources.getActiveStyles()) {
     57                    for (SourceEntry source: sources.getActiveStyles()) {
     58                        i++;
    5559                        boolean canLoad = false;
    5660                        try {
    57                             TaggingPreset.readAll(source, false);
     61                            TaggingPreset.readAll(source.url, false);
    5862                            canLoad = true;
    5963                        } catch (IOException e) {
     
    6670                                continue SOURCES;
    6771                            case 2:
    68                                 sourcesToRemove.add(source);
     72                                sourcesToRemove.add(i);
    6973                                continue SOURCES;
    7074                            default:
     
    7882
    7983                        try {
    80                             TaggingPreset.readAll(source, true);
     84                            TaggingPreset.readAll(source.url, true);
    8185                        } catch (IOException e) {
    8286                            // Should not happen, but at least show message
     
    117121                                continue SOURCES;
    118122                            case JOptionPane.NO_OPTION:
    119                                 sourcesToRemove.add(source);
     123                                sourcesToRemove.add(i);
    120124                                continue SOURCES;
    121125                            default:
     
    124128                        }
    125129                    }
    126                 for (String toRemove:sourcesToRemove) {
    127                     sources.removeSource(toRemove);
    128                 }
     130                sources.removeSources(sourcesToRemove);
    129131                return true;
    130132            }  else
     
    136138        sortMenu = new JCheckBox(tr("Sort presets menu"),
    137139                Main.pref.getBoolean("taggingpreset.sortmenu", false));
    138         enableDefault = new JCheckBox(tr("Enable built-in defaults"),
    139                 Main.pref.getBoolean("taggingpreset.enable-defaults", true));
    140140
    141141        final JPanel panel = new JPanel(new GridBagLayout());
    142142        panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    143143        panel.add(sortMenu, GBC.eol().insets(5,5,5,0));
    144         panel.add(enableDefault, GBC.eol().insets(5,0,5,0));
    145         sources = new StyleSourceEditor("taggingpreset.sources", "taggingpreset.icon.sources",
    146         "http://josm.openstreetmap.de/presets");
     144        sources = new TaggingPresetSourceEditor();
    147145        panel.add(sources, GBC.eol().fill(GBC.BOTH));
    148146        gui.mapcontent.addTab(tr("Tagging Presets"), panel);
     
    163161    }
    164162
     163    class TaggingPresetSourceEditor extends StyleSourceEditor {
     164
     165        final private String iconpref = "taggingpreset.icon.sources";
     166
     167        public TaggingPresetSourceEditor() {
     168            super("http://josm.openstreetmap.de/presets");
     169        }
     170
     171        @Override
     172        public Collection<? extends SourceEntry> getInitialSourcesList() {
     173            return (new PresetPrefMigration()).get();
     174        }
     175
     176        @Override
     177        public boolean finish() {
     178            List<SourceEntry> activeStyles = activeStylesModel.getStyles();
     179
     180            boolean changed = (new PresetPrefMigration()).put(activeStyles);
     181
     182            if (tblIconPaths != null) {
     183                List<String> iconPaths = iconPathsModel.getIconPaths();
     184
     185                if (!iconPaths.isEmpty()) {
     186                    if (Main.pref.putCollection(iconpref, iconPaths)) {
     187                        changed = true;
     188                    }
     189                } else if (Main.pref.putCollection(iconpref, null)) {
     190                    changed = true;
     191                }
     192            }
     193            return changed;
     194        }
     195
     196        @Override
     197        public Collection<StyleSourceInfo> getDefault() {
     198            return (new PresetPrefMigration()).getDefault();
     199        }
     200
     201        @Override
     202        public Collection<String> getInitialIconPathsList() {
     203            return Main.pref.getCollection(iconpref, null);
     204        }
     205
     206        @Override
     207        public String getStr(I18nString ident) {
     208            switch (ident) {
     209                case AVAILABLE_SOURCES:
     210                    return tr("Available presets:");
     211                case ACTIVE_SOURCES:
     212                    return tr("Active presets:");
     213                case NEW_SOURCE_ENTRY:
     214                    return tr("New preset entry:");
     215                case REMOVE_SOURCE_TOOLTIP:
     216                    return tr("Remove the selected presets from the list of active presets");
     217                case EDIT_SOURCE_TOOLTIP:
     218                    return tr("Edit the filename or URL for the selected active preset");
     219                case ACTIVATE_TOOLTIP:
     220                    return tr("Add the selected available presets to the list of active presets");
     221                case RELOAD_ALL_AVAILABLE:
     222                    return marktr("Reloads the list of available presets from ''{0}''");
     223                case LOADING_SOURCES_FROM:
     224                    return marktr("Loading preset sources from ''{0}''");
     225                case FAILED_TO_LOAD_SOURCES_FROM:
     226                    return marktr("<html>Failed to load the list of preset sources from<br>"
     227                            + "''{0}''.<br>"
     228                            + "<br>"
     229                            + "Details (untranslated):<br>{1}</html>");
     230                case FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC:
     231                    return "/Preferences/Presets#FailedToLoadPresetSources";
     232                case ILLEGAL_FORMAT_OF_ENTRY:
     233                    return marktr("Warning: illegal format of entry in preset list ''{0}''. Got ''{1}''");
     234                default: throw new AssertionError();
     235            }
     236        }
     237    }
     238
    165239    public boolean ok() {
    166         boolean restart = Main.pref.put("taggingpreset.enable-defaults",
    167                 enableDefault.getSelectedObjects() != null);
    168         if(Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null)) {
    169             restart = true;
    170         }
    171         if(sources.finish()) {
    172             restart = true;
    173         }
     240        boolean restart = Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null);
     241        restart |= sources.finish();
     242
    174243        return restart;
    175244    }
     
    217286        }
    218287    }
     288
     289    public static class PresetPrefMigration extends StyleSourceEditor.SourcePrefMigration {
     290
     291        public PresetPrefMigration() {
     292            super("taggingpreset.sources",
     293                  "taggingpreset.enable-defaults",
     294                  "taggingpreset.sources-list");
     295        }
     296
     297        @Override
     298        public Collection<StyleSourceInfo> getDefault() {
     299            StyleSourceInfo i = new StyleSourceInfo("defaultpresets.xml", "resource://data/defaultpresets.xml");
     300            i.shortdescription = tr("Internal Preset");
     301            i.description = tr("The default preset for JOSM");
     302            return Collections.singletonList(i);
     303        }
     304
     305        @Override
     306        public Collection<String> serialize(SourceEntry entry) {
     307            return Arrays.asList(new String[] {entry.url, entry.shortdescription});
     308        }
     309
     310        @Override
     311        public SourceEntry deserialize(List<String> entryStr) {
     312            if (entryStr.size() < 2)
     313                return null;
     314            String url = entryStr.get(0);
     315            String shortdescription = entryStr.get(1);
     316            return new SourceEntry(url, null, shortdescription, true);
     317        }
     318    }
    219319}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java

    r3794 r3796  
    6464import org.openstreetmap.josm.gui.layer.Layer;
    6565import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     66import org.openstreetmap.josm.gui.preferences.SourceEntry;
     67import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference.PresetPrefMigration;
    6668import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
    6769import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionItemPritority;
     
    12281230        LinkedList<String> sources = new LinkedList<String>();
    12291231
    1230         if(Main.pref.getBoolean("taggingpreset.enable-defaults", true)) {
    1231             sources.add("resource://data/defaultpresets.xml");
    1232         }
    1233         sources.addAll(Main.pref.getCollection("taggingpreset.sources", new LinkedList<String>()));
     1232        for (SourceEntry e : (new PresetPrefMigration()).get()) {
     1233            sources.add(e.url);
     1234        }
     1235
    12341236        return sources;
    12351237    }
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r3711 r3796  
    2929
    3030    /**
     31     * for convenience: test whether 2 objects are either both null or a.equals(b)
     32     */
     33    public static <T> boolean equal(T a, T b) {
     34        if (a == null && b == null)
     35            return true;
     36        return (a != null && a.equals(b));
     37    }
     38
     39    /**
    3140     * return the modulus in the range [0, n)
    3241     */
Note: See TracChangeset for help on using the changeset viewer.