001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.actions;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.Component;
007    import java.awt.event.ActionEvent;
008    import java.lang.ref.WeakReference;
009    import java.util.ArrayList;
010    import java.util.Iterator;
011    import java.util.List;
012    
013    import javax.swing.ButtonModel;
014    
015    import org.openstreetmap.josm.Main;
016    
017    public class ExpertToggleAction extends JosmAction {
018    
019        private final List<ButtonModel> buttonModels = new ArrayList<ButtonModel>();
020        private boolean selected;
021    
022        public interface ExpertModeChangeListener {
023            void expertChanged(boolean isExpert);
024        }
025    
026        private static final List<WeakReference<ExpertModeChangeListener>> listeners = new ArrayList<WeakReference<ExpertModeChangeListener>>();
027        private static final List<WeakReference<Component>> visibilityToggleListeners = new ArrayList<WeakReference<Component>>();
028    
029        private static ExpertToggleAction INSTANCE = new ExpertToggleAction();
030    
031        private synchronized static void fireExpertModeChanged(boolean isExpert) {
032            {
033                Iterator<WeakReference<ExpertModeChangeListener>> it = listeners.iterator();
034                while (it.hasNext()) {
035                    WeakReference<ExpertModeChangeListener> wr = it.next();
036                    ExpertModeChangeListener listener = wr.get();
037                    if (listener == null) {
038                        it.remove();
039                        continue;
040                    }
041                    listener.expertChanged(isExpert);
042                }
043            }
044            {
045                Iterator<WeakReference<Component>> it = visibilityToggleListeners.iterator();
046                while (it.hasNext()) {
047                    WeakReference<Component> wr = it.next();
048                    Component c = wr.get();
049                    if (c == null) {
050                        it.remove();
051                        continue;
052                    }
053                    c.setVisible(isExpert);
054                }
055            }
056        }
057    
058        /**
059         * Register a expert mode change listener
060         *
061         * @param listener the listener. Ignored if null.
062         */
063        public static void addExpertModeChangeListener(ExpertModeChangeListener listener) {
064            addExpertModeChangeListener(listener, false);
065        }
066    
067        public synchronized static void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) {
068            if (listener == null) return;
069            for (WeakReference<ExpertModeChangeListener> wr : listeners) {
070                // already registered ? => abort
071                if (wr.get() == listener) return;
072            }
073            listeners.add(new WeakReference<ExpertModeChangeListener>(listener));
074            if (fireWhenAdding) {
075                listener.expertChanged(isExpert());
076            }
077        }
078    
079        /**
080         * Removes a expert mode change listener
081         *
082         * @param listener the listener. Ignored if null.
083         */
084        public synchronized static void removeExpertModeChangeListener(ExpertModeChangeListener listener) {
085            if (listener == null) return;
086            Iterator<WeakReference<ExpertModeChangeListener>> it = listeners.iterator();
087            while (it.hasNext()) {
088                WeakReference<ExpertModeChangeListener> wr = it.next();
089                // remove the listener - and any other listener which god garbage
090                // collected in the meantime
091                if (wr.get() == null || wr.get() == listener) {
092                    it.remove();
093                }
094            }
095        }
096    
097        public synchronized static void addVisibilitySwitcher(Component c) {
098            if (c == null) return;
099            for (WeakReference<Component> wr : visibilityToggleListeners) {
100                // already registered ? => abort
101                if (wr.get() == c) return;
102            }
103            visibilityToggleListeners.add(new WeakReference<Component>(c));
104            c.setVisible(isExpert());
105        }
106    
107        public synchronized static void removeVisibilitySwitcher(Component c) {
108            if (c == null) return;
109            Iterator<WeakReference<Component>> it = visibilityToggleListeners.iterator();
110            while (it.hasNext()) {
111                WeakReference<Component> wr = it.next();
112                // remove the listener - and any other listener which god garbage
113                // collected in the meantime
114                if (wr.get() == null || wr.get() == c) {
115                    it.remove();
116                }
117            }
118        }
119    
120        public ExpertToggleAction() {
121            super(
122                    tr("Expert Mode"),
123                    "expert",
124                    tr("Enable/disable expert mode"),
125                    null,
126                    false /* register toolbar */
127            );
128            putValue("toolbar", "expertmode");
129            Main.toolbar.register(this);
130            selected = Main.pref.getBoolean("expert", false);
131            notifySelectedState();
132        }
133    
134        public void addButtonModel(ButtonModel model) {
135            if (model != null && !buttonModels.contains(model)) {
136                buttonModels.add(model);
137                model.setSelected(selected);
138            }
139        }
140    
141        public void removeButtonModel(ButtonModel model) {
142            if (model != null && buttonModels.contains(model)) {
143                buttonModels.remove(model);
144            }
145        }
146    
147        protected void notifySelectedState() {
148            for (ButtonModel model: buttonModels) {
149                if (model.isSelected() != selected) {
150                    model.setSelected(selected);
151                }
152            }
153            fireExpertModeChanged(selected);
154        }
155    
156        protected void toggleSelectedState() {
157            selected = !selected;
158            Main.pref.put("expert", selected);
159            notifySelectedState();
160        }
161    
162        public void actionPerformed(ActionEvent e) {
163            toggleSelectedState();
164        }
165    
166        public boolean isSelected() {
167            return selected;
168        }
169    
170        public static ExpertToggleAction getInstance() {
171            return INSTANCE;
172        }
173    
174        public static boolean isExpert() {
175            return INSTANCE.isSelected();
176        }
177    }