/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.command.support;
import org.springframework.util.Assert;
import org.valkyriercp.command.config.CommandButtonConfigurer;
import org.valkyriercp.command.config.CommandFaceDescriptor;
import org.valkyriercp.factory.ButtonFactory;
import org.valkyriercp.factory.MenuFactory;
import javax.swing.*;
import java.util.Iterator;
public abstract class ToggleCommand extends ActionCommand {
public static final String SELECTED_PROPERTY = "selected";
private boolean selected;
private ExclusiveCommandGroupSelectionController exclusiveController;
public ToggleCommand() {
}
public ToggleCommand(String commandId) {
super(commandId);
}
public ToggleCommand(String id, CommandFaceDescriptor face) {
super(id, face);
}
public ToggleCommand(String id, String encodedLabel) {
super(id, encodedLabel);
}
public ToggleCommand(String id, String encodedLabel, Icon icon, String caption) {
super(id, encodedLabel, icon, caption);
}
public void setExclusiveController(ExclusiveCommandGroupSelectionController exclusiveController) {
this.exclusiveController = exclusiveController;
}
public boolean isExclusiveGroupMember() {
return exclusiveController != null;
}
public JMenuItem createMenuItem(String faceDescriptorId, MenuFactory factory,
CommandButtonConfigurer buttonConfigurer) {
JMenuItem menuItem;
if (isExclusiveGroupMember()) {
menuItem = factory.createRadioButtonMenuItem();
}
else {
menuItem = factory.createCheckBoxMenuItem();
}
attach(menuItem, faceDescriptorId, buttonConfigurer);
return menuItem;
}
public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory,
CommandButtonConfigurer configurer) {
AbstractButton button = buttonFactory.createToggleButton();
attach(button, faceDescriptorId, configurer);
return button;
}
public final AbstractButton createCheckBox() {
return createCheckBox(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer());
}
public final AbstractButton createCheckBox(ButtonFactory buttonFactory) {
return createCheckBox(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer());
}
public final AbstractButton createCheckBox(String faceDescriptorId, ButtonFactory buttonFactory) {
return createCheckBox(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer());
}
public AbstractButton createCheckBox(String faceDescriptorId, ButtonFactory buttonFactory,
CommandButtonConfigurer configurer) {
AbstractButton checkBox = buttonFactory.createCheckBox();
attach(checkBox, configurer);
return checkBox;
}
public final AbstractButton createRadioButton() {
return createRadioButton(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer());
}
public final AbstractButton createRadioButton(ButtonFactory buttonFactory) {
return createRadioButton(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer());
}
public final AbstractButton createRadioButton(String faceDescriptorId, ButtonFactory buttonFactory) {
return createRadioButton(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer());
}
public AbstractButton createRadioButton(String faceDescriptorId, ButtonFactory buttonFactory,
CommandButtonConfigurer configurer) {
Assert.state(isExclusiveGroupMember(),
"Can't create radio buttons for toggle commands that aren't members of an exclusive group");
AbstractButton radioButton = buttonFactory.createRadioButton();
attach(radioButton, faceDescriptorId, configurer);
return radioButton;
}
/**
* {@inheritDoc}
*/
protected void onButtonAttached(AbstractButton button) {
super.onButtonAttached(button);
button.setSelected(selected);
}
/**
* Returns <code>true</code> if the command is selected.
*/
public final boolean isSelected() {
return this.selected;
}
/**
* Set the selection state of the command.
*/
public final void setSelected(boolean selected) {
if (isExclusiveGroupMember()) {
boolean oldState = isSelected();
exclusiveController.handleSelectionRequest(this, selected);
// set back button state if controller didn't change this command;
// needed b/c of natural button check box toggling in swing
if (oldState == isSelected()) {
Iterator iter = buttonIterator();
while (iter.hasNext()) {
AbstractButton button = (AbstractButton)iter.next();
button.setSelected(isSelected());
}
}
}
else {
requestSetSelection(selected);
}
}
/**
* Handles the switching of the selected state. All attached buttons are updated.
*
* @param selected select state to set.
* @return the select state afterwards.
*/
protected boolean requestSetSelection(boolean selected) {
boolean previousState = isSelected();
if (previousState != selected) {
this.selected = onSelection(selected);
if (logger.isDebugEnabled()) {
logger.debug("Toggle command selection returned '" + this.selected + "'");
}
}
// we must always update toggle buttons
Iterator it = buttonIterator();
if (logger.isDebugEnabled()) {
logger.debug("Updating all attached toggle buttons to '" + isSelected() + "'");
}
while (it.hasNext()) {
AbstractButton button = (AbstractButton)it.next();
button.setSelected(isSelected());
}
if (previousState != isSelected()) {
if (logger.isDebugEnabled()) {
logger.debug("Selection changed; firing property change event");
}
firePropertyChange(SELECTED_PROPERTY, previousState, isSelected());
}
return isSelected();
}
/**
* Executing a toggleCommand will flip its select state.
*/
protected final void doExecuteCommand() {
setSelected(!isSelected());
}
/**
* Hook method to perform the toggle action. Subclasses may override.
* <p>
* The toggle selection request can be vetoed by returning a boolean result (for example if onSelected
* is handed 'true', signaling the toggle command was activated, a subclass can veto that by
* returning false.)
* @param selected The newly requested selection state of this toggle command
* @return the value of selected, if allowed, !selection if vetoed.
*/
protected boolean onSelection(boolean selected) {
if (selected) {
onSelection();
}
else {
onDeselection();
}
return selected;
}
/**
* Convenience hook method for processing a selection action.
*/
protected void onSelection() {
}
/**
* Convenience hook method for processing a deselection action.
*/
protected void onDeselection() {
}
public void requestDefaultIn(RootPaneContainer container) {
throw new UnsupportedOperationException();
}
}