[298] | 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others
|
---|
[4968] | 2 | package org.openstreetmap.josm.gui.preferences.map;
|
---|
[343] | 3 |
|
---|
[3796] | 4 | import static org.openstreetmap.josm.tools.I18n.marktr;
|
---|
[343] | 5 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 6 |
|
---|
| 7 | import java.awt.GridBagLayout;
|
---|
[3321] | 8 | import java.io.IOException;
|
---|
| 9 | import java.util.ArrayList;
|
---|
[343] | 10 | import java.util.Collection;
|
---|
[3796] | 11 | import java.util.Collections;
|
---|
[614] | 12 | import java.util.HashMap;
|
---|
[3321] | 13 | import java.util.List;
|
---|
[5220] | 14 | import java.util.Map;
|
---|
[343] | 15 |
|
---|
[397] | 16 | import javax.swing.BorderFactory;
|
---|
| 17 | import javax.swing.JCheckBox;
|
---|
[3321] | 18 | import javax.swing.JLabel;
|
---|
[614] | 19 | import javax.swing.JMenu;
|
---|
[397] | 20 | import javax.swing.JMenuItem;
|
---|
[3321] | 21 | import javax.swing.JOptionPane;
|
---|
[343] | 22 | import javax.swing.JPanel;
|
---|
[1743] | 23 | import javax.swing.JSeparator;
|
---|
[2400] | 24 | import javax.swing.event.ChangeEvent;
|
---|
| 25 | import javax.swing.event.ChangeListener;
|
---|
[343] | 26 |
|
---|
| 27 | import org.openstreetmap.josm.Main;
|
---|
[3321] | 28 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
---|
[4968] | 29 | import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
|
---|
| 30 | import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
|
---|
| 31 | import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
|
---|
[3321] | 32 | import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
|
---|
[4968] | 33 | import org.openstreetmap.josm.gui.preferences.SourceEditor;
|
---|
[3797] | 34 | import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
|
---|
[4968] | 35 | import org.openstreetmap.josm.gui.preferences.SourceEntry;
|
---|
| 36 | import org.openstreetmap.josm.gui.preferences.SourceProvider;
|
---|
| 37 | import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
|
---|
[5220] | 38 | import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
|
---|
[343] | 39 | import org.openstreetmap.josm.gui.tagging.TaggingPreset;
|
---|
[824] | 40 | import org.openstreetmap.josm.gui.tagging.TaggingPresetMenu;
|
---|
[6068] | 41 | import org.openstreetmap.josm.gui.tagging.TaggingPresetReader;
|
---|
[895] | 42 | import org.openstreetmap.josm.gui.tagging.TaggingPresetSeparator;
|
---|
[3210] | 43 | import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
|
---|
[343] | 44 | import org.openstreetmap.josm.tools.GBC;
|
---|
[3321] | 45 | import org.xml.sax.SAXException;
|
---|
| 46 | import org.xml.sax.SAXParseException;
|
---|
[343] | 47 |
|
---|
[6362] | 48 | public final class TaggingPresetPreference implements SubPreferenceSetting {
|
---|
[343] | 49 |
|
---|
[1742] | 50 | public static class Factory implements PreferenceSettingFactory {
|
---|
[6068] | 51 | @Override
|
---|
[1742] | 52 | public PreferenceSetting createPreferenceSetting() {
|
---|
| 53 | return new TaggingPresetPreference();
|
---|
| 54 | }
|
---|
| 55 | }
|
---|
[5220] | 56 |
|
---|
[4968] | 57 | private TaggingPresetPreference() {
|
---|
| 58 | super();
|
---|
| 59 | }
|
---|
[1742] | 60 |
|
---|
[4839] | 61 | private static final List<SourceProvider> presetSourceProviders = new ArrayList<SourceProvider>();
|
---|
[1169] | 62 | public static Collection<TaggingPreset> taggingPresets;
|
---|
[3797] | 63 | private SourceEditor sources;
|
---|
[1399] | 64 | private JCheckBox sortMenu;
|
---|
[4874] | 65 |
|
---|
[4839] | 66 | public static final boolean registerSourceProvider(SourceProvider provider) {
|
---|
[4874] | 67 | if (provider != null)
|
---|
[4839] | 68 | return presetSourceProviders.add(provider);
|
---|
| 69 | return false;
|
---|
| 70 | }
|
---|
[397] | 71 |
|
---|
[3321] | 72 | private ValidationListener validationListener = new ValidationListener() {
|
---|
[6068] | 73 | @Override
|
---|
[3321] | 74 | public boolean validatePreferences() {
|
---|
[3797] | 75 | if (sources.hasActiveSourcesChanged()) {
|
---|
[3796] | 76 | List<Integer> sourcesToRemove = new ArrayList<Integer>();
|
---|
| 77 | int i = -1;
|
---|
[3321] | 78 | SOURCES:
|
---|
[3797] | 79 | for (SourceEntry source: sources.getActiveSources()) {
|
---|
[3796] | 80 | i++;
|
---|
[3321] | 81 | boolean canLoad = false;
|
---|
| 82 | try {
|
---|
[6068] | 83 | TaggingPresetReader.readAll(source.url, false);
|
---|
[3321] | 84 | canLoad = true;
|
---|
| 85 | } catch (IOException e) {
|
---|
[6248] | 86 | Main.warn(tr("Could not read tagging preset source: {0}", source));
|
---|
[3321] | 87 | ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Error"),
|
---|
| 88 | new String[] {tr("Yes"), tr("No"), tr("Cancel")});
|
---|
| 89 | ed.setContent(tr("Could not read tagging preset source: {0}\nDo you want to keep it?", source));
|
---|
| 90 | switch (ed.showDialog().getValue()) {
|
---|
| 91 | case 1:
|
---|
| 92 | continue SOURCES;
|
---|
| 93 | case 2:
|
---|
[3796] | 94 | sourcesToRemove.add(i);
|
---|
[3321] | 95 | continue SOURCES;
|
---|
| 96 | default:
|
---|
| 97 | return false;
|
---|
| 98 | }
|
---|
| 99 | } catch (SAXException e) {
|
---|
| 100 | // We will handle this in step with validation
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | String errorMessage = null;
|
---|
| 104 |
|
---|
| 105 | try {
|
---|
[6068] | 106 | TaggingPresetReader.readAll(source.url, true);
|
---|
[3321] | 107 | } catch (IOException e) {
|
---|
| 108 | // Should not happen, but at least show message
|
---|
[3534] | 109 | String msg = tr("Could not read tagging preset source {0}", source);
|
---|
[6248] | 110 | Main.error(msg);
|
---|
[3534] | 111 | JOptionPane.showMessageDialog(Main.parent, msg);
|
---|
[3321] | 112 | return false;
|
---|
| 113 | } catch (SAXParseException e) {
|
---|
| 114 | if (canLoad) {
|
---|
| 115 | errorMessage = tr("<html>Tagging preset source {0} can be loaded but it contains errors. " +
|
---|
| 116 | "Do you really want to use it?<br><br><table width=600>Error is: [{1}:{2}] {3}</table></html>",
|
---|
[3323] | 117 | source, e.getLineNumber(), e.getColumnNumber(), e.getMessage());
|
---|
[3321] | 118 | } else {
|
---|
| 119 | errorMessage = tr("<html>Unable to parse tagging preset source: {0}. " +
|
---|
| 120 | "Do you really want to use it?<br><br><table width=400>Error is: [{1}:{2}] {3}</table></html>",
|
---|
[3323] | 121 | source, e.getLineNumber(), e.getColumnNumber(), e.getMessage());
|
---|
[3321] | 122 | }
|
---|
| 123 | } catch (SAXException e) {
|
---|
| 124 | if (canLoad) {
|
---|
| 125 | errorMessage = tr("<html>Tagging preset source {0} can be loaded but it contains errors. " +
|
---|
| 126 | "Do you really want to use it?<br><br><table width=600>Error is: {1}</table></html>",
|
---|
[3323] | 127 | source, e.getMessage());
|
---|
[3321] | 128 | } else {
|
---|
| 129 | errorMessage = tr("<html>Unable to parse tagging preset source: {0}. " +
|
---|
[3323] | 130 | "Do you really want to use it?<br><br><table width=600>Error is: {1}</table></html>",
|
---|
| 131 | source, e.getMessage());
|
---|
[3321] | 132 | }
|
---|
| 133 |
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | if (errorMessage != null) {
|
---|
[6248] | 137 | Main.error(errorMessage);
|
---|
[3321] | 138 | int result = JOptionPane.showConfirmDialog(Main.parent, new JLabel(errorMessage), tr("Error"),
|
---|
| 139 | JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE);
|
---|
| 140 |
|
---|
| 141 | switch (result) {
|
---|
| 142 | case JOptionPane.YES_OPTION:
|
---|
| 143 | continue SOURCES;
|
---|
| 144 | case JOptionPane.NO_OPTION:
|
---|
[3796] | 145 | sourcesToRemove.add(i);
|
---|
[3321] | 146 | continue SOURCES;
|
---|
| 147 | default:
|
---|
| 148 | return false;
|
---|
| 149 | }
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
[3796] | 152 | sources.removeSources(sourcesToRemove);
|
---|
[3321] | 153 | return true;
|
---|
| 154 | } else
|
---|
| 155 | return true;
|
---|
| 156 | }
|
---|
| 157 | };
|
---|
| 158 |
|
---|
[6068] | 159 | @Override
|
---|
[2745] | 160 | public void addGui(final PreferenceTabbedPane gui) {
|
---|
[1399] | 161 | sortMenu = new JCheckBox(tr("Sort presets menu"),
|
---|
| 162 | Main.pref.getBoolean("taggingpreset.sortmenu", false));
|
---|
[343] | 163 |
|
---|
[2400] | 164 | final JPanel panel = new JPanel(new GridBagLayout());
|
---|
[1743] | 165 | panel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
|
---|
| 166 | panel.add(sortMenu, GBC.eol().insets(5,5,5,0));
|
---|
[3796] | 167 | sources = new TaggingPresetSourceEditor();
|
---|
[1743] | 168 | panel.add(sources, GBC.eol().fill(GBC.BOTH));
|
---|
[5631] | 169 | gui.getMapPreference().addSubTab(this, tr("Tagging Presets"), panel);
|
---|
[2400] | 170 |
|
---|
| 171 | // this defers loading of tagging preset sources to the first time the tab
|
---|
| 172 | // with the tagging presets is selected by the user
|
---|
| 173 | //
|
---|
[5631] | 174 | gui.getMapPreference().getTabPane().addChangeListener(
|
---|
[2400] | 175 | new ChangeListener() {
|
---|
[6084] | 176 | @Override
|
---|
[2400] | 177 | public void stateChanged(ChangeEvent e) {
|
---|
[5631] | 178 | if (gui.getMapPreference().getTabPane().getSelectedComponent() == panel) {
|
---|
[3797] | 179 | sources.initiallyLoadAvailableSources();
|
---|
[2400] | 180 | }
|
---|
| 181 | }
|
---|
| 182 | }
|
---|
[4874] | 183 | );
|
---|
[3321] | 184 | gui.addValidationListener(validationListener);
|
---|
[1169] | 185 | }
|
---|
| 186 |
|
---|
[4874] | 187 | static class TaggingPresetSourceEditor extends SourceEditor {
|
---|
[3796] | 188 |
|
---|
[6246] | 189 | private static final String iconpref = "taggingpreset.icon.sources";
|
---|
[3796] | 190 |
|
---|
| 191 | public TaggingPresetSourceEditor() {
|
---|
[6143] | 192 | super(false, Main.JOSM_WEBSITE+"/presets", presetSourceProviders);
|
---|
[2400] | 193 | }
|
---|
[3796] | 194 |
|
---|
| 195 | @Override
|
---|
| 196 | public Collection<? extends SourceEntry> getInitialSourcesList() {
|
---|
[4936] | 197 | return PresetPrefHelper.INSTANCE.get();
|
---|
[2400] | 198 | }
|
---|
[3796] | 199 |
|
---|
| 200 | @Override
|
---|
| 201 | public boolean finish() {
|
---|
[3797] | 202 | List<SourceEntry> activeStyles = activeSourcesModel.getSources();
|
---|
[3796] | 203 |
|
---|
[4936] | 204 | boolean changed = PresetPrefHelper.INSTANCE.put(activeStyles);
|
---|
[3796] | 205 |
|
---|
| 206 | if (tblIconPaths != null) {
|
---|
| 207 | List<String> iconPaths = iconPathsModel.getIconPaths();
|
---|
| 208 |
|
---|
| 209 | if (!iconPaths.isEmpty()) {
|
---|
| 210 | if (Main.pref.putCollection(iconpref, iconPaths)) {
|
---|
| 211 | changed = true;
|
---|
| 212 | }
|
---|
| 213 | } else if (Main.pref.putCollection(iconpref, null)) {
|
---|
| 214 | changed = true;
|
---|
| 215 | }
|
---|
| 216 | }
|
---|
| 217 | return changed;
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | @Override
|
---|
[3797] | 221 | public Collection<ExtendedSourceEntry> getDefault() {
|
---|
[4936] | 222 | return PresetPrefHelper.INSTANCE.getDefault();
|
---|
[3796] | 223 | }
|
---|
| 224 |
|
---|
| 225 | @Override
|
---|
| 226 | public Collection<String> getInitialIconPathsList() {
|
---|
| 227 | return Main.pref.getCollection(iconpref, null);
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | @Override
|
---|
| 231 | public String getStr(I18nString ident) {
|
---|
| 232 | switch (ident) {
|
---|
[4874] | 233 | case AVAILABLE_SOURCES:
|
---|
| 234 | return tr("Available presets:");
|
---|
| 235 | case ACTIVE_SOURCES:
|
---|
| 236 | return tr("Active presets:");
|
---|
| 237 | case NEW_SOURCE_ENTRY_TOOLTIP:
|
---|
| 238 | return tr("Add a new preset by entering filename or URL");
|
---|
| 239 | case NEW_SOURCE_ENTRY:
|
---|
| 240 | return tr("New preset entry:");
|
---|
| 241 | case REMOVE_SOURCE_TOOLTIP:
|
---|
| 242 | return tr("Remove the selected presets from the list of active presets");
|
---|
| 243 | case EDIT_SOURCE_TOOLTIP:
|
---|
| 244 | return tr("Edit the filename or URL for the selected active preset");
|
---|
| 245 | case ACTIVATE_TOOLTIP:
|
---|
| 246 | return tr("Add the selected available presets to the list of active presets");
|
---|
| 247 | case RELOAD_ALL_AVAILABLE:
|
---|
| 248 | return marktr("Reloads the list of available presets from ''{0}''");
|
---|
| 249 | case LOADING_SOURCES_FROM:
|
---|
| 250 | return marktr("Loading preset sources from ''{0}''");
|
---|
| 251 | case FAILED_TO_LOAD_SOURCES_FROM:
|
---|
| 252 | return marktr("<html>Failed to load the list of preset sources from<br>"
|
---|
| 253 | + "''{0}''.<br>"
|
---|
| 254 | + "<br>"
|
---|
| 255 | + "Details (untranslated):<br>{1}</html>");
|
---|
| 256 | case FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC:
|
---|
| 257 | return "/Preferences/Presets#FailedToLoadPresetSources";
|
---|
| 258 | case ILLEGAL_FORMAT_OF_ENTRY:
|
---|
| 259 | return marktr("Warning: illegal format of entry in preset list ''{0}''. Got ''{1}''");
|
---|
| 260 | default: throw new AssertionError();
|
---|
[3796] | 261 | }
|
---|
| 262 | }
|
---|
| 263 | }
|
---|
| 264 |
|
---|
[6068] | 265 | @Override
|
---|
[3796] | 266 | public boolean ok() {
|
---|
| 267 | boolean restart = Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null);
|
---|
| 268 | restart |= sources.finish();
|
---|
| 269 |
|
---|
[1180] | 270 | return restart;
|
---|
[1169] | 271 | }
|
---|
| 272 |
|
---|
| 273 | /**
|
---|
| 274 | * Initialize the tagging presets (load and may display error)
|
---|
| 275 | */
|
---|
| 276 | public static void initialize() {
|
---|
[6068] | 277 | taggingPresets = TaggingPresetReader.readFromPreferences(false);
|
---|
[3321] | 278 | for (TaggingPreset tp: taggingPresets) {
|
---|
| 279 | if (!(tp instanceof TaggingPresetSeparator)) {
|
---|
| 280 | Main.toolbar.register(tp);
|
---|
| 281 | }
|
---|
| 282 | }
|
---|
[1169] | 283 | if (taggingPresets.isEmpty()) {
|
---|
| 284 | Main.main.menu.presetsMenu.setVisible(false);
|
---|
| 285 | }
|
---|
| 286 | else
|
---|
| 287 | {
|
---|
[3210] | 288 | AutoCompletionManager.cachePresets(taggingPresets);
|
---|
[1169] | 289 | HashMap<TaggingPresetMenu,JMenu> submenus = new HashMap<TaggingPresetMenu,JMenu>();
|
---|
| 290 | for (final TaggingPreset p : taggingPresets)
|
---|
| 291 | {
|
---|
| 292 | JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu;
|
---|
[2400] | 293 | if (p instanceof TaggingPresetSeparator) {
|
---|
[1169] | 294 | m.add(new JSeparator());
|
---|
[2400] | 295 | } else if (p instanceof TaggingPresetMenu)
|
---|
[1169] | 296 | {
|
---|
| 297 | JMenu submenu = new JMenu(p);
|
---|
[2140] | 298 | submenu.setText(p.getLocaleName());
|
---|
[1169] | 299 | ((TaggingPresetMenu)p).menu = submenu;
|
---|
| 300 | submenus.put((TaggingPresetMenu)p, submenu);
|
---|
| 301 | m.add(submenu);
|
---|
| 302 | }
|
---|
| 303 | else
|
---|
| 304 | {
|
---|
| 305 | JMenuItem mi = new JMenuItem(p);
|
---|
[1579] | 306 | mi.setText(p.getLocaleName());
|
---|
[1169] | 307 | m.add(mi);
|
---|
| 308 | }
|
---|
| 309 | }
|
---|
| 310 | }
|
---|
[2400] | 311 | if(Main.pref.getBoolean("taggingpreset.sortmenu")) {
|
---|
[1399] | 312 | TaggingPresetMenu.sortMenu(Main.main.menu.presetsMenu);
|
---|
[2400] | 313 | }
|
---|
[1169] | 314 | }
|
---|
[3796] | 315 |
|
---|
[4936] | 316 | public static class PresetPrefHelper extends SourceEditor.SourcePrefHelper {
|
---|
[3796] | 317 |
|
---|
[4936] | 318 | public final static PresetPrefHelper INSTANCE = new PresetPrefHelper();
|
---|
[3855] | 319 |
|
---|
[4936] | 320 | public PresetPrefHelper() {
|
---|
[5220] | 321 | super("taggingpreset.entries", "taggingpreset.sources-list");
|
---|
[3796] | 322 | }
|
---|
| 323 |
|
---|
| 324 | @Override
|
---|
[3797] | 325 | public Collection<ExtendedSourceEntry> getDefault() {
|
---|
| 326 | ExtendedSourceEntry i = new ExtendedSourceEntry("defaultpresets.xml", "resource://data/defaultpresets.xml");
|
---|
[3894] | 327 | i.title = tr("Internal Preset");
|
---|
[3796] | 328 | i.description = tr("The default preset for JOSM");
|
---|
| 329 | return Collections.singletonList(i);
|
---|
| 330 | }
|
---|
| 331 |
|
---|
| 332 | @Override
|
---|
[5220] | 333 | public Map<String, String> serialize(SourceEntry entry) {
|
---|
| 334 | Map<String, String> res = new HashMap<String, String>();
|
---|
| 335 | res.put("url", entry.url);
|
---|
| 336 | res.put("title", entry.title == null ? "" : entry.title);
|
---|
| 337 | return res;
|
---|
[3796] | 338 | }
|
---|
| 339 |
|
---|
| 340 | @Override
|
---|
[5220] | 341 | public SourceEntry deserialize(Map<String, String> s) {
|
---|
| 342 | return new SourceEntry(s.get("url"), null, s.get("title"), true);
|
---|
| 343 | }
|
---|
[3796] | 344 | }
|
---|
[4968] | 345 |
|
---|
| 346 | @Override
|
---|
| 347 | public boolean isExpert() {
|
---|
| 348 | return false;
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | @Override
|
---|
| 352 | public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
|
---|
| 353 | return gui.getMapPreference();
|
---|
| 354 | }
|
---|
[343] | 355 | }
|
---|