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 }