/*
* Copyright (c) 2006, 2015, Oracle. All rights reserved.
*
* This software is the proprietary information of Oracle Corporation.
* Use is subject to license terms.
*/
package org.eclipse.persistence.tools.workbench.uitools.swing;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.event.ChangeListener;
import org.eclipse.persistence.tools.workbench.utility.TriStateBoolean;
/**
* This extension over the Swing's <code>JCheckBox</code> adds support for a
* partially selected state.
* <p>
* This code was found at: <a
* href="http://forum.java.sun.com/thread.jspa?threadID=593755&messageID=3116647">http://forum.java.sun.com/thread.jspa?threadID=593755&messageID=3116647</a>
* <p>
* The Sun's bug number is 4079882: <a
* href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4079882">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4079882</a>
*
* @version 11.0.0
* @since 11.0.0
* @author Pascal Filion
*/
public class TriStateCheckBox extends JCheckBox
{
/**
* Cache the check icon in order to compose it with a secondary icon.
*/
private Icon checkIcon;
/**
* Constants used to determine to set the selection state as partially
* selected.
*/
public static TriStateBoolean PARTIALLY_SELECTED = TriStateBoolean.UNDEFINED;
/**
* Constants used to determine to set the selection state as selected.
*/
public static TriStateBoolean SELECTED = TriStateBoolean.TRUE;
/**
* Constants used to determine to set the selection state as partially
* selected.
*/
public static TriStateBoolean UNSELECTED = TriStateBoolean.FALSE;
/*
* @see JCheckBox()
*/
public TriStateCheckBox()
{
this(null);
}
/*
* @see JCheckBox(String)
*/
public TriStateCheckBox(String text)
{
this(text, UNSELECTED);
}
/*
* @see JCheckBox(String, Icon, boolean)
*/
public TriStateCheckBox(String text, Icon icon, TriStateBoolean selectedState)
{
super(text, icon);
initialize(selectedState);
}
/*
* @see JCheckBox(String, boolean)
*/
public TriStateCheckBox(String text, TriStateBoolean selectedState)
{
this(text, null, selectedState);
}
/**
* Returns the current state, which is determined by the selection status of
* the model.
*/
public TriStateBoolean getState()
{
return getTriStateModel().getState();
}
/**
* Returns this button's model.
* <p>
* <b>Note:</b> {@link javax.swing.AbstractButton#getModel()} is not
* overriden just in case the UI delegate calls it before we reset the model.
*
* @return This button's model
*/
public TriStateButtonModel getTriStateModel()
{
return (TriStateButtonModel) super.getModel();
}
/**
* Initializes the model and icon to support the partially selected state.
*
* @param selectedState The initial selection state
*/
protected void initialize(TriStateBoolean selectedState)
{
setOpaque(false);
// Install the tri-state button model
setModel(new TriStateButtonModel(getModel()));
setState(selectedState);
}
/**
* Determines whether the selection state is set to be partially selected.
*
* @return <code>true</code> if the selection is set as partially selected or
* <code>false</code> if it is set as unselected or selected
*/
public boolean isPartiallySelected()
{
return getTriStateModel().isPartiallySelected();
}
/*
* (non-Javadoc)
*/
@Override
public void setIcon(Icon icon)
{
setSecondaryIcon(icon);
}
/**
* Sets the secondary icon, which is shown after the check icon.
*
* @param icon The secondary icon or <code>null</code> to clear a previously
* set secondary icon
*/
public void setSecondaryIcon(Icon icon)
{
if (icon == null)
{
super.setIcon(checkIcon);
}
else
{
super.setIcon(new CompositeIcon(checkIcon, icon));
}
}
/*
* (non-Javadoc)
*/
@Override
public void setSelected(boolean selected)
{
setState(selected ? SELECTED : UNSELECTED);
}
/**
* Sets the new state to either {@link #SELECTED}, {@link #UNSELECTED} or
* {@link #PARTIALLY_SELECTED}. If <code>null</code>, then it is treated as
* {@link #PARTIALLY_SELECTED}.
*
* @param state The new selection state
*/
public void setState(TriStateBoolean state)
{
getTriStateModel().setState(state);
}
/**
* Exactly which Design Pattern is this? Is it an Adapter, a Proxy or a
* Decorator? In this case, my vote lies with the Decorator, because we are
* extending functionality and "decorating" the original model with a more
* powerful model.
*/
public static class TriStateButtonModel implements ButtonModel
{
/**
* The wrapped <code>ButtonModel</code> set by the UI delegate.
*/
private final ButtonModel delegate;
/**
* The selection state supporting three states: selected, partially
* selected or unselected.
*/
private TriStateBoolean selectionState;
/**
* Creates a new <code>TriStateButtonModel</code>.
*
* @param delegate The wrapped <code>ButtonModel</code> set by the UI
* delegate
*/
public TriStateButtonModel(ButtonModel delegate)
{
super();
this.delegate = delegate;
this.selectionState = TriStateBoolean.valueOf(delegate.isSelected());
}
/*
* (non-Javadoc)
*/
public void addActionListener(ActionListener listener)
{
delegate.addActionListener(listener);
}
/*
* (non-Javadoc)
*/
public void addChangeListener(ChangeListener listener)
{
delegate.addChangeListener(listener);
}
/*
* (non-Javadoc)
*/
public void addItemListener(ItemListener listener)
{
delegate.addItemListener(listener);
}
/*
* (non-Javadoc)
*/
public String getActionCommand()
{
return delegate.getActionCommand();
}
/**
* Returns the wrapped <code>ButtonModel</code> set by the UI delegate.
*
* @return The model used to store the actual properties
*/
protected final ButtonModel getDelegate()
{
return delegate;
}
/*
* (non-Javadoc)
*/
public int getMnemonic()
{
return delegate.getMnemonic();
}
/*
* (non-Javadoc)
*/
public Object[] getSelectedObjects()
{
return delegate.getSelectedObjects();
}
/**
* Returns the current selection state.
*
* @return One of the three possible selection states
*/
protected TriStateBoolean getState()
{
return selectionState;
}
/*
* (non-Javadoc)
*/
public boolean isArmed()
{
return delegate.isArmed();
}
/*
* (non-Javadoc)
*/
public boolean isEnabled()
{
return delegate.isEnabled();
}
/**
* Determines whether the selection state is set to be partially selected.
*
* @return <code>true</code> if the selection is set as partially selected or
* <code>false</code> if it is set as unselected or selected
*/
public boolean isPartiallySelected()
{
return getState() == PARTIALLY_SELECTED;
}
/*
* (non-Javadoc)
*/
public boolean isPressed()
{
return delegate.isPressed();
}
/*
* (non-Javadoc)
*/
public boolean isRollover()
{
return delegate.isRollover();
}
/*
* (non-Javadoc)
*/
public boolean isSelected()
{
return delegate.isSelected();
}
/**
* Rotates between {@link TriStateCheckBox#PARTIALLY_SELECTED},
* {@link TriStateCheckBox#SELECTED} and {@link TriStateCheckBox#UNSELECTED}.
*/
protected void nextState()
{
TriStateBoolean current = getState();
if (current == UNSELECTED)
{
setState(SELECTED);
}
else if (current == SELECTED)
{
setState(PARTIALLY_SELECTED);
}
else if (current == PARTIALLY_SELECTED)
{
setState(UNSELECTED);
}
}
/*
* (non-Javadoc)
*/
public void removeActionListener(ActionListener listener)
{
delegate.removeActionListener(listener);
}
/*
* (non-Javadoc)
*/
public void removeChangeListener(ChangeListener listener)
{
delegate.removeChangeListener(listener);
}
/*
* (non-Javadoc)
*/
public void removeItemListener(ItemListener listener)
{
delegate.removeItemListener(listener);
}
/*
* (non-Javadoc)
*/
public void setActionCommand(String actionCommand)
{
delegate.setActionCommand(actionCommand);
}
/*
* (non-Javadoc)
*/
public void setArmed(boolean armed)
{
delegate.setArmed(armed);
}
/*
* (non-Javadoc)
*/
public void setEnabled(boolean enabled)
{
delegate.setEnabled(enabled);
}
/*
* (non-Javadoc)
*/
public void setGroup(ButtonGroup group)
{
delegate.setGroup(group);
}
/*
* (non-Javadoc)
*/
public void setMnemonic(int mnemonic)
{
delegate.setMnemonic(mnemonic);
}
/*
* (non-Javadoc)
*/
public void setPressed(boolean pressed)
{
if ((isPressed() != pressed) && isEnabled())
{
if (!pressed && isArmed())
{
nextState();
}
// The temporary selected flag prevents the UI from showing the
// partially selected state as selected
boolean selected = isSelected();
delegate.setPressed(pressed);
delegate.setSelected(selected);
}
}
/*
* (non-Javadoc)
*/
public void setRollover(boolean rollover)
{
delegate.setRollover(rollover);
}
/*
* (non-Javadoc)
*/
public void setSelected(boolean selected)
{
delegate.setSelected(selected);
}
/**
* Sets the new state to either {@link #SELECTED}, {@link #UNSELECTED} or
* {@link #PARTIALLY_SELECTED}. If <code>null</code>, then it is treated as
* {@link #PARTIALLY_SELECTED}.
*
* @param state The new selection state
*/
protected void setState(TriStateBoolean selectionState)
{
if (selectionState == null)
{
selectionState = PARTIALLY_SELECTED;
}
this.selectionState = selectionState;
if (selectionState == PARTIALLY_SELECTED)
{
delegate.setSelected(false);
}
else
{
delegate.setSelected(selectionState.booleanValue());
}
}
}
}