/*
* @(#)TristateButtonModel.java 5/20/2011
*
* Copyright 2002 - 2011 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.swing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
/**
* Model for TristateCheckBox. It introduces a mixed state to represent check box in the mixed selected state.
* ActionEvent will be fired when the state is changed.
*/
public class TristateButtonModel extends JToggleButton.ToggleButtonModel {
private static final long serialVersionUID = 9179129427948325126L;
/**
* Identifies the "mixed" bit in the bitmask, which indicates that the button is partial selected.
*/
public final static int MIXED = 1 << 7;
public TristateButtonModel() {
}
public void setState(int state) {
switch (state) {
case TristateCheckBox.STATE_UNSELECTED:
setSelected(false);
break;
case TristateCheckBox.STATE_SELECTED:
setSelected(true);
break;
case TristateCheckBox.STATE_MIXED:
setMixed(true);
break;
}
}
public int getState() {
if (isMixed()) return TristateCheckBox.STATE_MIXED;
else if (isSelected()) return TristateCheckBox.STATE_SELECTED;
else return TristateCheckBox.STATE_UNSELECTED;
}
/**
* We rotate between STATE_UNSELECTED, STATE_SELECTED and STATE_MIXED. Subclass can override this method to tell the
* check box what next state is. Here is the default implementation.
* <code><pre>
* if (current == TristateCheckBox.STATE_UNSELECTED) {
* return TristateCheckBox.STATE_SELECTED;
* }
* else if (current == TristateCheckBox.STATE_SELECTED) {
* return TristateCheckBox.STATE_MIXED;
* }
* else if (current == TristateCheckBox.STATE_MIXED) {
* return TristateCheckBox.STATE_UNSELECTED;
* }
* </code></pre>
*
* @param current the current state
* @return the next state of the current state.
*/
protected int getNextState(int current) {
if (current == TristateCheckBox.STATE_UNSELECTED) {
return TristateCheckBox.STATE_SELECTED;
}
else if (current == TristateCheckBox.STATE_SELECTED) {
return TristateCheckBox.STATE_MIXED;
}
else /*if (current == STATE_MIXED)*/ {
return TristateCheckBox.STATE_UNSELECTED;
}
}
@Override
public void setPressed(boolean b) {
if ((isPressed() == b) || !isEnabled()) {
return;
}
if (!b && isArmed()) {
updateState();
}
if (b) {
stateMask |= PRESSED;
}
else {
stateMask &= ~PRESSED;
}
fireStateChanged();
if (!isPressed() && isArmed()) {
int modifiers = 0;
AWTEvent currentEvent = EventQueue.getCurrentEvent();
if (currentEvent instanceof InputEvent) {
modifiers = ((InputEvent) currentEvent).getModifiers();
}
else if (currentEvent instanceof ActionEvent) {
modifiers = ((ActionEvent) currentEvent).getModifiers();
}
fireActionPerformed(
new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
getActionCommand(),
EventQueue.getMostRecentEventTime(),
modifiers));
}
}
/**
* Updates the state when the mouse is clicked. The default implementation is
* <pre><code>
* setState(getNextState(getState()));
* </code></pre>
*/
protected void updateState() {
setState(getNextState(getState()));
}
@Override
public void setSelected(boolean b) {
boolean mixed = isMixed();
if (mixed) {
stateMask &= ~MIXED;
internalSetSelected(!isSelected());
}
super.setSelected(b);
}
void internalSetSelected(boolean b) {
if (b) {
stateMask |= SELECTED;
}
else {
stateMask &= ~SELECTED;
}
}
public boolean isMixed() {
return (stateMask & MIXED) != 0;
}
public void setMixed(boolean b) {
if ((isMixed() == b)) {
return;
}
if (b) {
stateMask |= MIXED;
stateMask |= SELECTED; // make it selected
}
else {
stateMask &= ~MIXED;
}
fireStateChanged();
}
}