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