/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.tools;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
/**
* This is the tri state check box.
*
* @author Santhosh Kumar, Ingo Mierswa
*/
public class ExtendedTriStateCheckBox extends JCheckBox {
private static final long serialVersionUID = 8924026691487760529L;
private final TristateDecorator model;
public ExtendedTriStateCheckBox(String text, Icon icon, Boolean initial) {
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction() { // NOI18N
private static final long serialVersionUID = 0L;
@Override
public void actionPerformed(ActionEvent e) {
grabFocus();
model.nextState();
}
});
map.put("released", null); // NOI18N
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public ExtendedTriStateCheckBox(String text, Boolean initial) {
this(text, null, initial);
}
public ExtendedTriStateCheckBox(String text) {
this(text, null);
}
public ExtendedTriStateCheckBox() {
this(null);
}
/** No one may add mouse listeners, not even Swing! */
@Override
public void addMouseListener(MouseListener l) {
}
/**
* Set the new state to either SELECTED, NOT_SELECTED or DONT_CARE. If state ==
* null, it is treated as DONT_CARE.
*/
public void setState(Boolean state) {
model.setState(state);
}
/**
* Return the current state, which is determined by the selection status of
* the model.
*/
public Boolean getState() {
return model.getState();
}
/**
* 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.
*/
private static class TristateDecorator implements ButtonModel {
private final ButtonModel other;
private TristateDecorator(ButtonModel other) {
this.other = other;
}
private void setState(Boolean state) {
if (state == null) {
other.setArmed(true);
setPressed(true);
setSelected(false);
} else if (state.booleanValue() == Boolean.FALSE) {
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state.booleanValue() == Boolean.TRUE) {
other.setArmed(false);
setPressed(false);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed state of the
* model.
*
* We return the SELECTED state when the checkbox is selected but not
* armed, DONT_CARE state when the checkbox is selected and armed (grey)
* and NOT_SELECTED when the checkbox is deselected.
*/
private Boolean getState() {
if (isSelected() && !isArmed()) {
// normal black tick
return Boolean.TRUE;
} else if (isSelected() && isArmed()) {
// don't care grey tick
return null;
} else {
// normal deselected
return Boolean.FALSE;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE. */
private void nextState() {
Boolean current = getState();
if (current == Boolean.FALSE) {
setState(Boolean.TRUE);
} else if (current == Boolean.TRUE) {
setState(null);
} else if (current == null) {
setState(Boolean.FALSE);
}
}
/** Filter: No one may change the armed status except us. */
@Override
public void setArmed(boolean b) {
}
/**
* We disable focusing on the component when it is not enabled.
*/
@Override
public void setEnabled(boolean b) {
// setFocusable(b);
other.setEnabled(b);
}
/**
* All these methods simply delegate to the "other" model that is being
* decorated.
*/
@Override
public boolean isArmed() {
return other.isArmed();
}
@Override
public boolean isSelected() {
return other.isSelected();
}
@Override
public boolean isEnabled() {
return other.isEnabled();
}
@Override
public boolean isPressed() {
return other.isPressed();
}
@Override
public boolean isRollover() {
return other.isRollover();
}
@Override
public void setSelected(boolean b) {
other.setSelected(b);
}
@Override
public void setPressed(boolean b) {
other.setPressed(b);
}
@Override
public void setRollover(boolean b) {
other.setRollover(b);
}
@Override
public void setMnemonic(int key) {
other.setMnemonic(key);
}
@Override
public int getMnemonic() {
return other.getMnemonic();
}
@Override
public void setActionCommand(String s) {
other.setActionCommand(s);
}
@Override
public String getActionCommand() {
return other.getActionCommand();
}
@Override
public void setGroup(ButtonGroup group) {
other.setGroup(group);
}
@Override
public void addActionListener(ActionListener l) {
other.addActionListener(l);
}
@Override
public void removeActionListener(ActionListener l) {
other.removeActionListener(l);
}
@Override
public void addItemListener(ItemListener l) {
other.addItemListener(l);
}
@Override
public void removeItemListener(ItemListener l) {
other.removeItemListener(l);
}
@Override
public void addChangeListener(ChangeListener l) {
other.addChangeListener(l);
}
@Override
public void removeChangeListener(ChangeListener l) {
other.removeChangeListener(l);
}
@Override
public Object[] getSelectedObjects() {
return other.getSelectedObjects();
}
}
}