001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.event.ActionEvent; 008 009import org.openstreetmap.josm.data.preferences.BooleanProperty; 010import org.openstreetmap.josm.gui.MainApplication; 011import org.openstreetmap.josm.tools.ListenerList; 012 013/** 014 * This action toggles the Expert mode. 015 * @since 4840 016 */ 017public class ExpertToggleAction extends ToggleAction { 018 019 /** 020 * This listener is notified whenever the expert mode setting changed. 021 */ 022 @FunctionalInterface 023 public interface ExpertModeChangeListener { 024 /** 025 * The expert mode changed. 026 * @param isExpert <code>true</code> if expert mode was enabled, false otherwise. 027 */ 028 void expertChanged(boolean isExpert); 029 } 030 031 // TODO: Switch to checked list. We can do this as soon as we do not see any more warnings. 032 private static final ListenerList<ExpertModeChangeListener> listeners = ListenerList.createUnchecked(); 033 private static final ListenerList<Component> visibilityToggleListeners = ListenerList.createUnchecked(); 034 035 private static final BooleanProperty PREF_EXPERT = new BooleanProperty("expert", false); 036 037 private static final ExpertToggleAction INSTANCE = new ExpertToggleAction(); 038 039 private static synchronized void fireExpertModeChanged(boolean isExpert) { 040 listeners.fireEvent(listener -> listener.expertChanged(isExpert)); 041 visibilityToggleListeners.fireEvent(c -> c.setVisible(isExpert)); 042 } 043 044 /** 045 * Register a expert mode change listener. 046 * 047 * @param listener the listener. Ignored if null. 048 */ 049 public static void addExpertModeChangeListener(ExpertModeChangeListener listener) { 050 addExpertModeChangeListener(listener, false); 051 } 052 053 /** 054 * Register a expert mode change listener, and optionnally fires it. 055 * @param listener the listener. Ignored if null. 056 * @param fireWhenAdding if true, the listener will be fired immediately after added 057 */ 058 public static synchronized void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) { 059 if (listener == null) return; 060 listeners.addWeakListener(listener); 061 if (fireWhenAdding) { 062 listener.expertChanged(isExpert()); 063 } 064 } 065 066 /** 067 * Removes a expert mode change listener 068 * 069 * @param listener the listener. Ignored if null. 070 */ 071 public static synchronized void removeExpertModeChangeListener(ExpertModeChangeListener listener) { 072 if (listener == null) return; 073 listeners.removeListener(listener); 074 } 075 076 /** 077 * Marks a component to be only visible when expert mode is enabled. The visibility of the component is changed automatically. 078 * @param c The component. 079 */ 080 public static synchronized void addVisibilitySwitcher(Component c) { 081 if (c == null) return; 082 visibilityToggleListeners.addWeakListener(c); 083 c.setVisible(isExpert()); 084 } 085 086 /** 087 * Stops tracking visibility changes for the given component. 088 * @param c The component. 089 * @see #addVisibilitySwitcher(Component) 090 */ 091 public static synchronized void removeVisibilitySwitcher(Component c) { 092 if (c == null) return; 093 visibilityToggleListeners.removeListener(c); 094 } 095 096 /** 097 * Constructs a new {@code ExpertToggleAction}. 098 */ 099 public ExpertToggleAction() { 100 super(tr("Expert Mode"), 101 "expert", 102 tr("Enable/disable expert mode"), 103 null, 104 false /* register toolbar */ 105 ); 106 putValue("toolbar", "expertmode"); 107 if (MainApplication.getToolbar() != null) { 108 MainApplication.getToolbar().register(this); 109 } 110 setSelected(PREF_EXPERT.get()); 111 notifySelectedState(); 112 } 113 114 @Override 115 protected final void notifySelectedState() { 116 super.notifySelectedState(); 117 PREF_EXPERT.put(isSelected()); 118 fireExpertModeChanged(isSelected()); 119 } 120 121 /** 122 * Forces the expert mode state to the given state. 123 * @param isExpert if expert mode should be used. 124 * @since 11224 125 */ 126 public void setExpert(boolean isExpert) { 127 if (isSelected() != isExpert) { 128 setSelected(isExpert); 129 notifySelectedState(); 130 } 131 } 132 133 @Override 134 public void actionPerformed(ActionEvent e) { 135 toggleSelectedState(e); 136 notifySelectedState(); 137 } 138 139 /** 140 * Replies the unique instance of this action. 141 * @return The unique instance of this action 142 */ 143 public static ExpertToggleAction getInstance() { 144 return INSTANCE; 145 } 146 147 /** 148 * Determines if expert mode is enabled. 149 * @return {@code true} if expert mode is enabled, {@code false} otherwise. 150 */ 151 public static boolean isExpert() { 152 return INSTANCE.isSelected(); 153 } 154}