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

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

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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