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}