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;
008import java.lang.ref.WeakReference;
009import java.util.ArrayList;
010import java.util.Iterator;
011import java.util.List;
012
013import org.openstreetmap.josm.Main;
014
015/**
016 * This action toggles the Expert mode.
017 * @since 4840
018 */
019public class ExpertToggleAction extends ToggleAction {
020
021    public interface ExpertModeChangeListener {
022        void expertChanged(boolean isExpert);
023    }
024
025    private static final List<WeakReference<ExpertModeChangeListener>> listeners = new ArrayList<WeakReference<ExpertModeChangeListener>>();
026    private static final List<WeakReference<Component>> visibilityToggleListeners = new ArrayList<WeakReference<Component>>();
027
028    private static ExpertToggleAction INSTANCE = new ExpertToggleAction();
029
030    private synchronized static void fireExpertModeChanged(boolean isExpert) {
031        {
032            Iterator<WeakReference<ExpertModeChangeListener>> it = listeners.iterator();
033            while (it.hasNext()) {
034                WeakReference<ExpertModeChangeListener> wr = it.next();
035                ExpertModeChangeListener listener = wr.get();
036                if (listener == null) {
037                    it.remove();
038                    continue;
039                }
040                listener.expertChanged(isExpert);
041            }
042        }
043        {
044            Iterator<WeakReference<Component>> it = visibilityToggleListeners.iterator();
045            while (it.hasNext()) {
046                WeakReference<Component> wr = it.next();
047                Component c = wr.get();
048                if (c == null) {
049                    it.remove();
050                    continue;
051                }
052                c.setVisible(isExpert);
053            }
054        }
055    }
056
057    /**
058     * Register a expert mode change listener
059     *
060     * @param listener the listener. Ignored if null.
061     */
062    public static void addExpertModeChangeListener(ExpertModeChangeListener listener) {
063        addExpertModeChangeListener(listener, false);
064    }
065
066    public synchronized static void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) {
067        if (listener == null) return;
068        for (WeakReference<ExpertModeChangeListener> wr : listeners) {
069            // already registered ? => abort
070            if (wr.get() == listener) return;
071        }
072        listeners.add(new WeakReference<ExpertModeChangeListener>(listener));
073        if (fireWhenAdding) {
074            listener.expertChanged(isExpert());
075        }
076    }
077
078    /**
079     * Removes a expert mode change listener
080     *
081     * @param listener the listener. Ignored if null.
082     */
083    public synchronized static void removeExpertModeChangeListener(ExpertModeChangeListener listener) {
084        if (listener == null) return;
085        Iterator<WeakReference<ExpertModeChangeListener>> it = listeners.iterator();
086        while (it.hasNext()) {
087            WeakReference<ExpertModeChangeListener> wr = it.next();
088            // remove the listener - and any other listener which god garbage
089            // collected in the meantime
090            if (wr.get() == null || wr.get() == listener) {
091                it.remove();
092            }
093        }
094    }
095
096    public synchronized static void addVisibilitySwitcher(Component c) {
097        if (c == null) return;
098        for (WeakReference<Component> wr : visibilityToggleListeners) {
099            // already registered ? => abort
100            if (wr.get() == c) return;
101        }
102        visibilityToggleListeners.add(new WeakReference<Component>(c));
103        c.setVisible(isExpert());
104    }
105
106    public synchronized static void removeVisibilitySwitcher(Component c) {
107        if (c == null) return;
108        Iterator<WeakReference<Component>> it = visibilityToggleListeners.iterator();
109        while (it.hasNext()) {
110            WeakReference<Component> wr = it.next();
111            // remove the listener - and any other listener which god garbage
112            // collected in the meantime
113            if (wr.get() == null || wr.get() == c) {
114                it.remove();
115            }
116        }
117    }
118
119    /**
120     * Constructs a new {@code ExpertToggleAction}.
121     */
122    public ExpertToggleAction() {
123        super(tr("Expert Mode"),
124              "expert",
125              tr("Enable/disable expert mode"),
126              null,
127              false /* register toolbar */
128        );
129        putValue("toolbar", "expertmode");
130        Main.toolbar.register(this);
131        setSelected(Main.pref.getBoolean("expert", false));
132        notifySelectedState();
133    }
134
135    protected void notifySelectedState() {
136        super.notifySelectedState();
137        fireExpertModeChanged(isSelected());
138    }
139
140    @Override
141    public void actionPerformed(ActionEvent e) {
142        toggleSelectedState(e);
143        Main.pref.put("expert", isSelected());
144        notifySelectedState();
145    }
146
147    /**
148     * Replies the unique instance of this action.
149     * @return The unique instance of this action
150     */
151    public static ExpertToggleAction getInstance() {
152        return INSTANCE;
153    }
154
155    /**
156     * Determines if expert mode is enabled.
157     * @return {@code true} if expert mode is enabled, {@code false} otherwise.
158     */
159    public static boolean isExpert() {
160        return INSTANCE.isSelected();
161    }
162}