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

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

fix #20099 - make sure "OSM Server" preferences display a vertical scrollbar if needed

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