source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java@ 6529

Last change on this file since 6529 was 6529, checked in by Don-vip, 10 years ago

Various stuff:

  • see #9414: remove old DeprecatedTags test
  • refactor some classes in gui.preferences package
  • improve javadoc
  • Property svn:eol-style set to native
File size: 16.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.advanced;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Dimension;
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
10import java.io.File;
11import java.io.IOException;
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.LinkedHashMap;
16import java.util.List;
17import java.util.Map;
18import java.util.Map.Entry;
19
20import javax.swing.AbstractAction;
21import javax.swing.Box;
22import javax.swing.JButton;
23import javax.swing.JFileChooser;
24import javax.swing.JLabel;
25import javax.swing.JMenu;
26import javax.swing.JOptionPane;
27import javax.swing.JPanel;
28import javax.swing.JPopupMenu;
29import javax.swing.JScrollPane;
30import javax.swing.event.DocumentEvent;
31import javax.swing.event.DocumentListener;
32import javax.swing.event.MenuEvent;
33import javax.swing.event.MenuListener;
34import javax.swing.filechooser.FileFilter;
35
36import org.openstreetmap.josm.Main;
37import org.openstreetmap.josm.actions.DiskAccessAction;
38import org.openstreetmap.josm.data.CustomConfigurator;
39import org.openstreetmap.josm.data.Preferences;
40import org.openstreetmap.josm.data.Preferences.Setting;
41import org.openstreetmap.josm.gui.actionsupport.LogShowDialog;
42import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
43import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
44import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
45import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
46import org.openstreetmap.josm.gui.util.GuiHelper;
47import org.openstreetmap.josm.gui.widgets.JosmTextField;
48import org.openstreetmap.josm.tools.GBC;
49
50public final class AdvancedPreference extends DefaultTabPreferenceSetting {
51
52 /**
53 * Factory used to create a new {@code AdvancedPreference}.
54 */
55 public static class Factory implements PreferenceSettingFactory {
56 @Override
57 public PreferenceSetting createPreferenceSetting() {
58 return new AdvancedPreference();
59 }
60 }
61
62 private AdvancedPreference() {
63 super("advanced", tr("Advanced Preferences"), tr("Setting Preference entries directly. Use with caution!"));
64 }
65
66 @Override
67 public boolean isExpert() {
68 return true;
69 }
70
71 protected List<PrefEntry> allData;
72 protected List<PrefEntry> displayData = new ArrayList<PrefEntry>();
73 protected JosmTextField txtFilter;
74 protected PreferencesTable table;
75
76 @Override
77 public void addGui(final PreferenceTabbedPane gui) {
78 JPanel p = gui.createPreferenceTab(this);
79
80 txtFilter = new JosmTextField();
81 JLabel lbFilter = new JLabel(tr("Search: "));
82 lbFilter.setLabelFor(txtFilter);
83 p.add(lbFilter);
84 p.add(txtFilter, GBC.eol().fill(GBC.HORIZONTAL));
85 txtFilter.getDocument().addDocumentListener(new DocumentListener(){
86 @Override public void changedUpdate(DocumentEvent e) {
87 action();
88 }
89 @Override public void insertUpdate(DocumentEvent e) {
90 action();
91 }
92 @Override public void removeUpdate(DocumentEvent e) {
93 action();
94 }
95 private void action() {
96 applyFilter();
97 }
98 });
99 readPreferences(Main.pref);
100
101 applyFilter();
102 table = new PreferencesTable(displayData);
103 JScrollPane scroll = new JScrollPane(table);
104 p.add(scroll, GBC.eol().fill(GBC.BOTH));
105 scroll.setPreferredSize(new Dimension(400,200));
106
107 JButton add = new JButton(tr("Add"));
108 p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
109 p.add(add, GBC.std().insets(0,5,0,0));
110 add.addActionListener(new ActionListener(){
111 @Override public void actionPerformed(ActionEvent e) {
112 PrefEntry pe = table.addPreference(gui);
113 if (pe!=null) {
114 allData.add(pe);
115 Collections.sort(allData);
116 applyFilter();
117 }
118 }
119 });
120
121 JButton edit = new JButton(tr("Edit"));
122 p.add(edit, GBC.std().insets(5,5,5,0));
123 edit.addActionListener(new ActionListener(){
124 @Override public void actionPerformed(ActionEvent e) {
125 boolean ok = table.editPreference(gui);
126 if (ok) applyFilter();
127 }
128 });
129
130 JButton reset = new JButton(tr("Reset"));
131 p.add(reset, GBC.std().insets(0,5,0,0));
132 reset.addActionListener(new ActionListener(){
133 @Override public void actionPerformed(ActionEvent e) {
134 table.resetPreferences(gui);
135 }
136 });
137
138 JButton read = new JButton(tr("Read from file"));
139 p.add(read, GBC.std().insets(5,5,0,0));
140 read.addActionListener(new ActionListener(){
141 @Override public void actionPerformed(ActionEvent e) {
142 readPreferencesFromXML();
143 }
144 });
145
146 JButton export = new JButton(tr("Export selected items"));
147 p.add(export, GBC.std().insets(5,5,0,0));
148 export.addActionListener(new ActionListener(){
149 @Override public void actionPerformed(ActionEvent e) {
150 exportSelectedToXML();
151 }
152 });
153
154 final JButton more = new JButton(tr("More..."));
155 p.add(more, GBC.std().insets(5,5,0,0));
156 more.addActionListener(new ActionListener() {
157 JPopupMenu menu = buildPopupMenu();
158 @Override public void actionPerformed(ActionEvent ev) {
159 menu.show(more, 0, 0);
160 }
161 });
162 }
163
164 private void readPreferences(Preferences tmpPrefs) {
165 Map<String, Setting> loaded;
166 Map<String, Setting> orig = Main.pref.getAllSettings();
167 Map<String, Setting> defaults = tmpPrefs.getAllDefaults();
168 orig.remove("osm-server.password");
169 defaults.remove("osm-server.password");
170 if (tmpPrefs != Main.pref) {
171 loaded = tmpPrefs.getAllSettings();
172 // plugins preference keys may be changed directly later, after plugins are downloaded
173 // so we do not want to show it in the table as "changed" now
174 Setting pluginSetting = orig.get("plugins");
175 if (pluginSetting!=null) {
176 loaded.put("plugins", pluginSetting);
177 }
178 } else {
179 loaded = orig;
180 }
181 allData = prepareData(loaded, orig, defaults);
182 }
183
184 private File[] askUserForCustomSettingsFiles(boolean saveFileFlag, String title) {
185 FileFilter filter = new FileFilter() {
186 @Override
187 public boolean accept(File f) {
188 return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml");
189 }
190 @Override
191 public String getDescription() {
192 return tr("JOSM custom settings files (*.xml)");
193 }
194 };
195 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(!saveFileFlag, !saveFileFlag, title, filter, JFileChooser.FILES_ONLY, "customsettings.lastDirectory");
196 if (fc != null) {
197 File[] sel = fc.isMultiSelectionEnabled() ? fc.getSelectedFiles() : (new File[]{fc.getSelectedFile()});
198 if (sel.length==1 && !sel[0].getName().contains(".")) sel[0]=new File(sel[0].getAbsolutePath()+".xml");
199 return sel;
200 }
201 return new File[0];
202 }
203
204 private void exportSelectedToXML() {
205 List<String> keys = new ArrayList<String>();
206 boolean hasLists = false;
207
208 for (PrefEntry p: table.getSelectedItems()) {
209 // preferences with default values are not saved
210 if (!(p.getValue() instanceof Preferences.StringSetting)) {
211 hasLists = true; // => append and replace differs
212 }
213 if (!p.isDefault()) {
214 keys.add(p.getKey());
215 }
216 }
217
218 if (keys.isEmpty()) {
219 JOptionPane.showMessageDialog(Main.parent,
220 tr("Please select some preference keys not marked as default"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
221 return;
222 }
223
224 File[] files = askUserForCustomSettingsFiles(true, tr("Export preferences keys to JOSM customization file"));
225 if (files.length == 0) {
226 return;
227 }
228
229 int answer = 0;
230 if (hasLists) {
231 answer = JOptionPane.showOptionDialog(
232 Main.parent, tr("What to do with preference lists when this file is to be imported?"), tr("Question"),
233 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
234 new String[]{tr("Append preferences from file to existing values"), tr("Replace existing values")}, 0);
235 }
236 CustomConfigurator.exportPreferencesKeysToFile(files[0].getAbsolutePath(), answer == 0, keys);
237 }
238
239 private void readPreferencesFromXML() {
240 File[] files = askUserForCustomSettingsFiles(false, tr("Open JOSM customization file"));
241 if (files.length == 0) return;
242
243 Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
244
245 StringBuilder log = new StringBuilder();
246 log.append("<html>");
247 for (File f : files) {
248 CustomConfigurator.readXML(f, tmpPrefs);
249 log.append(CustomConfigurator.getLog());
250 }
251 log.append("</html>");
252 String msg = log.toString().replace("\n", "<br/>");
253
254 new LogShowDialog(tr("Import log"), tr("<html>Here is file import summary. <br/>"
255 + "You can reject preferences changes by pressing \"Cancel\" in preferences dialog <br/>"
256 + "To activate some changes JOSM restart may be needed.</html>"), msg).showDialog();
257
258 readPreferences(tmpPrefs);
259 // sorting after modification - first modified, then non-default, then default entries
260 Collections.sort(allData, customComparator);
261 applyFilter();
262 }
263
264 private Comparator<PrefEntry> customComparator = new Comparator<PrefEntry>() {
265 @Override
266 public int compare(PrefEntry o1, PrefEntry o2) {
267 if (o1.isChanged() && !o2.isChanged()) return -1;
268 if (o2.isChanged() && !o1.isChanged()) return 1;
269 if (!(o1.isDefault()) && o2.isDefault()) return -1;
270 if (!(o2.isDefault()) && o1.isDefault()) return 1;
271 return o1.compareTo(o2);
272 }
273 };
274
275 private List<PrefEntry> prepareData(Map<String, Setting> loaded, Map<String, Setting> orig, Map<String, Setting> defaults) {
276 List<PrefEntry> data = new ArrayList<PrefEntry>();
277 for (Entry<String, Setting> e : loaded.entrySet()) {
278 Setting value = e.getValue();
279 Setting old = orig.get(e.getKey());
280 Setting def = defaults.get(e.getKey());
281 if (def == null) {
282 def = value.getNullInstance();
283 }
284 PrefEntry en = new PrefEntry(e.getKey(), value, def, false);
285 // after changes we have nondefault value. Value is changed if is not equal to old value
286 if ( !Preferences.isEqual(old, value) ) {
287 en.markAsChanged();
288 }
289 data.add(en);
290 }
291 for (Entry<String, Setting> e : defaults.entrySet()) {
292 if (!loaded.containsKey(e.getKey())) {
293 PrefEntry en = new PrefEntry(e.getKey(), e.getValue(), e.getValue(), true);
294 // after changes we have default value. So, value is changed if old value is not default
295 Setting old = orig.get(e.getKey());
296 if ( old!=null ) {
297 en.markAsChanged();
298 }
299 data.add(en);
300 }
301 }
302 Collections.sort(data);
303 displayData.clear();
304 displayData.addAll(data);
305 return data;
306 }
307
308 Map<String,String> profileTypes = new LinkedHashMap<String, String>();
309
310 private JPopupMenu buildPopupMenu() {
311 JPopupMenu menu = new JPopupMenu();
312 profileTypes.put(marktr("shortcut"), "shortcut\\..*");
313 profileTypes.put(marktr("color"), "color\\..*");
314 profileTypes.put(marktr("toolbar"), "toolbar.*");
315 profileTypes.put(marktr("imagery"), "imagery.*");
316
317 for (Entry<String,String> e: profileTypes.entrySet()) {
318 menu.add(new ExportProfileAction(Main.pref, e.getKey(), e.getValue()));
319 }
320
321 menu.addSeparator();
322 menu.add(getProfileMenu());
323 menu.addSeparator();
324 menu.add(new AbstractAction(tr("Reset preferences")) {
325 @Override public void actionPerformed(ActionEvent ae) {
326 if (!GuiHelper.warnUser(tr("Reset preferences"),
327 "<html>"+
328 tr("You are about to clear all preferences to their default values<br />"+
329 "All your settings will be deleted: plugins, imagery, filters, toolbar buttons, keyboard, etc. <br />"+
330 "Are you sure you want to continue?")
331 +"</html>", null, "")) {
332 Main.pref.resetToDefault();
333 try {
334 Main.pref.save();
335 } catch (IOException e) {
336 Main.warn("IOException while saving preferences: "+e.getMessage());
337 }
338 readPreferences(Main.pref);
339 applyFilter();
340 }
341 }
342 });
343 return menu;
344 }
345
346 private JMenu getProfileMenu() {
347 final JMenu p =new JMenu(tr("Load profile"));
348 p.addMenuListener(new MenuListener() {
349 @Override
350 public void menuSelected(MenuEvent me) {
351 p.removeAll();
352 for (File f: new File(".").listFiles()) {
353 String s = f.getName();
354 int idx = s.indexOf('_');
355 if (idx>=0) {
356 String t=s.substring(0,idx);
357 if (profileTypes.containsKey(t)) {
358 p.add(new ImportProfileAction(s, f, t));
359 }
360 }
361 }
362 for (File f: Main.pref.getPreferencesDirFile().listFiles()) {
363 String s = f.getName();
364 int idx = s.indexOf('_');
365 if (idx>=0) {
366 String t=s.substring(0,idx);
367 if (profileTypes.containsKey(t)) {
368 p.add(new ImportProfileAction(s, f, t));
369 }
370 }
371 }
372 }
373 @Override public void menuDeselected(MenuEvent me) { }
374 @Override public void menuCanceled(MenuEvent me) { }
375 });
376 return p;
377 }
378
379 private class ImportProfileAction extends AbstractAction {
380 private final File file;
381 private final String type;
382
383 public ImportProfileAction(String name, File file, String type) {
384 super(name);
385 this.file = file;
386 this.type = type;
387 }
388
389 @Override
390 public void actionPerformed(ActionEvent ae) {
391 Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
392 CustomConfigurator.readXML(file, tmpPrefs);
393 readPreferences(tmpPrefs);
394 String prefRegex = profileTypes.get(type);
395 // clean all the preferences from the chosen group
396 for (PrefEntry p : allData) {
397 if (p.getKey().matches(prefRegex) && !p.isDefault()) {
398 p.reset();
399 }
400 }
401 // allow user to review the changes in table
402 Collections.sort(allData, customComparator);
403 applyFilter();
404 }
405 }
406
407 private void applyFilter() {
408 displayData.clear();
409 for (PrefEntry e : allData) {
410 String prefKey = e.getKey();
411 Setting valueSetting = e.getValue();
412 String prefValue = valueSetting.getValue() == null ? "" : valueSetting.getValue().toString();
413
414 String[] input = txtFilter.getText().split("\\s+");
415 boolean canHas = true;
416
417 // Make 'wmsplugin cache' search for e.g. 'cache.wmsplugin'
418 final String prefKeyLower = prefKey.toLowerCase();
419 final String prefValueLower = prefValue.toLowerCase();
420 for (String bit : input) {
421 bit = bit.toLowerCase();
422 if (!prefKeyLower.contains(bit) && !prefValueLower.contains(bit)) {
423 canHas = false;
424 break;
425 }
426 }
427 if (canHas) {
428 displayData.add(e);
429 }
430 }
431 if (table!=null) table.fireDataChanged();
432 }
433
434 @Override
435 public boolean ok() {
436 for (PrefEntry e : allData) {
437 if (e.isChanged()) {
438 Main.pref.putSetting(e.getKey(), e.getValue());
439 }
440 }
441 return false;
442 }
443}
Note: See TracBrowser for help on using the repository browser.