001 /* 002 * Copyright 2002-2004 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016 package org.springframework.richclient.command; 017 018 import java.util.Iterator; 019 020 import javax.swing.AbstractButton; 021 import javax.swing.Icon; 022 import javax.swing.JMenuItem; 023 import javax.swing.RootPaneContainer; 024 025 import org.springframework.richclient.command.config.CommandButtonConfigurer; 026 import org.springframework.richclient.command.config.CommandFaceDescriptor; 027 import org.springframework.richclient.factory.ButtonFactory; 028 import org.springframework.richclient.factory.MenuFactory; 029 import org.springframework.util.Assert; 030 031 public abstract class ToggleCommand extends ActionCommand { 032 033 public static final String SELECTED_PROPERTY = "selected"; 034 035 private boolean selected; 036 037 private ExclusiveCommandGroupSelectionController exclusiveController; 038 039 public ToggleCommand() { 040 } 041 042 public ToggleCommand(String commandId) { 043 super(commandId); 044 } 045 046 public ToggleCommand(String id, CommandFaceDescriptor face) { 047 super(id, face); 048 } 049 050 public ToggleCommand(String id, String encodedLabel) { 051 super(id, encodedLabel); 052 } 053 054 public ToggleCommand(String id, String encodedLabel, Icon icon, String caption) { 055 super(id, encodedLabel, icon, caption); 056 } 057 058 public void setExclusiveController(ExclusiveCommandGroupSelectionController exclusiveController) { 059 this.exclusiveController = exclusiveController; 060 } 061 062 public boolean isExclusiveGroupMember() { 063 return exclusiveController != null; 064 } 065 066 public JMenuItem createMenuItem(String faceDescriptorId, MenuFactory factory, 067 CommandButtonConfigurer buttonConfigurer) { 068 JMenuItem menuItem; 069 if (isExclusiveGroupMember()) { 070 menuItem = factory.createRadioButtonMenuItem(); 071 } 072 else { 073 menuItem = factory.createCheckBoxMenuItem(); 074 } 075 attach(menuItem, faceDescriptorId, buttonConfigurer); 076 return menuItem; 077 } 078 079 public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory, 080 CommandButtonConfigurer configurer) { 081 AbstractButton button = buttonFactory.createToggleButton(); 082 attach(button, faceDescriptorId, configurer); 083 return button; 084 } 085 086 public final AbstractButton createCheckBox() { 087 return createCheckBox(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer()); 088 } 089 090 public final AbstractButton createCheckBox(ButtonFactory buttonFactory) { 091 return createCheckBox(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer()); 092 } 093 094 public final AbstractButton createCheckBox(String faceDescriptorId, ButtonFactory buttonFactory) { 095 return createCheckBox(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer()); 096 } 097 098 public AbstractButton createCheckBox(String faceDescriptorId, ButtonFactory buttonFactory, 099 CommandButtonConfigurer configurer) { 100 AbstractButton checkBox = buttonFactory.createCheckBox(); 101 attach(checkBox, configurer); 102 return checkBox; 103 } 104 105 public final AbstractButton createRadioButton() { 106 return createRadioButton(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer()); 107 } 108 109 public final AbstractButton createRadioButton(ButtonFactory buttonFactory) { 110 return createRadioButton(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer()); 111 } 112 113 public final AbstractButton createRadioButton(String faceDescriptorId, ButtonFactory buttonFactory) { 114 return createRadioButton(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer()); 115 } 116 117 public AbstractButton createRadioButton(String faceDescriptorId, ButtonFactory buttonFactory, 118 CommandButtonConfigurer configurer) { 119 Assert.state(isExclusiveGroupMember(), 120 "Can't create radio buttons for toggle commands that aren't members of an exclusive group"); 121 AbstractButton radioButton = buttonFactory.createRadioButton(); 122 attach(radioButton, faceDescriptorId, configurer); 123 return radioButton; 124 } 125 126 /** 127 * {@inheritDoc} 128 */ 129 protected void onButtonAttached(AbstractButton button) { 130 super.onButtonAttached(button); 131 button.setSelected(selected); 132 } 133 134 /** 135 * Returns <code>true</code> if the command is selected. 136 */ 137 public final boolean isSelected() { 138 return this.selected; 139 } 140 141 /** 142 * Set the selection state of the command. 143 */ 144 public final void setSelected(boolean selected) { 145 if (isExclusiveGroupMember()) { 146 boolean oldState = isSelected(); 147 exclusiveController.handleSelectionRequest(this, selected); 148 // set back button state if controller didn't change this command; 149 // needed b/c of natural button check box toggling in swing 150 if (oldState == isSelected()) { 151 Iterator iter = buttonIterator(); 152 while (iter.hasNext()) { 153 AbstractButton button = (AbstractButton)iter.next(); 154 button.setSelected(isSelected()); 155 } 156 } 157 } 158 else { 159 requestSetSelection(selected); 160 } 161 } 162 163 /** 164 * Handles the switching of the selected state. All attached buttons are updated. 165 * 166 * @param selected select state to set. 167 * @return the select state afterwards. 168 */ 169 protected boolean requestSetSelection(boolean selected) { 170 boolean previousState = isSelected(); 171 172 if (previousState != selected) { 173 this.selected = onSelection(selected); 174 if (logger.isDebugEnabled()) { 175 logger.debug("Toggle command selection returned '" + this.selected + "'"); 176 } 177 } 178 179 // we must always update toggle buttons 180 Iterator it = buttonIterator(); 181 if (logger.isDebugEnabled()) { 182 logger.debug("Updating all attached toggle buttons to '" + isSelected() + "'"); 183 } 184 while (it.hasNext()) { 185 AbstractButton button = (AbstractButton)it.next(); 186 button.setSelected(isSelected()); 187 } 188 189 if (previousState != isSelected()) { 190 if (logger.isDebugEnabled()) { 191 logger.debug("Selection changed; firing property change event"); 192 } 193 firePropertyChange(SELECTED_PROPERTY, previousState, isSelected()); 194 } 195 196 return isSelected(); 197 } 198 199 /** 200 * Executing a toggleCommand will flip its select state. 201 */ 202 protected final void doExecuteCommand() { 203 setSelected(!isSelected()); 204 } 205 206 /** 207 * Hook method to perform the toggle action. Subclasses may override. 208 * <p> 209 * The toggle selection request can be vetoed by returning a boolean result (for example if onSelected 210 * is handed 'true', signaling the toggle command was activated, a subclass can veto that by 211 * returning false.) 212 * @param selected The newly requested selection state of this toggle command 213 * @return the value of selected, if allowed, !selection if vetoed. 214 */ 215 protected boolean onSelection(boolean selected) { 216 if (selected) { 217 onSelection(); 218 } 219 else { 220 onDeselection(); 221 } 222 return selected; 223 } 224 225 /** 226 * Convenience hook method for processing a selection action. 227 */ 228 protected void onSelection() { 229 230 } 231 232 /** 233 * Convenience hook method for processing a deselection action. 234 */ 235 protected void onDeselection() { 236 237 } 238 239 public void requestDefaultIn(RootPaneContainer container) { 240 throw new UnsupportedOperationException(); 241 } 242 243 }