Changeset 2400 in josm


Ignore:
Timestamp:
2009-11-07T17:01:23+01:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3800: UI: improve mappaint styles and presets tabs in preferences

Location:
trunk
Files:
1 added
2 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java

    r2017 r2400  
    1313import javax.swing.JLabel;
    1414import javax.swing.JPanel;
    15 import javax.swing.JScrollPane;
     15import javax.swing.event.ChangeEvent;
     16import javax.swing.event.ChangeListener;
    1617
    1718import org.openstreetmap.josm.Main;
     
    2021
    2122public class MapPaintPreference implements PreferenceSetting {
    22     private StyleSources sources;
     23    private StyleSourceEditor sources;
    2324    private JCheckBox enableIconDefault;
    2425    private JCheckBox enableDefault;
     
    3738                Main.pref.getBoolean("mappaint.icon.enable-defaults", true));
    3839
    39         sources = new StyleSources("mappaint.style.sources", "mappaint.icon.sources",
    40         "http://josm.openstreetmap.de/styles", false, tr("Map Paint Styles"));
     40        sources = new StyleSourceEditor("mappaint.style.sources", "mappaint.icon.sources",
     41        "http://josm.openstreetmap.de/styles");
    4142
    4243        Collection<String> styles = new TreeSet<String>(MapPaintStyles.getStyles().getStyleNames());
    4344        String defstyle = Main.pref.get("mappaint.style", "standard");
    4445        styles.add(defstyle);
    45         for(String style : styles)
     46        for(String style : styles) {
    4647            styleCombo.addItem(style);
     48        }
    4749
    4850        styleCombo.setEditable(true);
     
    5456        }
    5557
    56         JPanel panel = new JPanel(new GridBagLayout());
    57         JScrollPane scrollpane = new JScrollPane(panel);
     58        final JPanel panel = new JPanel(new GridBagLayout());
    5859        panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    5960        panel.add(enableDefault, GBC.std().insets(5,5,5,0));
     
    6566
    6667        panel.add(sources, GBC.eol().fill(GBC.BOTH));
    67         gui.mapcontent.addTab(tr("Map Paint Styles"), scrollpane);
     68        gui.mapcontent.addTab(tr("Map Paint Styles"), panel);
     69
     70        // this defers loading of style sources to the first time the tab
     71        // with the map paint preferences is selected by the user
     72        //
     73        gui.mapcontent.addChangeListener(
     74                new ChangeListener() {
     75                    public void stateChanged(ChangeEvent e) {
     76                        if (gui.mapcontent.getSelectedComponent() == panel) {
     77                            sources.initiallyLoadAvailableStyles();
     78                        }
     79                    }
     80                }
     81        );
    6882    }
    6983
    7084    public boolean ok() {
    7185        Boolean restart = Main.pref.put("mappaint.style.enable-defaults", enableDefault.getSelectedObjects() != null);
    72         if(Main.pref.put("mappaint.icon.enable-defaults", enableIconDefault.getSelectedObjects() != null))
    73           restart = true;
    74         if(sources.finish())
    75           restart = true;
     86        if(Main.pref.put("mappaint.icon.enable-defaults", enableIconDefault.getSelectedObjects() != null)) {
     87            restart = true;
     88        }
     89        if(sources.finish()) {
     90            restart = true;
     91        }
    7692        Main.pref.put("mappaint.style", styleCombo.getEditor().getItem().toString());
    7793        return restart;
  • trunk/src/org/openstreetmap/josm/gui/preferences/StyleSourceEditor.java

    r2394 r2400  
    22package org.openstreetmap.josm.gui.preferences;
    33
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
    67import java.awt.Component;
     8import java.awt.FlowLayout;
     9import java.awt.GridBagConstraints;
    710import java.awt.GridBagLayout;
    811import java.awt.event.ActionEvent;
    9 import java.awt.event.ActionListener;
     12import java.awt.event.FocusAdapter;
     13import java.awt.event.FocusEvent;
     14import java.awt.event.KeyEvent;
     15import java.awt.event.MouseEvent;
    1016import java.io.BufferedReader;
     17import java.io.File;
     18import java.io.IOException;
    1119import java.io.InputStreamReader;
    1220import java.io.UnsupportedEncodingException;
     21import java.net.MalformedURLException;
     22import java.net.URL;
    1323import java.util.ArrayList;
    1424import java.util.Collection;
     25import java.util.Collections;
     26import java.util.Comparator;
     27import java.util.EventObject;
     28import java.util.Iterator;
     29import java.util.LinkedList;
     30import java.util.List;
     31import java.util.concurrent.CopyOnWriteArrayList;
    1532import java.util.regex.Matcher;
    1633import java.util.regex.Pattern;
    1734
     35import javax.swing.AbstractAction;
    1836import javax.swing.BorderFactory;
    19 import javax.swing.Box;
    2037import javax.swing.DefaultListModel;
     38import javax.swing.DefaultListSelectionModel;
    2139import javax.swing.JButton;
     40import javax.swing.JComponent;
     41import javax.swing.JFileChooser;
    2242import javax.swing.JLabel;
    2343import javax.swing.JList;
     
    2545import javax.swing.JPanel;
    2646import javax.swing.JScrollPane;
     47import javax.swing.JTable;
     48import javax.swing.JTextField;
     49import javax.swing.KeyStroke;
    2750import javax.swing.ListCellRenderer;
     51import javax.swing.ListSelectionModel;
     52import javax.swing.event.CellEditorListener;
     53import javax.swing.event.ChangeEvent;
     54import javax.swing.event.ListSelectionEvent;
     55import javax.swing.event.ListSelectionListener;
     56import javax.swing.table.AbstractTableModel;
     57import javax.swing.table.TableCellEditor;
    2858
    2959import org.openstreetmap.josm.Main;
     60import org.openstreetmap.josm.gui.HelpAwareOptionPane;
     61import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    3062import org.openstreetmap.josm.io.MirroredInputStream;
     63import org.openstreetmap.josm.io.OsmTransferException;
    3164import org.openstreetmap.josm.tools.GBC;
     65import org.openstreetmap.josm.tools.ImageProvider;
    3266import org.openstreetmap.josm.tools.LanguageInfo;
    33 
    34 public class StyleSources extends JPanel {
    35     private JList sourcesList;
    36     private JList sourcesDefaults;
    37     private JList iconsList = null;
     67import org.xml.sax.SAXException;
     68
     69public class StyleSourceEditor extends JPanel {
     70    private JTable tblActiveStyles;
     71    private ActiveStylesModel activeStylesModel;
     72    private JList lstAvailableStyles;
     73    private AvailableStylesListModel availableStylesModel;
     74    private JTable tblIconPaths = null;
     75    private IconPathTableModel iconPathsModel;
    3876    private String pref;
    3977    private String iconpref;
    40 
    41     public class SourceInfo {
     78    private boolean stylesInitiallyLoaded;
     79    private String availableStylesUrl;
     80
     81
     82    /**
     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)
     86     * @param availableStylesUrl the URL to the list of available style sources
     87     */
     88    public StyleSourceEditor(String stylesPreferencesKey, String iconsPreferenceKey, final String availableStylesUrl) {
     89
     90        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
     91        tblActiveStyles = new JTable(activeStylesModel = new ActiveStylesModel(selectionModel));
     92        tblActiveStyles.setSelectionModel(selectionModel);
     93        tblActiveStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     94        tblActiveStyles.setTableHeader(null);
     95        tblActiveStyles.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor());
     96        tblActiveStyles.setRowHeight(20);
     97        activeStylesModel.setActiveStyles(Main.pref.getCollection(stylesPreferencesKey, null));
     98
     99        selectionModel = new DefaultListSelectionModel();
     100        lstAvailableStyles = new JList(availableStylesModel =new AvailableStylesListModel(selectionModel));
     101        lstAvailableStyles.setSelectionModel(selectionModel);
     102        lstAvailableStyles.setCellRenderer(new StyleSourceCellRenderer());
     103        //availableStylesModel.setStyleSources(reloadAvailableStyles(availableStylesUrl));
     104        this.availableStylesUrl = availableStylesUrl;
     105
     106        this.pref = stylesPreferencesKey;
     107        this.iconpref = iconsPreferenceKey;
     108
     109        JButton iconadd = null;
     110        JButton iconedit = null;
     111        JButton icondelete = null;
     112
     113        if (iconsPreferenceKey != null) {
     114            selectionModel = new DefaultListSelectionModel();
     115            tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
     116            tblIconPaths.setSelectionModel(selectionModel);
     117            tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     118            tblIconPaths.setTableHeader(null);
     119            tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor());
     120            tblIconPaths.setRowHeight(20);
     121            iconPathsModel.setIconPaths(Main.pref.getCollection(iconsPreferenceKey, null));
     122
     123            iconadd = new JButton(new NewIconPathAction());
     124
     125            EditIconPathAction editIconPathAction = new EditIconPathAction();
     126            tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
     127            iconedit = new JButton(editIconPathAction);
     128
     129            RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
     130            tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
     131            icondelete = new JButton(removeIconPathAction);
     132            tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
     133            tblIconPaths.getActionMap().put("delete", removeIconPathAction);
     134        }
     135
     136        JButton add = new JButton(new NewActiveStyleAction());
     137
     138        EditActiveStyleAction editActiveStyleAction = new EditActiveStyleAction();
     139        tblActiveStyles.getSelectionModel().addListSelectionListener(editActiveStyleAction);
     140        JButton edit = new JButton(editActiveStyleAction);
     141
     142        RemoveActiveStylesAction removeActiveStylesAction = new RemoveActiveStylesAction();
     143        tblActiveStyles.getSelectionModel().addListSelectionListener(removeActiveStylesAction);
     144        tblActiveStyles.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
     145        tblActiveStyles.getActionMap().put("delete", removeActiveStylesAction);
     146        JButton delete = new JButton(removeActiveStylesAction);
     147
     148
     149        ActivateStylesAction activateStylesAction = new ActivateStylesAction();
     150        lstAvailableStyles.addListSelectionListener(activateStylesAction);
     151        JButton copy = new JButton(activateStylesAction);
     152
     153        JButton update = new JButton(new ReloadStylesAction(availableStylesUrl));
     154
     155        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
     156        setLayout(new GridBagLayout());
     157        add(new JLabel(tr("Active styles")), GBC.eol().insets(5, 5, 5, 0));
     158        JScrollPane sp;
     159        add(sp = new JScrollPane(tblActiveStyles), GBC.eol().insets(5, 0, 5, 0).fill(GBC.BOTH));
     160        sp.setColumnHeaderView(null);
     161        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     162        add(buttonPanel, GBC.eol().insets(5, 0, 5, 5).fill(GBC.HORIZONTAL));
     163        buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0));
     164        buttonPanel.add(edit, GBC.std().insets(5, 5, 5, 0));
     165        buttonPanel.add(delete, GBC.std().insets(0, 5, 5, 0));
     166        buttonPanel.add(copy, GBC.std().insets(0, 5, 5, 0));
     167        add(new JLabel(tr("Available styles (from {0})", availableStylesUrl)), GBC.eol().insets(5, 5, 5, 0));
     168        add(new JScrollPane(lstAvailableStyles), GBC.eol().insets(5, 0, 5, 0).fill(GBC.BOTH));
     169        buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     170        add(buttonPanel, GBC.eol().insets(5, 0, 5, 5).fill(GBC.HORIZONTAL));
     171        buttonPanel.add(update, GBC.std().insets(0, 5, 0, 0));
     172        if (tblIconPaths != null) {
     173            add(new JLabel(tr("Icon paths")), GBC.eol().insets(5, -5, 5, 0));
     174            add(sp = new JScrollPane(tblIconPaths), GBC.eol().insets(5, 0, 5, 0).fill(GBC.BOTH));
     175            sp.setColumnHeaderView(null);
     176            buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     177            add(buttonPanel, GBC.eol().insets(5, 0, 5, 5).fill(GBC.HORIZONTAL));
     178            buttonPanel.add(iconadd);
     179            buttonPanel.add(iconedit);
     180            buttonPanel.add(icondelete);
     181        }
     182    }
     183
     184    public boolean finish() {
     185        boolean changed = false;
     186        List<String> activeStyles = activeStylesModel.getStyles();
     187
     188        if (activeStyles.size() > 0) {
     189            if (Main.pref.putCollection(pref, activeStyles)) {
     190                changed = true;
     191            }
     192        } else if (Main.pref.putCollection(pref, null)) {
     193            changed = true;
     194        }
     195
     196        if (tblIconPaths != null) {
     197            List<String> iconPaths = iconPathsModel.getIconPaths();
     198
     199            if (!iconPaths.isEmpty()) {
     200                if (Main.pref.putCollection(iconpref, iconPaths)) {
     201                    changed = true;
     202                }
     203            } else if (Main.pref.putCollection(iconpref, null)) {
     204                changed = true;
     205            }
     206        }
     207        return changed;
     208    }
     209
     210    protected void reloadAvailableStyles(String url) {
     211        Main.worker.submit(new StyleSourceLoader(url));
     212    }
     213
     214    public void initiallyLoadAvailableStyles() {
     215        if (!stylesInitiallyLoaded) {
     216            reloadAvailableStyles(this.availableStylesUrl);
     217        }
     218        stylesInitiallyLoaded = true;
     219    }
     220
     221    class AvailableStylesListModel extends DefaultListModel {
     222        private ArrayList<StyleSourceInfo> data;
     223        private DefaultListSelectionModel selectionModel;
     224
     225        public AvailableStylesListModel(DefaultListSelectionModel selectionModel) {
     226            data = new ArrayList<StyleSourceInfo>();
     227            this.selectionModel = selectionModel;
     228        }
     229
     230        public void setStyleSources(List<StyleSourceInfo> styleSources) {
     231            data.clear();
     232            if (styleSources != null) {
     233                data.addAll(styleSources);
     234            }
     235            fireContentsChanged(this, 0, data.size());
     236        }
     237
     238        @Override
     239        public Object getElementAt(int index) {
     240            return data.get(index);
     241        }
     242
     243        @Override
     244        public int getSize() {
     245            if (data == null) return 0;
     246            return data.size();
     247        }
     248
     249        public void deleteSelected() {
     250            Iterator<StyleSourceInfo> it = data.iterator();
     251            int i=0;
     252            while(it.hasNext()) {
     253                it.next();
     254                if (selectionModel.isSelectedIndex(i)) {
     255                    it.remove();
     256                }
     257                i++;
     258            }
     259            fireContentsChanged(this, 0, data.size());
     260        }
     261
     262        public List<StyleSourceInfo> getSelected() {
     263            ArrayList<StyleSourceInfo> ret = new ArrayList<StyleSourceInfo>();
     264            for(int i=0; i<data.size();i++) {
     265                if (selectionModel.isSelectedIndex(i)) {
     266                    ret.add(data.get(i));
     267                }
     268            }
     269            return ret;
     270        }
     271    }
     272
     273
     274    class ActiveStylesModel extends AbstractTableModel {
     275        private ArrayList<String> data;
     276        private DefaultListSelectionModel selectionModel;
     277
     278        public ActiveStylesModel(DefaultListSelectionModel selectionModel) {
     279            this.selectionModel = selectionModel;
     280            this.data = new ArrayList<String>();
     281        }
     282
     283        public int getColumnCount() {
     284            return 1;
     285        }
     286
     287        public int getRowCount() {
     288            return data == null ? 0 : data.size();
     289        }
     290
     291        public Object getValueAt(int rowIndex, int columnIndex) {
     292            return data.get(rowIndex);
     293        }
     294
     295        @Override
     296        public boolean isCellEditable(int rowIndex, int columnIndex) {
     297            return true;
     298        }
     299
     300        @Override
     301        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
     302            updateStyle(rowIndex, (String)aValue);
     303        }
     304
     305        public void setActiveStyles(Collection<String> styles) {
     306            data.clear();
     307            if (styles !=null) {
     308                data.addAll(styles);
     309            }
     310            sort();
     311            fireTableDataChanged();
     312        }
     313
     314        public void addStyle(String style) {
     315            if (style == null) return;
     316            data.add(style);
     317            sort();
     318            fireTableDataChanged();
     319            int idx = data.indexOf(style);
     320            if (idx >= 0) {
     321                selectionModel.setSelectionInterval(idx, idx);
     322            }
     323        }
     324
     325        public void updateStyle(int pos, String style) {
     326            if (style == null) return;
     327            if (pos < 0 || pos >= getRowCount()) return;
     328            data.set(pos, style);
     329            sort();
     330            fireTableDataChanged();
     331            int idx = data.indexOf(style);
     332            if (idx >= 0) {
     333                selectionModel.setSelectionInterval(idx, idx);
     334            }
     335        }
     336
     337        public void removeSelected() {
     338            Iterator<String> it = data.iterator();
     339            int i=0;
     340            while(it.hasNext()) {
     341                it.next();
     342                if (selectionModel.isSelectedIndex(i)) {
     343                    it.remove();
     344                }
     345                i++;
     346            }
     347            fireTableDataChanged();
     348        }
     349
     350        protected void sort() {
     351            Collections.sort(
     352                    data,
     353                    new Comparator<String>() {
     354                        public int compare(String o1, String o2) {
     355                            if (o1.equals("") && o2.equals(""))
     356                                return 0;
     357                            if (o1.equals("")) return 1;
     358                            if (o2.equals("")) return -1;
     359                            return o1.compareTo(o2);
     360                        }
     361                    }
     362            );
     363        }
     364
     365        public void addStylesFromSources(List<StyleSourceInfo> sources) {
     366            if (sources == null) return;
     367            for (StyleSourceInfo info: sources) {
     368                data.add(info.url);
     369            }
     370            sort();
     371            fireTableDataChanged();
     372            selectionModel.clearSelection();
     373            for (StyleSourceInfo info: sources) {
     374                int pos = data.indexOf(info.url);
     375                if (pos >=0) {
     376                    selectionModel.addSelectionInterval(pos, pos);
     377                }
     378            }
     379        }
     380
     381        public List<String> getStyles() {
     382            return new ArrayList<String>(data);
     383        }
     384
     385        public String getStyle(int pos) {
     386            return data.get(pos);
     387        }
     388    }
     389
     390    public class StyleSourceInfo {
    42391        String version;
    43392        String name;
     
    47396        String description;
    48397        String shortdescription;
    49         public SourceInfo(String name, String url)
    50         {
     398
     399        public StyleSourceInfo(String name, String url) {
    51400            this.name = name;
    52401            this.url = url;
    53402            version = author = link = description = shortdescription = null;
    54403        }
    55         public String getName()
    56         {
     404
     405        public String getName() {
    57406            return shortdescription == null ? name : shortdescription;
    58407        }
    59         public String getTooltip()
    60         {
     408
     409        public String getTooltip() {
    61410            String s = tr("Short Description: {0}", getName()) + "<br>" + tr("URL: {0}", url);
    62             if(author != null) {
     411            if (author != null) {
    63412                s += "<br>" + tr("Author: {0}", author);
    64413            }
    65             if(link != null) {
     414            if (link != null) {
    66415                s += "<br>" + tr("Webpage: {0}", link);
    67416            }
    68             if(description != null) {
     417            if (description != null) {
    69418                s += "<br>" + tr("Description: {0}", description);
    70419            }
    71             if(version != null) {
     420            if (version != null) {
    72421                s += "<br>" + tr("Version: {0}", version);
    73422            }
    74423            return "<html>" + s + "</html>";
    75424        }
     425
    76426        @Override
    77         public String toString()
    78         {
     427        public String toString() {
    79428            return getName() + " (" + url + ")";
    80429        }
    81     };
    82 
    83     class MyCellRenderer extends JLabel implements ListCellRenderer {
    84         public Component getListCellRendererComponent(JList list, Object value,
    85                 int index, boolean isSelected, boolean cellHasFocus)
    86         {
     430    }
     431
     432    class NewActiveStyleAction extends AbstractAction {
     433        public NewActiveStyleAction() {
     434            putValue(NAME, tr("New"));
     435            putValue(SHORT_DESCRIPTION, tr("Add a filename or an URL of an active style"));
     436            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
     437        }
     438
     439        public void actionPerformed(ActionEvent e) {
     440            activeStylesModel.addStyle("");
     441            tblActiveStyles.requestFocusInWindow();
     442            tblActiveStyles.editCellAt(activeStylesModel.getRowCount()-1, 0);
     443        }
     444    }
     445
     446    class RemoveActiveStylesAction extends AbstractAction implements ListSelectionListener {
     447
     448        public RemoveActiveStylesAction() {
     449            putValue(NAME, tr("Remove"));
     450            putValue(SHORT_DESCRIPTION, tr("Remove the selected styles from the list of active styles"));
     451            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
     452            updateEnabledState();
     453        }
     454
     455        protected void updateEnabledState() {
     456            setEnabled(tblActiveStyles.getSelectedRowCount() > 0);
     457        }
     458
     459        public void valueChanged(ListSelectionEvent e) {
     460            updateEnabledState();
     461        }
     462
     463        public void actionPerformed(ActionEvent e) {
     464            activeStylesModel.removeSelected();
     465        }
     466    }
     467
     468    class EditActiveStyleAction extends AbstractAction implements ListSelectionListener {
     469        public EditActiveStyleAction() {
     470            putValue(NAME, tr("Edit"));
     471            putValue(SHORT_DESCRIPTION, tr("Edit the filename or URL for the selected active style"));
     472            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
     473            updateEnabledState();
     474        }
     475
     476        protected void updateEnabledState() {
     477            setEnabled(tblActiveStyles.getSelectedRowCount() == 1);
     478        }
     479
     480        public void valueChanged(ListSelectionEvent e) {
     481            updateEnabledState();
     482        }
     483
     484        public void actionPerformed(ActionEvent e) {
     485            int pos = tblActiveStyles.getSelectedRow();
     486            tblActiveStyles.editCellAt(pos, 0);
     487        }
     488    }
     489
     490    class ActivateStylesAction extends AbstractAction implements ListSelectionListener {
     491        public ActivateStylesAction() {
     492            putValue(NAME, tr("Activate"));
     493            putValue(SHORT_DESCRIPTION, tr("Add the selected available styles to the list of active styles"));
     494            putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
     495            updateEnabledState();
     496        }
     497
     498        protected void updateEnabledState() {
     499            setEnabled(lstAvailableStyles.getSelectedIndices().length > 0);
     500        }
     501
     502        public void valueChanged(ListSelectionEvent e) {
     503            updateEnabledState();
     504        }
     505
     506        public void actionPerformed(ActionEvent e) {
     507            List<StyleSourceInfo> styleSources = availableStylesModel.getSelected();
     508            activeStylesModel.addStylesFromSources(styleSources);
     509        }
     510    }
     511
     512    class ReloadStylesAction extends AbstractAction {
     513        private String url;
     514        public ReloadStylesAction(String url) {
     515            putValue(NAME, tr("Reload"));
     516            putValue(SHORT_DESCRIPTION, tr("Reloads the list of available styles from ''{0}''", url));
     517            putValue(SMALL_ICON, ImageProvider.get("download"));
     518            this.url = url;
     519        }
     520
     521        public void actionPerformed(ActionEvent e) {
     522            MirroredInputStream.cleanup(url);
     523            reloadAvailableStyles(url);
     524        }
     525    }
     526
     527    class IconPathTableModel extends AbstractTableModel {
     528        private ArrayList<String> data;
     529        private DefaultListSelectionModel selectionModel;
     530
     531        public IconPathTableModel(DefaultListSelectionModel selectionModel) {
     532            this.selectionModel = selectionModel;
     533            this.data = new ArrayList<String>();
     534        }
     535
     536        public int getColumnCount() {
     537            return 1;
     538        }
     539
     540        public int getRowCount() {
     541            return data == null ? 0 : data.size();
     542        }
     543
     544        public Object getValueAt(int rowIndex, int columnIndex) {
     545            return data.get(rowIndex);
     546        }
     547
     548        @Override
     549        public boolean isCellEditable(int rowIndex, int columnIndex) {
     550            return true;
     551        }
     552
     553        @Override
     554        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
     555            updatePath(rowIndex, (String)aValue);
     556        }
     557
     558        public void setIconPaths(Collection<String> styles) {
     559            data.clear();
     560            if (styles !=null) {
     561                data.addAll(styles);
     562            }
     563            sort();
     564            fireTableDataChanged();
     565        }
     566
     567        public void addPath(String path) {
     568            if (path == null) return;
     569            data.add(path);
     570            sort();
     571            fireTableDataChanged();
     572            int idx = data.indexOf(path);
     573            if (idx >= 0) {
     574                selectionModel.setSelectionInterval(idx, idx);
     575            }
     576        }
     577
     578        public void updatePath(int pos, String path) {
     579            if (path == null) return;
     580            if (pos < 0 || pos >= getRowCount()) return;
     581            data.set(pos, path);
     582            sort();
     583            fireTableDataChanged();
     584            int idx = data.indexOf(path);
     585            if (idx >= 0) {
     586                selectionModel.setSelectionInterval(idx, idx);
     587            }
     588        }
     589
     590        public void removeSelected() {
     591            Iterator<String> it = data.iterator();
     592            int i=0;
     593            while(it.hasNext()) {
     594                it.next();
     595                if (selectionModel.isSelectedIndex(i)) {
     596                    it.remove();
     597                }
     598                i++;
     599            }
     600            fireTableDataChanged();
     601            selectionModel.clearSelection();
     602        }
     603
     604        protected void sort() {
     605            Collections.sort(
     606                    data,
     607                    new Comparator<String>() {
     608                        public int compare(String o1, String o2) {
     609                            if (o1.equals("") && o2.equals(""))
     610                                return 0;
     611                            if (o1.equals("")) return 1;
     612                            if (o2.equals("")) return -1;
     613                            return o1.compareTo(o2);
     614                        }
     615                    }
     616            );
     617        }
     618
     619        public List<String> getIconPaths() {
     620            return new ArrayList<String>(data);
     621        }
     622    }
     623
     624    class NewIconPathAction extends AbstractAction {
     625        public NewIconPathAction() {
     626            putValue(NAME, tr("New"));
     627            putValue(SHORT_DESCRIPTION, tr("Add a new icon path"));
     628            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
     629        }
     630
     631        public void actionPerformed(ActionEvent e) {
     632            iconPathsModel.addPath("");
     633            tblIconPaths.editCellAt(iconPathsModel.getRowCount() -1,0);
     634        }
     635    }
     636
     637    class RemoveIconPathAction extends AbstractAction implements ListSelectionListener {
     638        public RemoveIconPathAction() {
     639            putValue(NAME, tr("Remove"));
     640            putValue(SHORT_DESCRIPTION, tr("Remove the selected icon paths"));
     641            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
     642            updateEnabledState();
     643        }
     644
     645        protected void updateEnabledState() {
     646            setEnabled(tblIconPaths.getSelectedRowCount() > 0);
     647        }
     648
     649        public void valueChanged(ListSelectionEvent e) {
     650            updateEnabledState();
     651        }
     652
     653        public void actionPerformed(ActionEvent e) {
     654            iconPathsModel.removeSelected();
     655        }
     656    }
     657
     658    class EditIconPathAction extends AbstractAction implements ListSelectionListener {
     659        public EditIconPathAction() {
     660            putValue(NAME, tr("Edit"));
     661            putValue(SHORT_DESCRIPTION, tr("Edit the selected icon path"));
     662            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
     663            updateEnabledState();
     664        }
     665
     666        protected void updateEnabledState() {
     667            setEnabled(tblIconPaths.getSelectedRowCount() == 1);
     668        }
     669
     670        public void valueChanged(ListSelectionEvent e) {
     671            updateEnabledState();
     672        }
     673
     674        public void actionPerformed(ActionEvent e) {
     675            int row = tblIconPaths.getSelectedRow();
     676            tblIconPaths.editCellAt(row, 0);
     677        }
     678    }
     679
     680    class StyleSourceCellRenderer extends JLabel implements ListCellRenderer {
     681        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
     682                boolean cellHasFocus) {
    87683            String s = value.toString();
    88684            setText(s);
     
    90686                setBackground(list.getSelectionBackground());
    91687                setForeground(list.getSelectionForeground());
    92             }
    93             else {
     688            } else {
    94689                setBackground(list.getBackground());
    95690                setForeground(list.getForeground());
     
    98693            setFont(list.getFont());
    99694            setOpaque(true);
    100             setToolTipText(((SourceInfo)value).getTooltip());
     695            setToolTipText(((StyleSourceInfo) value).getTooltip());
    101696            return this;
    102697        }
    103698    }
    104699
    105     public StyleSources(String pref, String iconpref, final String url, boolean named, final String name)
    106     {
    107         sourcesList = new JList(new DefaultListModel());
    108         sourcesDefaults = new JList(new DefaultListModel());
    109         sourcesDefaults.setCellRenderer(new MyCellRenderer());
    110         getDefaults(url);
    111 
    112         this.pref = pref;
    113         this.iconpref = iconpref;
    114 
    115         Collection<String> sources = Main.pref.getCollection(pref, null);
    116         if(sources != null) {
    117             for(String s : sources) {
    118                 ((DefaultListModel)sourcesList.getModel()).addElement(s);
    119             }
    120         }
    121 
    122         JButton iconadd = null;
    123         JButton iconedit = null;
    124         JButton icondelete = null;
    125 
    126         if(iconpref != null)
    127         {
    128             iconsList = new JList(new DefaultListModel());
    129             sources = Main.pref.getCollection(iconpref, null);
    130             if(sources != null) {
    131                 for(String s : sources) {
    132                     ((DefaultListModel)iconsList.getModel()).addElement(s);
    133                 }
    134             }
    135 
    136             iconadd = new JButton(tr("Add"));
    137             iconadd.addActionListener(new ActionListener(){
    138                 public void actionPerformed(ActionEvent e) {
    139                     String source = JOptionPane.showInputDialog(
    140                             Main.parent,
    141                             tr("Icon paths"),
    142                             tr("Icon paths"),
    143                             JOptionPane.QUESTION_MESSAGE
    144                     );
    145                     if (source != null) {
    146                         ((DefaultListModel)iconsList.getModel()).addElement(source);
     700    class StyleSourceLoader extends PleaseWaitRunnable {
     701        private String url;
     702        private BufferedReader reader;
     703        private boolean canceled;
     704
     705        public StyleSourceLoader(String url) {
     706            super(tr("Loading style sources from ''{0}''", url));
     707            this.url = url;
     708        }
     709
     710        @Override
     711        protected void cancel() {
     712            canceled = true;
     713            if (reader!= null) {
     714                try {
     715                    reader.close();
     716                } catch(IOException e) {
     717                    // ignore
     718                }
     719            }
     720        }
     721
     722        @Override
     723        protected void finish() {}
     724
     725        protected void warn(Exception e) {
     726            String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
     727            emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
     728            String msg = tr("<html>Failed to load the list of style sources from<br>"
     729                    + "''{0}''.<br>"
     730                    + "<br>"
     731                    + "Details (untranslated):<br>{1}</html>",
     732                    url, emsg
     733            );
     734
     735            HelpAwareOptionPane.showOptionDialog(
     736                    Main.parent,
     737                    msg,
     738                    tr("Error"),
     739                    JOptionPane.ERROR_MESSAGE,
     740                    ht("Preferences/Styles#FailedToLoadStyleSources")
     741            );
     742        }
     743
     744        @Override
     745        protected void realRun() throws SAXException, IOException, OsmTransferException {
     746            LinkedList<StyleSourceInfo> styles = new LinkedList<StyleSourceInfo>();
     747            String lang = LanguageInfo.getLanguageCodeXML();
     748            try {
     749                MirroredInputStream stream = new MirroredInputStream(url);
     750                InputStreamReader r;
     751                try {
     752                    r = new InputStreamReader(stream, "UTF-8");
     753                } catch (UnsupportedEncodingException e) {
     754                    r = new InputStreamReader(stream);
     755                }
     756                BufferedReader reader = new BufferedReader(r);
     757
     758                String line;
     759                StyleSourceInfo last = null;
     760
     761                while ((line = reader.readLine()) != null && !canceled) {
     762                    if (line.trim().equals("")) {
     763                        continue; // skip empty lines
    147764                    }
    148                 }
    149             });
    150 
    151             iconedit = new JButton(tr("Edit"));
    152             iconedit.addActionListener(new ActionListener(){
    153                 public void actionPerformed(ActionEvent e) {
    154                     if (sourcesList.getSelectedIndex() == -1) {
    155                         JOptionPane.showMessageDialog(
    156                                 Main.parent,
    157                                 tr("Please select the row to edit."),
    158                                 tr("Warning"),
    159                                 JOptionPane.WARNING_MESSAGE
    160                         );
    161                     } else {
    162                         String source = (String)JOptionPane.showInputDialog(
    163                                 Main.parent,
    164                                 tr("Icon paths"),
    165                                 tr("Icon paths"),
    166                                 JOptionPane.QUESTION_MESSAGE,
    167                                 null,
    168                                 null,
    169                                 iconsList.getSelectedValue()
    170                         );
    171                         if (source != null) {
    172                             ((DefaultListModel)iconsList.getModel()).setElementAt(source, iconsList.getSelectedIndex());
     765                    if (line.startsWith("\t")) {
     766                        Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
     767                        if (! m.matches()) {
     768                            System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
     769                            continue;
    173770                        }
    174                     }
    175                 }
    176             });
    177 
    178             icondelete = new JButton(tr("Delete"));
    179             icondelete.addActionListener(new ActionListener(){
    180                 public void actionPerformed(ActionEvent e) {
    181                     if (iconsList.getSelectedIndex() == -1) {
    182                         JOptionPane.showMessageDialog(
    183                                 Main.parent, tr("Please select the row to delete."),
    184                                 tr("Warning"),
    185                                 JOptionPane.WARNING_MESSAGE);
    186                     } else {
    187                         ((DefaultListModel)iconsList.getModel()).remove(iconsList.getSelectedIndex());
    188                     }
    189                 }
    190             });
    191         }
    192 
    193         JButton add = new JButton(tr("Add"));
    194         add.addActionListener(new ActionListener(){
    195             public void actionPerformed(ActionEvent e) {
    196                 String source = JOptionPane.showInputDialog(
    197                         Main.parent,
    198                         name,
    199                         name,
    200                         JOptionPane.QUESTION_MESSAGE);
    201                 if (source != null) {
    202                     ((DefaultListModel)sourcesList.getModel()).addElement(source);
    203                 }
    204             }
    205         });
    206 
    207         JButton edit = new JButton(tr("Edit"));
    208         edit.addActionListener(new ActionListener(){
    209             public void actionPerformed(ActionEvent e) {
    210                 if (sourcesList.getSelectedIndex() == -1) {
    211                     JOptionPane.showMessageDialog(
    212                             Main.parent, tr("Please select the row to edit."),
    213                             tr("Warning"), JOptionPane.WARNING_MESSAGE);
    214                 } else {
    215                     String source = (String)JOptionPane.showInputDialog(
    216                             Main.parent,
    217                             name,
    218                             name,
    219                             JOptionPane.QUESTION_MESSAGE,
    220                             null,
    221                             null,
    222                             sourcesList.getSelectedValue()
    223                     );
    224                     if (source != null) {
    225                         ((DefaultListModel)sourcesList.getModel()).setElementAt(source, sourcesList.getSelectedIndex());
    226                     }
    227                 }
    228             }
    229         });
    230 
    231         JButton delete = new JButton(tr("Delete"));
    232         delete.addActionListener(new ActionListener(){
    233             public void actionPerformed(ActionEvent e) {
    234                 if (sourcesList.getSelectedIndex() == -1) {
    235                     JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."),
    236                             tr("Warning"), JOptionPane.WARNING_MESSAGE);
    237                 } else {
    238                     ((DefaultListModel)sourcesList.getModel()).remove(sourcesList.getSelectedIndex());
    239                 }
    240             }
    241         });
    242 
    243         JButton copy = new JButton(tr("Copy defaults"));
    244         copy.addActionListener(new ActionListener(){
    245             public void actionPerformed(ActionEvent e) {
    246                 if (sourcesDefaults.getSelectedIndex() == -1) {
    247                     JOptionPane.showMessageDialog(
    248                             Main.parent, tr("Please select the row to copy."),
    249                             tr("Warning"), JOptionPane.WARNING_MESSAGE);
    250                 } else {
    251                     ((DefaultListModel)sourcesList.getModel()).addElement(
    252                             ((SourceInfo)sourcesDefaults.getSelectedValue()).url);
    253                 }
    254             }
    255         });
    256 
    257         JButton update = new JButton(tr("Update"));
    258         update.addActionListener(new ActionListener(){
    259             public void actionPerformed(ActionEvent e) {
    260                 MirroredInputStream.cleanup(url);
    261                 getDefaults(url);
    262                 int num = sourcesList.getModel().getSize();
    263                 if (num > 0)
    264                 {
    265                     ArrayList<String> l = new ArrayList<String>();
    266                     for (int i = 0; i < num; ++i) {
    267                         MirroredInputStream.cleanup((String)sourcesList.getModel().getElementAt(i));
    268                     }
    269                 }
    270             }
    271         });
    272 
    273         sourcesList.setToolTipText(tr("The XML source (URL or filename) for {0} definition files.", name));
    274         add.setToolTipText(tr("Add a new XML source to the list."));
    275         delete.setToolTipText(tr("Delete the selected source from the list."));
    276 
    277         setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    278         setLayout(new GridBagLayout());
    279         add(new JLabel(name), GBC.eol().insets(5,5,5,0));
    280         add(new JScrollPane(sourcesList), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH));
    281         add(new JLabel(tr("Defaults (See tooltip for detailed information)")), GBC.eol().insets(5,5,5,0));
    282         add(new JScrollPane(sourcesDefaults), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH));
    283         JPanel buttonPanel = new JPanel(new GridBagLayout());
    284         add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL));
    285         buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    286         buttonPanel.add(add, GBC.std().insets(0,5,0,0));
    287         buttonPanel.add(edit, GBC.std().insets(5,5,5,0));
    288         buttonPanel.add(delete, GBC.std().insets(0,5,5,0));
    289         buttonPanel.add(copy, GBC.std().insets(0,5,5,0));
    290         buttonPanel.add(update, GBC.std().insets(0,5,0,0));
    291         if(iconsList != null)
    292         {
    293             add(new JLabel(tr("Icon paths")), GBC.eol().insets(5,-5,5,0));
    294             add(new JScrollPane(iconsList), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH));
    295             buttonPanel = new JPanel(new GridBagLayout());
    296             add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL));
    297             buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    298             buttonPanel.add(iconadd, GBC.std().insets(0,5,0,0));
    299             buttonPanel.add(iconedit, GBC.std().insets(5,5,5,0));
    300             buttonPanel.add(icondelete, GBC.std().insets(0,5,0,0));
    301         }
    302     }
    303 
    304     public boolean finish() {
    305         boolean changed = false;
    306         int num = sourcesList.getModel().getSize();
    307         if (num > 0)
    308         {
    309             ArrayList<String> l = new ArrayList<String>();
    310             for (int i = 0; i < num; ++i) {
    311                 l.add((String)sourcesList.getModel().getElementAt(i));
    312             }
    313             if(Main.pref.putCollection(pref, l)) {
    314                 changed = true;
    315             }
    316         }
    317         else if(Main.pref.putCollection(pref, null)) {
    318             changed = true;
    319         }
    320         if(iconsList != null)
    321         {
    322             num = iconsList.getModel().getSize();
    323             if (num > 0)
    324             {
    325                 ArrayList<String> l = new ArrayList<String>();
    326                 for (int i = 0; i < num; ++i) {
    327                     l.add((String)iconsList.getModel().getElementAt(i));
    328                 }
    329                 if(Main.pref.putCollection(iconpref, l)) {
    330                     changed = true;
    331                 }
    332             }
    333             else if(Main.pref.putCollection(iconpref, null)) {
    334                 changed = true;
    335             }
    336         }
    337         return changed;
    338     }
    339 
    340     public void getDefaults(String name)
    341     {
    342         ((DefaultListModel)sourcesDefaults.getModel()).removeAllElements();
    343         String lang = LanguageInfo.getLanguageCodeXML();
    344         try
    345         {
    346             MirroredInputStream stream = new MirroredInputStream(name);
    347             InputStreamReader r;
    348             try
    349             {
    350                 r = new InputStreamReader(stream, "UTF-8");
    351             }
    352             catch (UnsupportedEncodingException e)
    353             {
    354                 r = new InputStreamReader(stream);
    355             }
    356             BufferedReader reader = new BufferedReader(r);
    357 
    358             String line;
    359             SourceInfo last = null;
    360 
    361             while((line = reader.readLine()) != null)
    362             {
    363                 if(line.startsWith("\t"))
    364                 {
    365                     Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
    366                     m.matches();
    367                     try
    368                     {
    369                         if(last != null)
    370                         {
     771                        if (last != null) {
    371772                            String key = m.group(1);
    372773                            String value = m.group(2);
    373                             if("author".equals(key) && last.author == null) {
     774                            if ("author".equals(key) && last.author == null) {
    374775                                last.author = value;
    375                             } else if("version".equals(key)) {
     776                            } else if ("version".equals(key)) {
    376777                                last.version = value;
    377                             } else if("link".equals(key) && last.link == null) {
     778                            } else if ("link".equals(key) && last.link == null) {
    378779                                last.link = value;
    379                             } else if("description".equals(key) && last.description == null) {
     780                            } else if ("description".equals(key) && last.description == null) {
    380781                                last.description = value;
    381                             } else if("shortdescription".equals(key) && last.shortdescription == null) {
     782                            } else if ("shortdescription".equals(key) && last.shortdescription == null) {
    382783                                last.shortdescription = value;
    383                             } else if((lang+"author").equals(key)) {
     784                            } else if ((lang + "author").equals(key)) {
    384785                                last.author = value;
    385                             } else if((lang+"link").equals(key)) {
     786                            } else if ((lang + "link").equals(key)) {
    386787                                last.link = value;
    387                             } else if((lang+"description").equals(key)) {
     788                            } else if ((lang + "description").equals(key)) {
    388789                                last.description = value;
    389                             } else if((lang+"shortdescription").equals(key)) {
     790                            } else if ((lang + "shortdescription").equals(key)) {
    390791                                last.shortdescription = value;
    391792                            }
    392793                        }
     794                    } else {
     795                        last = null;
     796                        Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
     797                        if (m.matches()) {
     798                            styles.add(last = new StyleSourceInfo(m.group(1), m.group(2)));
     799                        } else {
     800                            System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
     801                        }
    393802                    }
    394                     catch (IllegalStateException e)
    395                     { e.printStackTrace(); }
    396                 }
    397                 else
    398                 {
    399                     last = null;
    400                     Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
    401                     m.matches();
    402                     try
    403                     {
    404                         last = new SourceInfo(m.group(1),m.group(2));
    405                         ((DefaultListModel)sourcesDefaults.getModel()).addElement(last);
     803                }
     804            } catch (Exception e) {
     805                if (canceled)
     806                    // ignore the exception and return
     807                    return;
     808                warn(e);
     809                return;
     810            }
     811            availableStylesModel.setStyleSources(styles);
     812        }
     813    }
     814
     815    class FileOrUrlCellEditor extends JPanel implements TableCellEditor {
     816        private JTextField tfFileName;
     817        private CopyOnWriteArrayList<CellEditorListener> listeners;
     818        private String value;
     819        private JFileChooser fileChooser;
     820
     821        protected JFileChooser getFileChooser() {
     822            if (fileChooser == null) {
     823                this.fileChooser = new JFileChooser();
     824            }
     825            return fileChooser;
     826        }
     827
     828        /**
     829         * build the GUI
     830         */
     831        protected void build() {
     832            setLayout(new GridBagLayout());
     833            GridBagConstraints gc = new GridBagConstraints();
     834            gc.gridx = 0;
     835            gc.gridy = 0;
     836            gc.fill = GridBagConstraints.BOTH;
     837            gc.weightx = 1.0;
     838            gc.weighty = 1.0;
     839            add(tfFileName = new JTextField(), gc);
     840
     841
     842            gc.gridx = 1;
     843            gc.gridy = 0;
     844            gc.fill = GridBagConstraints.BOTH;
     845            gc.weightx = 0.0;
     846            gc.weighty = 1.0;
     847            add(new JButton(new LaunchFileChooserAction()));
     848
     849            tfFileName.addFocusListener(
     850                    new FocusAdapter() {
     851                        @Override
     852                        public void focusGained(FocusEvent e) {
     853                            tfFileName.selectAll();
     854                        }
    406855                    }
    407                     catch (IllegalStateException e)
    408                     { e.printStackTrace(); }
    409                 }
    410             }
    411         }
    412         catch(Exception e)
    413         { e.printStackTrace(); }
     856            );
     857        }
     858
     859        public FileOrUrlCellEditor() {
     860            listeners = new CopyOnWriteArrayList<CellEditorListener>();
     861            build();
     862        }
     863
     864
     865        public void addCellEditorListener(CellEditorListener l) {
     866            if (!listeners.contains(l)) {
     867                listeners.add(l);
     868            }
     869        }
     870
     871        protected void fireEditingCanceled() {
     872            for (CellEditorListener l: listeners) {
     873                l.editingCanceled(new ChangeEvent(this));
     874            }
     875        }
     876
     877        protected void fireEditingStopped() {
     878            for (CellEditorListener l: listeners) {
     879                l.editingStopped(new ChangeEvent(this));
     880            }
     881        }
     882
     883        public void cancelCellEditing() {
     884            fireEditingCanceled();
     885        }
     886
     887        public Object getCellEditorValue() {
     888            return value;
     889        }
     890
     891        public boolean isCellEditable(EventObject anEvent) {
     892            if (anEvent instanceof MouseEvent)
     893                return ((MouseEvent)anEvent).getClickCount() >= 2;
     894                return true;
     895        }
     896
     897        public void removeCellEditorListener(CellEditorListener l) {
     898            if (listeners.contains(l)) {
     899                listeners.remove(l);
     900            }
     901        }
     902
     903        public boolean shouldSelectCell(EventObject anEvent) {
     904            return true;
     905        }
     906
     907        public boolean stopCellEditing() {
     908            value = tfFileName.getText();
     909            fireEditingStopped();
     910            return true;
     911        }
     912
     913        public void setInitialValue(String initialValue) {
     914            this.value = initialValue;
     915            if (initialValue == null) {
     916                this.tfFileName.setText("");
     917            } else {
     918                this.tfFileName.setText(initialValue);
     919            }
     920        }
     921
     922        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
     923            setInitialValue((String)value);
     924            tfFileName.selectAll();
     925            return this;
     926        }
     927
     928
     929        class LaunchFileChooserAction extends AbstractAction {
     930            public LaunchFileChooserAction() {
     931                putValue(NAME, "...");
     932                putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
     933            }
     934
     935            protected void prepareFileChooser(String url, JFileChooser fc) {
     936                if (url == null || url.trim().length() == 0) return;
     937                URL sourceUrl = null;
     938                try {
     939                    sourceUrl = new URL(url);
     940                } catch(MalformedURLException e) {
     941                    File f = new File(url);
     942                    if (f.isFile()) {
     943                        f = f.getParentFile();
     944                    }
     945                    if (f != null) {
     946                        fc.setCurrentDirectory(f);
     947                    }
     948                    return;
     949                }
     950                if (sourceUrl.getProtocol().startsWith("file")) {
     951                    File f = new File(sourceUrl.getPath());
     952                    if (f.isFile()) {
     953                        f = f.getParentFile();
     954                    }
     955                    if (f != null) {
     956                        fc.setCurrentDirectory(f);
     957                    }
     958                }
     959            }
     960
     961            public void actionPerformed(ActionEvent e) {
     962                JFileChooser fc = getFileChooser();
     963                prepareFileChooser(tfFileName.getText(), fc);
     964                int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(StyleSourceEditor.this));
     965                if (ret != JFileChooser.APPROVE_OPTION)
     966                    return;
     967                tfFileName.setText(fc.getSelectedFile().toString());
     968            }
     969        }
    414970    }
    415971}
  • trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java

    r2140 r2400  
    1313import javax.swing.JMenuItem;
    1414import javax.swing.JPanel;
    15 import javax.swing.JScrollPane;
    1615import javax.swing.JSeparator;
     16import javax.swing.event.ChangeEvent;
     17import javax.swing.event.ChangeListener;
    1718
    1819import org.openstreetmap.josm.Main;
     
    3132
    3233    public static Collection<TaggingPreset> taggingPresets;
    33     private StyleSources sources;
     34    private StyleSourceEditor sources;
    3435    private JCheckBox sortMenu;
    3536    private JCheckBox enableDefault;
     
    4142                Main.pref.getBoolean("taggingpreset.enable-defaults", true));
    4243
    43         JPanel panel = new JPanel(new GridBagLayout());
    44         JScrollPane scrollpane = new JScrollPane(panel);
     44        final JPanel panel = new JPanel(new GridBagLayout());
    4545        panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    4646        panel.add(sortMenu, GBC.eol().insets(5,5,5,0));
    4747        panel.add(enableDefault, GBC.eol().insets(5,0,5,0));
    48         sources = new StyleSources("taggingpreset.sources", "taggingpreset.icon.sources",
    49         "http://josm.openstreetmap.de/presets", false, tr("Tagging Presets"));
     48        sources = new StyleSourceEditor("taggingpreset.sources", "taggingpreset.icon.sources",
     49        "http://josm.openstreetmap.de/presets");
    5050        panel.add(sources, GBC.eol().fill(GBC.BOTH));
    51         gui.mapcontent.addTab(tr("Tagging Presets"), scrollpane);
     51        gui.mapcontent.addTab(tr("Tagging Presets"), panel);
     52
     53        // this defers loading of tagging preset sources to the first time the tab
     54        // with the tagging presets is selected by the user
     55        //
     56        gui.mapcontent.addChangeListener(
     57                new ChangeListener() {
     58                    public void stateChanged(ChangeEvent e) {
     59                        if (gui.mapcontent.getSelectedComponent() == panel) {
     60                            sources.initiallyLoadAvailableStyles();
     61                        }
     62                    }
     63                }
     64        );
    5265    }
    5366
    5467    public boolean ok() {
    5568        boolean restart = Main.pref.put("taggingpreset.enable-defaults",
    56         enableDefault.getSelectedObjects() != null);
    57         if(Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null))
     69                enableDefault.getSelectedObjects() != null);
     70        if(Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null)) {
    5871            restart = true;
    59         if(sources.finish())
     72        }
     73        if(sources.finish()) {
    6074            restart = true;
     75        }
    6176        return restart;
    6277    }
     
    7691            {
    7792                JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu;
    78                 if (p instanceof TaggingPresetSeparator)
     93                if (p instanceof TaggingPresetSeparator) {
    7994                    m.add(new JSeparator());
    80                 else if (p instanceof TaggingPresetMenu)
     95                } else if (p instanceof TaggingPresetMenu)
    8196                {
    8297                    JMenu submenu = new JMenu(p);
     
    94109            }
    95110        }
    96         if(Main.pref.getBoolean("taggingpreset.sortmenu"))
     111        if(Main.pref.getBoolean("taggingpreset.sortmenu")) {
    97112            TaggingPresetMenu.sortMenu(Main.main.menu.presetsMenu);
     113        }
    98114    }
    99115}
Note: See TracChangeset for help on using the changeset viewer.