001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.gui.preferences.map;
003    
004    import static org.openstreetmap.josm.tools.I18n.marktr;
005    import static org.openstreetmap.josm.tools.I18n.tr;
006    
007    import java.awt.GridBagLayout;
008    import java.util.ArrayList;
009    import java.util.Arrays;
010    import java.util.Collection;
011    import java.util.HashMap;
012    import java.util.List;
013    import java.util.Map;
014    import java.util.TreeSet;
015    
016    import javax.swing.BorderFactory;
017    import javax.swing.JCheckBox;
018    import javax.swing.JPanel;
019    import javax.swing.event.ChangeEvent;
020    import javax.swing.event.ChangeListener;
021    
022    import org.openstreetmap.josm.Main;
023    import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
024    import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
025    import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
026    import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
027    import org.openstreetmap.josm.gui.preferences.SourceEditor;
028    import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
029    import org.openstreetmap.josm.gui.preferences.SourceEntry;
030    import org.openstreetmap.josm.gui.preferences.SourceProvider;
031    import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
032    import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
033    import org.openstreetmap.josm.tools.GBC;
034    import org.openstreetmap.josm.tools.Predicate;
035    import org.openstreetmap.josm.tools.Utils;
036    
037    public class MapPaintPreference implements SubPreferenceSetting {
038        private SourceEditor sources;
039        private JCheckBox enableIconDefault;
040    
041        private static final List<SourceProvider> styleSourceProviders = new ArrayList<SourceProvider>();
042    
043        public static final boolean registerSourceProvider(SourceProvider provider) {
044            if (provider != null)
045                return styleSourceProviders.add(provider);
046            return false;
047        }
048    
049        public static class Factory implements PreferenceSettingFactory {
050            public PreferenceSetting createPreferenceSetting() {
051                return new MapPaintPreference();
052            }
053        }
054    
055        public void addGui(final PreferenceTabbedPane gui) {
056            enableIconDefault = new JCheckBox(tr("Enable built-in icon defaults"),
057                    Main.pref.getBoolean("mappaint.icon.enable-defaults", true));
058    
059            sources = new MapPaintSourceEditor();
060    
061            final JPanel panel = new JPanel(new GridBagLayout());
062            panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
063    
064            panel.add(sources, GBC.eol().fill(GBC.BOTH));
065            panel.add(enableIconDefault, GBC.eol().insets(11,2,5,0));
066    
067            gui.getMapPreference().mapcontent.addTab(tr("Map Paint Styles"), panel);
068    
069            // this defers loading of style sources to the first time the tab
070            // with the map paint preferences is selected by the user
071            //
072            gui.getMapPreference().mapcontent.addChangeListener(
073                    new ChangeListener() {
074                        public void stateChanged(ChangeEvent e) {
075                            if (gui.getMapPreference().mapcontent.getSelectedComponent() == panel) {
076                                sources.initiallyLoadAvailableSources();
077                            }
078                        }
079                    }
080                    );
081        }
082    
083        static class MapPaintSourceEditor extends SourceEditor {
084    
085            final private String iconpref = "mappaint.icon.sources";
086    
087            public MapPaintSourceEditor() {
088                super(true, "http://josm.openstreetmap.de/styles", styleSourceProviders);
089            }
090    
091            @Override
092            public Collection<? extends SourceEntry> getInitialSourcesList() {
093                return MapPaintPrefHelper.INSTANCE.get();
094            }
095    
096            @Override
097            public boolean finish() {
098                List<SourceEntry> activeStyles = activeSourcesModel.getSources();
099    
100                boolean changed = MapPaintPrefHelper.INSTANCE.put(activeStyles);
101    
102                if (tblIconPaths != null) {
103                    List<String> iconPaths = iconPathsModel.getIconPaths();
104    
105                    if (!iconPaths.isEmpty()) {
106                        if (Main.pref.putCollection(iconpref, iconPaths)) {
107                            changed = true;
108                        }
109                    } else if (Main.pref.putCollection(iconpref, null)) {
110                        changed = true;
111                    }
112                }
113                return changed;
114            }
115    
116            @Override
117            public Collection<ExtendedSourceEntry> getDefault() {
118                return MapPaintPrefHelper.INSTANCE.getDefault();
119            }
120    
121            @Override
122            public Collection<String> getInitialIconPathsList() {
123                return Main.pref.getCollection(iconpref, null);
124            }
125    
126            @Override
127            public String getStr(I18nString ident) {
128                switch (ident) {
129                case AVAILABLE_SOURCES:
130                    return tr("Available styles:");
131                case ACTIVE_SOURCES:
132                    return tr("Active styles:");
133                case NEW_SOURCE_ENTRY_TOOLTIP:
134                    return tr("Add a new style by entering filename or URL");
135                case NEW_SOURCE_ENTRY:
136                    return tr("New style entry:");
137                case REMOVE_SOURCE_TOOLTIP:
138                    return tr("Remove the selected styles from the list of active styles");
139                case EDIT_SOURCE_TOOLTIP:
140                    return tr("Edit the filename or URL for the selected active style");
141                case ACTIVATE_TOOLTIP:
142                    return tr("Add the selected available styles to the list of active styles");
143                case RELOAD_ALL_AVAILABLE:
144                    return marktr("Reloads the list of available styles from ''{0}''");
145                case LOADING_SOURCES_FROM:
146                    return marktr("Loading style sources from ''{0}''");
147                case FAILED_TO_LOAD_SOURCES_FROM:
148                    return marktr("<html>Failed to load the list of style sources from<br>"
149                            + "''{0}''.<br>"
150                            + "<br>"
151                            + "Details (untranslated):<br>{1}</html>");
152                case FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC:
153                    return "/Preferences/Styles#FailedToLoadStyleSources";
154                case ILLEGAL_FORMAT_OF_ENTRY:
155                    return marktr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''");
156                default: throw new AssertionError();
157                }
158            }
159    
160        }
161    
162        public boolean ok() {
163            boolean reload = Main.pref.put("mappaint.icon.enable-defaults", enableIconDefault.isSelected());
164            reload |= sources.finish();
165            if (reload) {
166                MapPaintStyles.readFromPreferences();
167            }
168            if (Main.isDisplayingMapView())
169            {
170                MapPaintStyles.getStyles().clearCached();
171            }
172            return false;
173        }
174    
175        /**
176         * Initialize the styles
177         */
178        public static void initialize() {
179            MapPaintStyles.readFromPreferences();
180        }
181    
182        public static class MapPaintPrefHelper extends SourceEditor.SourcePrefHelper {
183    
184            public final static MapPaintPrefHelper INSTANCE = new MapPaintPrefHelper();
185    
186            public MapPaintPrefHelper() {
187                super("mappaint.style.entries", "mappaint.style.sources-list");
188            }
189    
190            @Override
191            public List<SourceEntry> get() {
192                List<SourceEntry> ls = super.get();
193                if (insertNewDefaults(ls)) {
194                    put(ls);
195                }
196                return ls;
197            }
198    
199            /**
200             * If the selection of default styles changes in future releases, add
201             * the new entries to the user-configured list. Remember the known URLs,
202             * so an item that was deleted explicitly is not added again.
203             */
204            private boolean insertNewDefaults(List<SourceEntry> list) {
205                boolean changed = false;
206    
207                Collection<String> knownDefaults = new TreeSet<String>(Main.pref.getCollection("mappaint.style.known-defaults"));
208    
209                Collection<ExtendedSourceEntry> defaults = getDefault();
210                int insertionIdx = 0;
211                for (final SourceEntry def : defaults) {
212                    int i = Utils.indexOf(list,
213                            new Predicate<SourceEntry>() {
214                        @Override
215                        public boolean evaluate(SourceEntry se) {
216                            return Utils.equal(def.url, se.url);
217                        }
218                    });
219                    if (i == -1 && !knownDefaults.contains(def.url)) {
220                        list.add(insertionIdx, def);
221                        insertionIdx++;
222                        changed = true;
223                    } else {
224                        if (i >= insertionIdx) {
225                            insertionIdx = i + 1;
226                        }
227                    }
228                }
229    
230                for (SourceEntry def : defaults) {
231                    knownDefaults.add(def.url);
232                }
233                Main.pref.putCollection("mappaint.style.known-defaults", knownDefaults);
234    
235                return changed;
236            }
237    
238            @Override
239            public Collection<ExtendedSourceEntry> getDefault() {
240                ExtendedSourceEntry defJOSM = new ExtendedSourceEntry("elemstyles.xml", "resource://styles/standard/elemstyles.xml");
241                defJOSM.active = true;
242                defJOSM.name = "standard";
243                defJOSM.title = tr("JOSM Internal Style");
244                defJOSM.description = tr("Internal style to be used as base for runtime switchable overlay styles");
245                ExtendedSourceEntry defPL2 = new ExtendedSourceEntry("potlatch2.mapcss", "resource://styles/standard/potlatch2.mapcss");
246                defPL2.active = false;
247                defPL2.name = "standard";
248                defPL2.title = tr("Potlatch 2");
249                defPL2.description = tr("the main Potlatch 2 style");
250    
251                return Arrays.asList(new ExtendedSourceEntry[] { defJOSM, defPL2 });
252            }
253    
254            @Override
255            public Map<String, String> serialize(SourceEntry entry) {
256                Map<String, String> res = new HashMap<String, String>();
257                res.put("url", entry.url);
258                res.put("title", entry.title == null ? "" : entry.title);
259                res.put("active", Boolean.toString(entry.active));
260                if (entry.name != null) {
261                    res.put("ptoken", entry.name);
262                }
263                return res;
264            }
265    
266            @Override
267            public SourceEntry deserialize(Map<String, String> s) {
268                return new SourceEntry(s.get("url"), s.get("ptoken"), s.get("title"), Boolean.parseBoolean(s.get("active")));
269            }
270    
271            @Override
272            public Map<String, String> migrate(Collection<String> old) {
273                List<String> entryStr = new ArrayList<String>(old);
274                if (entryStr.size() < 4)
275                    return null;
276                Map<String, String> res = new HashMap<String, String>();
277                res.put("url", entryStr.get(0));
278                res.put("ptoken", entryStr.get(1));
279                res.put("title", entryStr.get(2));
280                res.put("active", entryStr.get(3));
281                return res;
282            }
283        }
284    
285        @Override
286        public boolean isExpert() {
287            return false;
288        }
289    
290        @Override
291        public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
292            return gui.getMapPreference();
293        }
294    }