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