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}