source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java@ 17318

Last change on this file since 17318 was 17316, checked in by Don-vip, 3 years ago

see #7548 - fix #20094 - fix NPE

  • Property svn:eol-style set to native
File size: 29.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Container;
9import java.awt.Font;
10import java.awt.FontMetrics;
11import java.awt.GridBagConstraints;
12import java.awt.GridBagLayout;
13import java.awt.event.MouseWheelEvent;
14import java.awt.event.MouseWheelListener;
15import java.util.ArrayList;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.HashSet;
19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.NoSuchElementException;
23import java.util.Objects;
24import java.util.OptionalInt;
25import java.util.Set;
26import java.util.function.Predicate;
27import java.util.stream.IntStream;
28
29import javax.swing.BorderFactory;
30import javax.swing.Icon;
31import javax.swing.ImageIcon;
32import javax.swing.JLabel;
33import javax.swing.JOptionPane;
34import javax.swing.JPanel;
35import javax.swing.JScrollPane;
36import javax.swing.JTabbedPane;
37import javax.swing.SwingConstants;
38import javax.swing.UIManager;
39import javax.swing.event.ChangeEvent;
40import javax.swing.event.ChangeListener;
41
42import org.openstreetmap.josm.actions.ExpertToggleAction;
43import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener;
44import org.openstreetmap.josm.actions.RestartAction;
45import org.openstreetmap.josm.gui.HelpAwareOptionPane;
46import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
47import org.openstreetmap.josm.gui.MainApplication;
48import org.openstreetmap.josm.gui.preferences.advanced.AdvancedPreference;
49import org.openstreetmap.josm.gui.preferences.audio.AudioPreference;
50import org.openstreetmap.josm.gui.preferences.display.ColorPreference;
51import org.openstreetmap.josm.gui.preferences.display.DisplayPreference;
52import org.openstreetmap.josm.gui.preferences.display.DrawingPreference;
53import org.openstreetmap.josm.gui.preferences.display.GPXPreference;
54import org.openstreetmap.josm.gui.preferences.display.LafPreference;
55import org.openstreetmap.josm.gui.preferences.display.LanguagePreference;
56import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
57import org.openstreetmap.josm.gui.preferences.map.BackupPreference;
58import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
59import org.openstreetmap.josm.gui.preferences.map.MapPreference;
60import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
61import org.openstreetmap.josm.gui.preferences.plugin.PluginPreference;
62import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
63import org.openstreetmap.josm.gui.preferences.remotecontrol.RemoteControlPreference;
64import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
65import org.openstreetmap.josm.gui.preferences.server.ServerAccessPreference;
66import org.openstreetmap.josm.gui.preferences.shortcut.ShortcutPreference;
67import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference;
68import org.openstreetmap.josm.gui.preferences.validator.ValidatorTagCheckerRulesPreference;
69import org.openstreetmap.josm.gui.preferences.validator.ValidatorTestsPreference;
70import org.openstreetmap.josm.gui.util.GuiHelper;
71import org.openstreetmap.josm.plugins.PluginDownloadTask;
72import org.openstreetmap.josm.plugins.PluginHandler;
73import org.openstreetmap.josm.plugins.PluginInformation;
74import org.openstreetmap.josm.tools.CheckParameterUtil;
75import org.openstreetmap.josm.tools.GBC;
76import org.openstreetmap.josm.tools.ImageProvider;
77import org.openstreetmap.josm.tools.Logging;
78import org.openstreetmap.josm.tools.Pair;
79import org.openstreetmap.josm.tools.Utils;
80import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
81
82/**
83 * The preference settings.
84 *
85 * @author imi
86 */
87@SuppressWarnings("deprecation")
88public final class PreferenceTabbedPane extends JTabbedPane implements ExpertModeChangeListener, ChangeListener {
89
90 private final class PluginDownloadAfterTask implements Runnable {
91 private final PluginPreference preference;
92 private final PluginDownloadTask task;
93 private final Set<PluginInformation> toDownload;
94
95 private PluginDownloadAfterTask(PluginPreference preference, PluginDownloadTask task,
96 Set<PluginInformation> toDownload) {
97 this.preference = preference;
98 this.task = task;
99 this.toDownload = toDownload;
100 }
101
102 @Override
103 public void run() {
104 boolean requiresRestart = false;
105
106 for (PreferenceSetting setting : settingsInitialized) {
107 if (setting.ok()) {
108 requiresRestart = true;
109 }
110 }
111
112 // build the messages. We only display one message, including the status information from the plugin download task
113 // and - if necessary - a hint to restart JOSM
114 //
115 StringBuilder sb = new StringBuilder();
116 sb.append("<html>");
117 if (task != null && !task.isCanceled()) {
118 PluginHandler.refreshLocalUpdatedPluginInfo(task.getDownloadedPlugins());
119 sb.append(PluginPreference.buildDownloadSummary(task));
120 }
121 if (requiresRestart) {
122 sb.append(tr("You have to restart JOSM for some settings to take effect."));
123 sb.append("<br/><br/>");
124 sb.append(tr("Would you like to restart now?"));
125 }
126 sb.append("</html>");
127
128 // display the message, if necessary
129 //
130 if (requiresRestart) {
131 final ButtonSpec[] options = RestartAction.getButtonSpecs();
132 if (0 == HelpAwareOptionPane.showOptionDialog(
133 MainApplication.getMainFrame(),
134 sb.toString(),
135 tr("Restart"),
136 JOptionPane.INFORMATION_MESSAGE,
137 null, /* no special icon */
138 options,
139 options[0],
140 null /* no special help */
141 )) {
142 MainApplication.getMenu().restart.actionPerformed(null);
143 }
144 } else if (task != null && !task.isCanceled()) {
145 JOptionPane.showMessageDialog(
146 MainApplication.getMainFrame(),
147 sb.toString(),
148 tr("Warning"),
149 JOptionPane.WARNING_MESSAGE
150 );
151 }
152
153 // load the plugins that can be loaded at runtime
154 List<PluginInformation> newPlugins = preference.getNewlyActivatedPlugins();
155 if (newPlugins != null) {
156 Collection<PluginInformation> downloadedPlugins = null;
157 if (task != null && !task.isCanceled()) {
158 downloadedPlugins = task.getDownloadedPlugins();
159 }
160 List<PluginInformation> toLoad = new ArrayList<>();
161 for (PluginInformation pi : newPlugins) {
162 if (toDownload.contains(pi) && downloadedPlugins != null && !downloadedPlugins.contains(pi)) {
163 continue; // failed download
164 }
165 if (pi.canloadatruntime) {
166 toLoad.add(pi);
167 }
168 }
169 // check if plugin dependencies can also be loaded
170 Collection<PluginInformation> allPlugins = new HashSet<>(toLoad);
171 allPlugins.addAll(PluginHandler.getPlugins());
172 boolean removed;
173 do {
174 removed = false;
175 Iterator<PluginInformation> it = toLoad.iterator();
176 while (it.hasNext()) {
177 if (!PluginHandler.checkRequiredPluginsPreconditions(null, allPlugins, it.next(), requiresRestart)) {
178 it.remove();
179 removed = true;
180 }
181 }
182 } while (removed);
183
184 if (!toLoad.isEmpty()) {
185 PluginHandler.loadPlugins(PreferenceTabbedPane.this, toLoad, null);
186 }
187 }
188
189 if (MainApplication.getMainFrame() != null) {
190 MainApplication.getMainFrame().repaint();
191 }
192 }
193 }
194
195 /**
196 * Allows PreferenceSettings to do validation of entered values when ok was pressed.
197 * If data is invalid then event can return false to cancel closing of preferences dialog.
198 * @since 10600 (functional interface)
199 */
200 @FunctionalInterface
201 public interface ValidationListener {
202 /**
203 * Determines if preferences can be saved.
204 * @return True if preferences can be saved
205 */
206 boolean validatePreferences();
207 }
208
209 private interface PreferenceTab {
210 TabPreferenceSetting getTabPreferenceSetting();
211
212 Component getComponent();
213 }
214
215 public static final class PreferencePanel extends JPanel implements PreferenceTab {
216 private final transient TabPreferenceSetting preferenceSetting;
217
218 private PreferencePanel(TabPreferenceSetting preferenceSetting) {
219 super(new GridBagLayout());
220 CheckParameterUtil.ensureParameterNotNull(preferenceSetting);
221 this.preferenceSetting = preferenceSetting;
222 buildPanel();
223 }
224
225 private void buildPanel() {
226 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
227 JPanel headerPanel = new JPanel(new BorderLayout());
228 add(headerPanel, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
229
230 JLabel label = new JLabel("<html>" +
231 "<b>" + preferenceSetting.getTitle() + "</b><br>" +
232 "<i>" + preferenceSetting.getDescription() + "</i></html>");
233 label.setFont(label.getFont().deriveFont(Font.PLAIN));
234 headerPanel.add(label, BorderLayout.CENTER);
235
236 ImageIcon icon = preferenceSetting.getIcon(ImageProvider.ImageSizes.SETTINGS_TAB);
237 headerPanel.add(new JLabel(icon), BorderLayout.EAST);
238 }
239
240 @Override
241 public TabPreferenceSetting getTabPreferenceSetting() {
242 return preferenceSetting;
243 }
244
245 @Override
246 public Component getComponent() {
247 return this;
248 }
249 }
250
251 public static final class PreferenceScrollPane extends JScrollPane implements PreferenceTab {
252 private final transient TabPreferenceSetting preferenceSetting;
253
254 private PreferenceScrollPane(Component view, TabPreferenceSetting preferenceSetting) {
255 super(view);
256 this.preferenceSetting = preferenceSetting;
257 }
258
259 private PreferenceScrollPane(PreferencePanel preferencePanel) {
260 this(preferencePanel.getComponent(), preferencePanel.getTabPreferenceSetting());
261 GuiHelper.setDefaultIncrement(this);
262 }
263
264 @Override
265 public TabPreferenceSetting getTabPreferenceSetting() {
266 return preferenceSetting;
267 }
268
269 @Override
270 public Component getComponent() {
271 return this;
272 }
273 }
274
275 // all created tabs
276 private final transient List<PreferenceTab> tabs = new ArrayList<>();
277 private static final Collection<PreferenceSettingFactory> SETTINGS_FACTORIES = new LinkedList<>();
278 private static final PreferenceSettingFactory ADVANCED_PREFERENCE_FACTORY = new AdvancedPreference.Factory();
279 private final transient List<PreferenceSetting> settings = new ArrayList<>();
280
281 // distinct list of tabs that have been initialized (we do not initialize tabs until they are displayed to speed up dialog startup)
282 private final transient List<PreferenceSetting> settingsInitialized = new ArrayList<>();
283
284 final transient List<ValidationListener> validationListeners = new ArrayList<>();
285
286 /**
287 * Add validation listener to currently open preferences dialog. Calling to removeValidationListener is not necessary, all listeners will
288 * be automatically removed when dialog is closed
289 * @param validationListener validation listener to add
290 */
291 public void addValidationListener(ValidationListener validationListener) {
292 validationListeners.add(validationListener);
293 }
294
295 /**
296 * Construct a PreferencePanel for the preference settings. Layout is GridBagLayout
297 * and a centered title label and the description are added.
298 * @param caller Preference settings, that display a top level tab
299 * @return The created panel ready to add other controls.
300 */
301 public PreferencePanel createPreferenceTab(TabPreferenceSetting caller) {
302 return createPreferenceTab(caller, false);
303 }
304
305 /**
306 * Construct a PreferencePanel for the preference settings. Layout is GridBagLayout
307 * and a centered title label and the description are added.
308 * @param caller Preference settings, that display a top level tab
309 * @param inScrollPane if <code>true</code> the added tab will show scroll bars
310 * if the panel content is larger than the available space
311 * @return The created panel ready to add other controls.
312 */
313 public PreferencePanel createPreferenceTab(TabPreferenceSetting caller, boolean inScrollPane) {
314 CheckParameterUtil.ensureParameterNotNull(caller, "caller");
315 PreferencePanel p = new PreferencePanel(caller);
316
317 PreferenceTab tab = p;
318 if (inScrollPane) {
319 PreferenceScrollPane sp = new PreferenceScrollPane(p);
320 tab = sp;
321 }
322 tabs.add(tab);
323 return p;
324 }
325
326 private OptionalInt indexOfTab(Predicate<TabPreferenceSetting> predicate) {
327 return IntStream.range(0, getTabCount())
328 .filter(i -> getComponentAt(i) instanceof PreferenceTab
329 && predicate.test(((PreferenceTab) getComponentAt(i)).getTabPreferenceSetting()))
330 .findFirst();
331 }
332
333 private void selectTabBy(Predicate<TabPreferenceSetting> predicate) {
334 setSelectedIndex(indexOfTab(predicate).orElse(0));
335 }
336
337 /**
338 * Selects a {@link TabPreferenceSetting} by its icon name
339 * @param name the icon name
340 */
341 public void selectTabByName(String name) {
342 Objects.requireNonNull(name);
343 selectTabBy(tps -> Objects.equals(name, tps.getIconName()));
344 }
345
346 /**
347 * Selects a {@link TabPreferenceSetting} by class
348 * @param clazz preferences tab class
349 */
350 public void selectTabByPref(Class<? extends TabPreferenceSetting> clazz) {
351 selectTabBy(clazz::isInstance);
352 }
353
354 /**
355 * Selects a {@link SubPreferenceSetting} by class
356 * @param clazz sub preferences tab class
357 * @return true if the specified preference settings have been selected, false otherwise.
358 */
359 public boolean selectSubTabByPref(Class<? extends SubPreferenceSetting> clazz) {
360 try {
361 final SubPreferenceSetting sub = getSetting(clazz);
362 final TabPreferenceSetting tab = sub.getTabPreferenceSetting(this);
363 selectTabBy(tps -> tps.equals(tab));
364 return tab.selectSubTab(sub);
365 } catch (NoSuchElementException ignore) {
366 Logging.trace(ignore);
367 return false;
368 }
369 }
370
371 /**
372 * Returns the currently selected preference and sub preference setting
373 * @return the currently selected preference and sub preference setting
374 */
375 public Pair<Class<? extends TabPreferenceSetting>, Class<? extends SubPreferenceSetting>> getSelectedTab() {
376 final Component selected = getSelectedComponent();
377 if (selected instanceof PreferenceTab) {
378 final TabPreferenceSetting setting = ((PreferenceTab) selected).getTabPreferenceSetting();
379 return Pair.create(setting.getClass(), setting.getSelectedSubTab());
380 } else {
381 return null;
382 }
383 }
384
385 /**
386 * Returns the {@code DisplayPreference} object.
387 * @return the {@code DisplayPreference} object.
388 */
389 public DisplayPreference getDisplayPreference() {
390 return getSetting(DisplayPreference.class);
391 }
392
393 /**
394 * Returns the {@code MapPreference} object.
395 * @return the {@code MapPreference} object.
396 * @deprecated to remove beginning of 2021
397 */
398 @Deprecated
399 public MapPreference getMapPreference() {
400 return getSetting(MapPreference.class);
401 }
402
403 /**
404 * Returns the {@code PluginPreference} object.
405 * @return the {@code PluginPreference} object.
406 */
407 public PluginPreference getPluginPreference() {
408 return getSetting(PluginPreference.class);
409 }
410
411 /**
412 * Returns the {@code ImageryPreference} object.
413 * @return the {@code ImageryPreference} object.
414 */
415 public ImageryPreference getImageryPreference() {
416 return getSetting(ImageryPreference.class);
417 }
418
419 /**
420 * Returns the {@code ShortcutPreference} object.
421 * @return the {@code ShortcutPreference} object.
422 */
423 public ShortcutPreference getShortcutPreference() {
424 return getSetting(ShortcutPreference.class);
425 }
426
427 /**
428 * Returns the {@code ServerAccessPreference} object.
429 * @return the {@code ServerAccessPreference} object.
430 * @since 6523
431 */
432 public ServerAccessPreference getServerPreference() {
433 return getSetting(ServerAccessPreference.class);
434 }
435
436 /**
437 * Returns the {@code ValidatorPreference} object.
438 * @return the {@code ValidatorPreference} object.
439 * @since 6665
440 */
441 public ValidatorPreference getValidatorPreference() {
442 return getSetting(ValidatorPreference.class);
443 }
444
445 /**
446 * Saves preferences.
447 */
448 public void savePreferences() {
449 // create a task for downloading plugins if the user has activated, yet not downloaded, new plugins
450 final PluginPreference preference = getPluginPreference();
451 if (preference != null) {
452 final Set<PluginInformation> toDownload = preference.getPluginsScheduledForUpdateOrDownload();
453 final PluginDownloadTask task;
454 if (toDownload != null && !toDownload.isEmpty()) {
455 task = new PluginDownloadTask(this, toDownload, tr("Download plugins"));
456 } else {
457 task = null;
458 }
459
460 // this is the task which will run *after* the plugins are downloaded
461 final Runnable continuation = new PluginDownloadAfterTask(preference, task, toDownload);
462
463 if (task != null) {
464 // if we have to launch a plugin download task we do it asynchronously, followed
465 // by the remaining "save preferences" activities run on the Swing EDT.
466 MainApplication.worker.submit(task);
467 MainApplication.worker.submit(() -> GuiHelper.runInEDT(continuation));
468 } else {
469 // no need for asynchronous activities. Simply run the remaining "save preference"
470 // activities on this thread (we are already on the Swing EDT
471 continuation.run();
472 }
473 }
474 }
475
476 /**
477 * If the dialog is closed with Ok, the preferences will be stored to the preferences-
478 * file, otherwise no change of the file happens.
479 */
480 public PreferenceTabbedPane() {
481 super(SwingConstants.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT);
482 super.addMouseWheelListener(new WheelListener(this));
483 ExpertToggleAction.addExpertModeChangeListener(this);
484 }
485
486 /**
487 * Constructs GUI.
488 */
489 public void buildGui() {
490 Collection<PreferenceSettingFactory> factories = new ArrayList<>(SETTINGS_FACTORIES);
491 factories.addAll(PluginHandler.getPreferenceSetting());
492 factories.add(ADVANCED_PREFERENCE_FACTORY);
493
494 for (PreferenceSettingFactory factory : factories) {
495 if (factory != null) {
496 PreferenceSetting setting = factory.createPreferenceSetting();
497 if (setting != null) {
498 settings.add(setting);
499 }
500 }
501 }
502 addGUITabs(false);
503 super.getModel().addChangeListener(this);
504 }
505
506 private void addGUITabsForSetting(Icon icon, TabPreferenceSetting tps, int maxWidth) {
507 for (PreferenceTab tab : tabs) {
508 if (tab.getTabPreferenceSetting().equals(tps)) {
509 insertGUITabsForSetting(icon, tps, tab.getComponent(), getTabCount(), maxWidth);
510 }
511 }
512 }
513
514 private int insertGUITabsForSetting(Icon icon, TabPreferenceSetting tps, int index, int maxWidth) {
515 int position = index;
516 for (PreferenceTab tab : tabs) {
517 if (tab.getTabPreferenceSetting().equals(tps)) {
518 insertGUITabsForSetting(icon, tps, tab.getComponent(), position, maxWidth);
519 position++;
520 }
521 }
522 return position - 1;
523 }
524
525 private void insertGUITabsForSetting(Icon icon, TabPreferenceSetting tps, final Component component, int position, int maxWidth) {
526 // macOS / AquaLookAndFeel does not support horizontal tabs, see https://josm.openstreetmap.de/ticket/7548#comment:80
527 String title = "Aqua".equals(UIManager.getLookAndFeel().getID()) ? null : htmlTabTitle(tps.getTitle(), maxWidth);
528 insertTab(title, icon, component, tps.getTooltip(), position);
529 }
530
531 private String htmlTabTitle(String title, int maxWidth) {
532 // Width is set to force left alignment, see https://stackoverflow.com/a/33781096/2257172
533 return "<html><div style='padding-left:5px; width:" + maxWidth + "px'>" + title + "</div></html>";
534 }
535
536 private void addGUITabs(boolean clear) {
537 boolean expert = ExpertToggleAction.isExpert();
538 if (clear) {
539 removeAll();
540 }
541 // Compute max tab length in pixels
542 int maxWidth = computeMaxTabWidth();
543 // Inspect each tab setting
544 for (PreferenceSetting setting : settings) {
545 if (setting instanceof TabPreferenceSetting) {
546 TabPreferenceSetting tps = (TabPreferenceSetting) setting;
547 if (expert || !tps.isExpert()) {
548 ImageIcon icon = tps.getIcon(ImageProvider.ImageSizes.LARGEICON);
549 if (settingsInitialized.contains(tps)) {
550 // If it has been initialized, add corresponding tab(s)
551 addGUITabsForSetting(icon, tps, maxWidth);
552 } else {
553 // If it has not been initialized, create an empty tab with only icon and tooltip
554 insertGUITabsForSetting(icon, tps, new PreferencePanel(tps), getTabCount(), maxWidth);
555 }
556 }
557 } else if (!(setting instanceof SubPreferenceSetting)) {
558 Logging.warn("Ignoring preferences "+setting);
559 }
560 }
561 // Hide empty TabPreferenceSetting (only present for plugins)
562 for (DefaultTabPreferenceSetting tps : Utils.filteredCollection(settings, DefaultTabPreferenceSetting.class)) {
563 if (!tps.canBeHidden() || Utils.filteredCollection(settings, SubPreferenceSetting.class).stream()
564 .anyMatch(s -> s.getTabPreferenceSetting(this) == tps)) {
565 continue;
566 }
567 indexOfTab(tps::equals).ifPresent(index -> {
568 remove(index);
569 Logging.debug("{0}: hiding empty {1}", getClass().getSimpleName(), tps);
570 });
571 }
572 setSelectedIndex(-1);
573 }
574
575 private int computeMaxTabWidth() {
576 FontMetrics fm = getFontMetrics(getFont());
577 return settings.stream().filter(x -> x instanceof TabPreferenceSetting).map(x -> ((TabPreferenceSetting) x).getTitle())
578 .filter(Objects::nonNull).mapToInt(fm::stringWidth).max().orElse(120);
579 }
580
581 @Override
582 public void expertChanged(boolean isExpert) {
583 Component sel = getSelectedComponent();
584 addGUITabs(true);
585 int index = -1;
586 if (sel != null) {
587 index = indexOfComponent(sel);
588 }
589 setSelectedIndex(Math.max(index, 0));
590 }
591
592 /**
593 * Returns a list of all preferences settings
594 * @return a list of all preferences settings
595 */
596 public List<PreferenceSetting> getSettings() {
597 return Collections.unmodifiableList(settings);
598 }
599
600 /**
601 * Returns the preferences setting for the given class
602 * @param clazz the preference setting class
603 * @param <T> the preference setting type
604 * @return the preferences setting for the given class
605 * @throws NoSuchElementException if there is no such value
606 */
607 public <T extends PreferenceSetting> T getSetting(Class<? extends T> clazz) {
608 return Utils.filteredCollection(settings, clazz).iterator().next();
609 }
610
611 static {
612 // order is important!
613 SETTINGS_FACTORIES.add(new DisplayPreference.Factory());
614 SETTINGS_FACTORIES.add(new DrawingPreference.Factory());
615 SETTINGS_FACTORIES.add(new GPXPreference.Factory());
616 SETTINGS_FACTORIES.add(new ColorPreference.Factory());
617 SETTINGS_FACTORIES.add(new LafPreference.Factory());
618 SETTINGS_FACTORIES.add(new LanguagePreference.Factory());
619
620 SETTINGS_FACTORIES.add(new ServerAccessPreference.Factory());
621 SETTINGS_FACTORIES.add(new ProxyPreference.Factory());
622 SETTINGS_FACTORIES.add(new MapPreference.Factory());
623 SETTINGS_FACTORIES.add(new ProjectionPreference.Factory());
624 SETTINGS_FACTORIES.add(new MapPaintPreference.Factory());
625 SETTINGS_FACTORIES.add(new TaggingPresetPreference.Factory());
626 SETTINGS_FACTORIES.add(new BackupPreference.Factory());
627 SETTINGS_FACTORIES.add(new PluginPreference.Factory());
628 SETTINGS_FACTORIES.add(MainApplication.getToolbar());
629 SETTINGS_FACTORIES.add(new AudioPreference.Factory());
630 SETTINGS_FACTORIES.add(new ShortcutPreference.Factory());
631 SETTINGS_FACTORIES.add(new ValidatorPreference.Factory());
632 SETTINGS_FACTORIES.add(new ValidatorTestsPreference.Factory());
633 SETTINGS_FACTORIES.add(new ValidatorTagCheckerRulesPreference.Factory());
634 SETTINGS_FACTORIES.add(new RemoteControlPreference.Factory());
635 SETTINGS_FACTORIES.add(new ImageryPreference.Factory());
636 }
637
638 /**
639 * This mouse wheel listener reacts when a scroll is carried out over the
640 * tab strip and scrolls one tab/down or up, selecting it immediately.
641 */
642 static final class WheelListener implements MouseWheelListener {
643
644 final JTabbedPane tabbedPane;
645
646 WheelListener(JTabbedPane tabbedPane) {
647 this.tabbedPane = tabbedPane;
648 }
649
650 @Override
651 public void mouseWheelMoved(MouseWheelEvent wev) {
652 // Ensure the cursor is over the tab strip
653 if (tabbedPane.indexAtLocation(wev.getPoint().x, wev.getPoint().y) < 0)
654 return;
655
656 // Get currently selected tab && ensure the new tab index is sound
657 int newTab = Utils.clamp(tabbedPane.getSelectedIndex() + wev.getWheelRotation(),
658 0, tabbedPane.getTabCount() - 1);
659
660 tabbedPane.setSelectedIndex(newTab);
661 }
662 }
663
664 @Override
665 public void stateChanged(ChangeEvent e) {
666 int index = getSelectedIndex();
667 Component sel = getSelectedComponent();
668 if (index > -1 && sel instanceof PreferenceTab) {
669 PreferenceTab tab = (PreferenceTab) sel;
670 TabPreferenceSetting preferenceSettings = tab.getTabPreferenceSetting();
671 if (!settingsInitialized.contains(preferenceSettings)) {
672 try {
673 getModel().removeChangeListener(this);
674 preferenceSettings.addGui(this);
675 // Add GUI for sub preferences
676 for (PreferenceSetting setting : settings) {
677 if (setting instanceof SubPreferenceSetting) {
678 addSubPreferenceSetting(preferenceSettings, (SubPreferenceSetting) setting);
679 }
680 }
681 Icon icon = getIconAt(index);
682 remove(index);
683 if (index <= insertGUITabsForSetting(icon, preferenceSettings, index, computeMaxTabWidth())) {
684 setSelectedIndex(index);
685 }
686 } catch (SecurityException ex) {
687 Logging.error(ex);
688 } catch (RuntimeException ex) { // NOPMD
689 // allow to change most settings even if e.g. a plugin fails
690 BugReportExceptionHandler.handleException(ex);
691 } finally {
692 settingsInitialized.add(preferenceSettings);
693 getModel().addChangeListener(this);
694 }
695 }
696 Container ancestor = getTopLevelAncestor();
697 if (ancestor instanceof PreferenceDialog) {
698 ((PreferenceDialog) ancestor).setHelpContext(preferenceSettings.getHelpContext());
699 }
700 }
701 }
702
703 private void addSubPreferenceSetting(TabPreferenceSetting preferenceSettings, SubPreferenceSetting sps) {
704 if (sps.getTabPreferenceSetting(this) == preferenceSettings) {
705 try {
706 sps.addGui(this);
707 } catch (SecurityException ex) {
708 Logging.error(ex);
709 } catch (RuntimeException ex) { // NOPMD
710 BugReportExceptionHandler.handleException(ex);
711 } finally {
712 settingsInitialized.add(sps);
713 }
714 }
715 }
716}
Note: See TracBrowser for help on using the repository browser.