Okay, this bit of a hack. There are a number of problems which are difficult to solve.
ButtonGroup
is a concrete class, so making a customised version is difficult (and this is why I like interface
s), it's further complicated as it relies very heavily on private
(or package private) properties, which doesn't provide any reasonable extension point
So, this leaves two courses of action, either use reflection or copy the base class and make our own modifications - neither case is pleasant.
So, of all the code I copied, I only modified one method...
public void setSelected(ButtonModel m, boolean b) {
if (b) {
if (m != selection) {
ButtonModel oldSelection = selection;
selection = m;
if (oldSelection != null && oldSelection != m) {
oldSelection.setSelected(false);
}
m.setSelected(true);
}
} else if (selection != null) {
ButtonModel oldSelection = selection;
selection = null;
oldSelection.setSelected(false);
}
}
Basically, this now supports deselecting the current element
public class UnselectableButtonGroup extends ButtonGroup {
// the list of buttons participating in this group
protected Vector<AbstractButton> buttons = new Vector<AbstractButton>();
/**
* The current selection.
*/
ButtonModel selection = null;
/**
* Creates a new <code>ButtonGroup</code>.
*/
public UnselectableButtonGroup() {
}
/**
* Adds the button to the group.
*
* @param b the button to be added
*/
public void add(AbstractButton b) {
if (b == null) {
return;
}
buttons.addElement(b);
if (b.isSelected()) {
if (selection == null) {
selection = b.getModel();
} else {
b.setSelected(false);
}
}
b.getModel().setGroup(this);
}
/**
* Removes the button from the group.
*
* @param b the button to be removed
*/
public void remove(AbstractButton b) {
if (b == null) {
return;
}
buttons.removeElement(b);
if (b.getModel() == selection) {
selection = null;
}
b.getModel().setGroup(null);
}
/**
* Clears the selection such that none of the buttons in the
* <code>ButtonGroup</code> are selected.
*
* @since 1.6
*/
public void clearSelection() {
if (selection != null) {
ButtonModel oldSelection = selection;
selection = null;
oldSelection.setSelected(false);
}
}
/**
* Returns all the buttons that are participating in this group.
*
* @return an <code>Enumeration</code> of the buttons in this group
*/
public Enumeration<AbstractButton> getElements() {
return buttons.elements();
}
/**
* Returns the model of the selected button.
*
* @return the selected button model
*/
public ButtonModel getSelection() {
return selection;
}
/**
* Sets the selected value for the <code>ButtonModel</code>. Only one
* button in the group may be selected at a time.
*
* @param m the <code>ButtonModel</code>
* @param b <code>true</code> if this button is to be selected,
* otherwise <code>false</code>
*/
public void setSelected(ButtonModel m, boolean b) {
if (b) {
if (m != selection) {
ButtonModel oldSelection = selection;
selection = m;
if (oldSelection != null && oldSelection != m) {
oldSelection.setSelected(false);
}
m.setSelected(true);
}
} else if (selection != null) {
ButtonModel oldSelection = selection;
selection = null;
oldSelection.setSelected(false);
}
}
/**
* Returns whether a <code>ButtonModel</code> is selected.
*
* @return <code>true</code> if the button is selected, otherwise
* returns <code>false</code>
*/
public boolean isSelected(ButtonModel m) {
return (m == selection);
}
/**
* Returns the number of buttons in the group.
*
* @return the button count
* @since 1.3
*/
public int getButtonCount() {
if (buttons == null) {
return 0;
} else {
return buttons.size();
}
}
}
I also investigated using an ItemListener
and a ChangeListener
(and both) on the buttons to try and control the state of the ButtonModel
I also tried Select Button Group but it didn't work for me (I didn't spend any time trying to debug it, but it's possible it might still work for other people)
Runnable Example...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
UnselectableButtonGroup bg = new UnselectableButtonGroup();
JRadioButton buttons[] = new JRadioButton[5];
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
for (int index = 0; index < buttons.length; index++) {
buttons[index] = new JRadioButton("Button " + index);
add(buttons[index], gbc);
bg.add(buttons[index]);
}
}
}
public class UnselectableButtonGroup extends ButtonGroup {
// the list of buttons participating in this group
protected Vector<AbstractButton> buttons = new Vector<AbstractButton>();
/**
* The current selection.
*/
ButtonModel selection = null;
/**
* Creates a new <code>ButtonGroup</code>.
*/
public UnselectableButtonGroup() {
}
/**
* Adds the button to the group.
*
* @param b the button to be added
*/
public void add(AbstractButton b) {
if (b == null) {
return;
}
buttons.addElement(b);
if (b.isSelected()) {
if (selection == null) {
selection = b.getModel();
} else {
b.setSelected(false);
}
}
b.getModel().setGroup(this);
}
/**
* Removes the button from the group.
*
* @param b the button to be removed
*/
public void remove(AbstractButton b) {
if (b == null) {
return;
}
buttons.removeElement(b);
if (b.getModel() == selection) {
selection = null;
}
b.getModel().setGroup(null);
}
/**
* Clears the selection such that none of the buttons in the
* <code>ButtonGroup</code> are selected.
*
* @since 1.6
*/
public void clearSelection() {
if (selection != null) {
ButtonModel oldSelection = selection;
selection = null;
oldSelection.setSelected(false);
}
}
/**
* Returns all the buttons that are participating in this group.
*
* @return an <code>Enumeration</code> of the buttons in this group
*/
public Enumeration<AbstractButton> getElements() {
return buttons.elements();
}
/**
* Returns the model of the selected button.
*
* @return the selected button model
*/
public ButtonModel getSelection() {
return selection;
}
/**
* Sets the selected value for the <code>ButtonModel</code>. Only one
* button in the group may be selected at a time.
*
* @param m the <code>ButtonModel</code>
* @param b <code>true</code> if this button is to be selected,
* otherwise <code>false</code>
*/
public void setSelected(ButtonModel m, boolean b) {
if (b) {
if (m != selection) {
ButtonModel oldSelection = selection;
selection = m;
if (oldSelection != null && oldSelection != m) {
oldSelection.setSelected(false);
}
m.setSelected(true);
}
} else if (selection != null) {
ButtonModel oldSelection = selection;
selection = null;
oldSelection.setSelected(false);
}
}
/**
* Returns whether a <code>ButtonModel</code> is selected.
*
* @return <code>true</code> if the button is selected, otherwise
* returns <code>false</code>
*/
public boolean isSelected(ButtonModel m) {
return (m == selection);
}
/**
* Returns the number of buttons in the group.
*
* @return the button count
* @since 1.3
*/
public int getButtonCount() {
if (buttons == null) {
return 0;
} else {
return buttons.size();
}
}
}
}