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) {
082            if (getAction() != null) {
083                s = getAction().getClass().getName();
084            }
085        }
086        return "sidetoolbar.hidden."+s;
087
088    }
089
090    @Override
091    public void expertChanged(boolean isExpert) {
092        applyButtonHiddenPreferences();
093    }
094
095    @Override
096    public void applyButtonHiddenPreferences() {
097        boolean alwaysHideDisabled = Main.pref.getBoolean("sidetoolbar.hideDisabledButtons", false);
098        if (!isEnabled() && (hideIfDisabled || alwaysHideDisabled)) {
099            setVisible(false);  // hide because of disabled button
100        } else {
101            boolean hiddenFlag = false;
102            String hiddenFlagStr = Main.pref.get(getPreferenceKey(), null);
103            if (hiddenFlagStr == null) {
104                if (isExpert && !ExpertToggleAction.isExpert()) {
105                    hiddenFlag = true;
106                }
107            } else {
108                hiddenFlag = Boolean.parseBoolean(hiddenFlagStr);
109            }
110            setVisible(!hiddenFlag); // show or hide, do what preferences say
111        }
112    }
113
114    @Override
115    public void setButtonHidden(boolean b) {
116        setVisible(!b);
117        if (listener != null) { // if someone wants to know about changes of visibility
118            if (!b) listener.buttonShown(); else listener.buttonHidden();
119        }
120        if ((b && isExpert && !ExpertToggleAction.isExpert()) ||
121            (!b && isExpert && ExpertToggleAction.isExpert())) {
122            Main.pref.put(getPreferenceKey(), null);
123        } else {
124            Main.pref.put(getPreferenceKey(), b);
125        }
126    }
127
128    /**
129     * This function should be called for plugins that want to enable auto-hiding
130     * custom buttons when they are disabled (because of incorrect layer, for example)
131     * @param b hide if disabled
132     */
133    public void setAutoHideDisabledButton(boolean b) {
134        hideIfDisabled = b;
135        if (b && !isEnabled()) {
136            setVisible(false);
137        }
138    }
139
140    @Override
141    public void showButton() {
142        setButtonHidden(false);
143    }
144
145    @Override
146    public void hideButton() {
147        setButtonHidden(true);
148    }
149
150    @Override
151    public String getActionName() {
152        return (String) getSafeActionValue(Action.NAME);
153    }
154
155    @Override
156    public Icon getIcon() {
157        Object o = getSafeActionValue(Action.LARGE_ICON_KEY);
158        if (o == null)
159            o = getSafeActionValue(Action.SMALL_ICON);
160        return (Icon) o;
161    }
162
163    @Override
164    public boolean isButtonVisible() {
165        return isVisible();
166    }
167
168    @Override
169    public void setShowHideButtonListener(ShowHideButtonListener l) {
170        listener = l;
171    }
172
173    protected final Object getSafeActionValue(String key) {
174        // Mac OS X Aqua L&F can call accessors from constructor, so getAction() can be null in those cases
175        return getAction() != null ? getAction().getValue(key) : null;
176    }
177}