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