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