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    }