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}