001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyChangeListener;
006
007import javax.swing.Action;
008import javax.swing.Icon;
009import javax.swing.JToggleButton;
010
011import org.openstreetmap.josm.Main;
012import org.openstreetmap.josm.actions.ExpertToggleAction;
013import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener;
014import org.openstreetmap.josm.tools.CheckParameterUtil;
015import org.openstreetmap.josm.tools.Destroyable;
016
017/**
018 * Just a toggle button, with smaller border and icon only to display in
019 * MapFrame toolbars.
020 * Also provides methods for storing hidden state in preferences
021 * @author imi, akks
022 */
023public class IconToggleButton extends JToggleButton implements HideableButton, PropertyChangeListener, Destroyable, ExpertModeChangeListener {
024
025    private transient ShowHideButtonListener listener;
026    private boolean hideIfDisabled;
027    private boolean isExpert;
028
029    /**
030     * Construct the toggle button with the given action.
031     * @param action associated action
032     */
033    public IconToggleButton(Action action) {
034        this(action, false);
035    }
036
037    /**
038     * Construct the toggle button with the given action.
039     * @param action associated action
040     * @param isExpert {@code true} if it's reserved to expert mode
041     */
042    public IconToggleButton(Action action, boolean isExpert) {
043        super(action);
044        CheckParameterUtil.ensureParameterNotNull(action, "action");
045        this.isExpert = isExpert;
046        setText(null);
047
048        Object o = action.getValue(Action.SHORT_DESCRIPTION);
049        if (o != null) {
050            setToolTipText(o.toString());
051        }
052
053        action.addPropertyChangeListener(this);
054
055        ExpertToggleAction.addExpertModeChangeListener(this);
056    }
057
058    @Override
059    public void propertyChange(PropertyChangeEvent evt) {
060        if ("active".equals(evt.getPropertyName())) {
061            setSelected((Boolean) evt.getNewValue());
062            requestFocusInWindow();
063        } else if ("selected".equals(evt.getPropertyName())) {
064            setSelected((Boolean) evt.getNewValue());
065        }
066    }
067
068    @Override
069    public void destroy() {
070        Action action = getAction();
071        if (action instanceof Destroyable) {
072            ((Destroyable) action).destroy();
073        }
074        if (action != null) {
075            action.removePropertyChangeListener(this);
076        }
077    }
078
079    String getPreferenceKey() {
080        String s = (String) getSafeActionValue("toolbar");
081        if (s == null && getAction() != null) {
082            s = getAction().getClass().getName();
083        }
084        return "sidetoolbar.hidden."+s;
085    }
086
087    @Override
088    public void expertChanged(boolean isExpert) {
089        applyButtonHiddenPreferences();
090    }
091
092    @Override
093    public void applyButtonHiddenPreferences() {
094        boolean alwaysHideDisabled = Main.pref.getBoolean("sidetoolbar.hideDisabledButtons", false);
095        if (!isEnabled() && (hideIfDisabled || alwaysHideDisabled)) {
096            setVisible(false);  // hide because of disabled button
097        } else {
098            boolean hiddenFlag = false;
099            String hiddenFlagStr = Main.pref.get(getPreferenceKey(), null);
100            if (hiddenFlagStr == null) {
101                if (isExpert && !ExpertToggleAction.isExpert()) {
102                    hiddenFlag = true;
103                }
104            } else {
105                hiddenFlag = Boolean.parseBoolean(hiddenFlagStr);
106            }
107            setVisible(!hiddenFlag); // show or hide, do what preferences say
108        }
109    }
110
111    @Override
112    public void setButtonHidden(boolean b) {
113        setVisible(!b);
114        if (listener != null) { // if someone wants to know about changes of visibility
115            if (!b) listener.buttonShown(); else listener.buttonHidden();
116        }
117        if ((b && isExpert && !ExpertToggleAction.isExpert()) ||
118            (!b && isExpert && ExpertToggleAction.isExpert())) {
119            Main.pref.put(getPreferenceKey(), null);
120        } else {
121            Main.pref.put(getPreferenceKey(), b);
122        }
123    }
124
125    /**
126     * This function should be called for plugins that want to enable auto-hiding
127     * custom buttons when they are disabled (because of incorrect layer, for example)
128     * @param b hide if disabled
129     */
130    public void setAutoHideDisabledButton(boolean b) {
131        hideIfDisabled = b;
132        if (b && !isEnabled()) {
133            setVisible(false);
134        }
135    }
136
137    @Override
138    public void showButton() {
139        setButtonHidden(false);
140    }
141
142    @Override
143    public void hideButton() {
144        setButtonHidden(true);
145    }
146
147    @Override
148    public String getActionName() {
149        return (String) getSafeActionValue(Action.NAME);
150    }
151
152    @Override
153    public Icon getIcon() {
154        Object o = getSafeActionValue(Action.LARGE_ICON_KEY);
155        if (o == null)
156            o = getSafeActionValue(Action.SMALL_ICON);
157        return (Icon) o;
158    }
159
160    @Override
161    public boolean isButtonVisible() {
162        return isVisible();
163    }
164
165    @Override
166    public void setShowHideButtonListener(ShowHideButtonListener l) {
167        listener = l;
168    }
169
170    protected final Object getSafeActionValue(String key) {
171        // Mac OS X Aqua L&F can call accessors from constructor, so getAction() can be null in those cases
172        return getAction() != null ? getAction().getValue(key) : null;
173    }
174}