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

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

see #8465 - use diamond operator where applicable

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