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 ShowHideButtonListener listener;
028    private boolean hideIfDisabled = false;
029    private boolean isExpert;
030
031    /**
032     * Construct the toggle button with the given action.
033     */
034    public IconToggleButton(Action action) {
035        this(action, false);
036    }
037
038    /**
039     * Construct the toggle button with the given action.
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        addMouseListener(new MouseAdapter(){
054            @Override public void mousePressed(MouseEvent e) {
055                groupbutton = e.getX() > getWidth()/2 && e.getY() > getHeight()/2;
056            }
057        });
058
059        ExpertToggleAction.addExpertModeChangeListener(this);
060    }
061
062    @Override
063    public void propertyChange(PropertyChangeEvent evt) {
064        if ("active".equals(evt.getPropertyName())) {
065            setSelected((Boolean)evt.getNewValue());
066            requestFocusInWindow();
067        } else if ("selected".equals(evt.getPropertyName())) {
068            setSelected((Boolean)evt.getNewValue());
069        }
070    }
071
072    @Override
073    public void destroy() {
074        Action action = getAction();
075        if (action instanceof Destroyable) {
076            ((Destroyable) action).destroy();
077        }
078        if (action != null) {
079            action.removePropertyChangeListener(this);
080        }
081    }
082
083    String getPreferenceKey() {
084        String s = (String) getSafeActionValue("toolbar");
085        if (s == null) {
086            if (getAction()!=null) {
087                s = getAction().getClass().getName();
088            }
089        }
090        return "sidetoolbar.hidden."+s;
091
092    }
093
094    @Override
095    public void expertChanged(boolean isExpert) {
096        applyButtonHiddenPreferences();
097    }
098
099    @Override
100    public void applyButtonHiddenPreferences() {
101        boolean alwaysHideDisabled = Main.pref.getBoolean("sidetoolbar.hideDisabledButtons", false);
102        if (!isEnabled() && (hideIfDisabled || alwaysHideDisabled)) {
103            setVisible(false);  // hide because of disabled button
104        } else {
105            boolean hiddenFlag = false;
106            String hiddenFlagStr = Main.pref.get(getPreferenceKey(), null);
107            if (hiddenFlagStr == null) {
108                if (isExpert && !ExpertToggleAction.isExpert()) {
109                    hiddenFlag = true;
110                }
111            } else {
112                hiddenFlag = Boolean.parseBoolean(hiddenFlagStr);
113            }
114            setVisible( !hiddenFlag ); // show or hide, do what preferences say
115        }
116    }
117
118    @Override
119    public void setButtonHidden(boolean b) {
120        setVisible(!b);
121        if (listener!=null) { // if someone wants to know about changes of visibility
122            if (!b) listener.buttonShown(); else listener.buttonHidden();
123        }
124        if ((b && isExpert && !ExpertToggleAction.isExpert()) ||
125            (!b && isExpert && ExpertToggleAction.isExpert())) {
126            Main.pref.put(getPreferenceKey(), null);
127        } else {
128            Main.pref.put(getPreferenceKey(), b);
129        }
130    }
131
132    /*
133     * This fuction should be called for plugins that want to enable auto-hiding
134     * custom buttons when they are disabled (because of incorrect layer, for example)
135     */
136    public void setAutoHideDisabledButton(boolean b) {
137        hideIfDisabled = b;
138        if (b && !isEnabled()) {
139            setVisible(false);
140        }
141    }
142
143    @Override
144    public void showButton() {
145        setButtonHidden(false);
146    }
147
148    @Override
149    public void hideButton() {
150        setButtonHidden(true);
151    }
152
153    @Override
154    public String getActionName() {
155        return (String) getSafeActionValue(Action.NAME);
156    }
157
158    @Override
159    public Icon getIcon() {
160        return (Icon) getSafeActionValue(Action.SMALL_ICON);
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}