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

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

fix sonar squid:S2039 - Member variable visibility should be specified

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