Implemented a class for selecting among a mutually exclusive set of

buttons.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@644 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2002-03-10 05:10:37 +00:00
parent d9f21d2b20
commit da6439a7bb
2 changed files with 430 additions and 0 deletions
@@ -0,0 +1,383 @@
//
// $Id: ComboButtonBox.java,v 1.1 2002/03/10 05:10:37 mdb Exp $
package com.samskivert.swing;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.border.Border;
import javax.swing.BorderFactory;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import com.samskivert.Log;
/**
* Used to display a horizontal or vertical array of buttons, out of which
* only one is selectable at a time (which will be represented by
* rendering it with an indented border, whereas the other buttons will
* render with an extruded border.
*/
public class ComboButtonBox extends JPanel
implements SwingConstants, ListDataListener, MouseListener
{
/**
* Constructs a button box with the specified orientation (either
* {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
public ComboButtonBox (int orientation)
{
this(orientation, new DefaultComboBoxModel());
}
/**
* Constructs a button box with the specified orientation (either
* {@link #HORIZONTAL} or {@link #VERTICAL}. The supplied model will
* be used to populate the buttons (see {@link #setModel} for more
* details).
*/
public ComboButtonBox (int orientation, ComboBoxModel model)
{
// set up our layout
setOrientation(orientation);
// set up our contents
setModel(model);
}
/**
* Sets the orientation of the box (either {@link #HORIZONTAL} or
* {@link #VERTICAL}.
*/
public void setOrientation (int orientation)
{
GroupLayout gl = (orientation == HORIZONTAL) ?
(GroupLayout)new HGroupLayout() : new VGroupLayout();
gl.setPolicy(GroupLayout.EQUALIZE);
setLayout(gl);
}
/**
* Provides the button box with a data model which it will display. If
* the model contains {@link Image} objects, they will be used to make
* icons for the buttons. Otherwise the button text will contain the
* string representation of the elements in the model.
*/
public void setModel (ComboBoxModel model)
{
// if we had a previous model, unregister ourselves from it
if (_model != null) {
_model.removeListDataListener(this);
}
// subscribe to our new model
_model = model;
_model.addListDataListener(this);
// rebuild the list
removeAll();
addButtons(0, _model.getSize());
}
/**
* Returns the model in use by the button box.
*/
public ComboBoxModel getModel ()
{
return _model;
}
/**
* Sets the index of the selected component. A value of -1 will clear
* the selection.
*/
public void setSelectedIndex (int selidx)
{
// update the display
updateSelection(selidx);
// let the model know what's up
Object item = (selidx == -1) ? null : _model.getElementAt(selidx);
_model.setSelectedItem(item);
}
/**
* Returns the index of the selected item.
*/
public int getSelectedIndex ()
{
return _selectedIndex;
}
/**
* Specifies the command that will be used when generating action
* events (which is done when the selection changes).
*/
public void setActionCommand (String actionCommand)
{
_actionCommand = actionCommand;
}
/**
* Adds a listener to our list of entities to be notified when the
* selection changes.
*/
public void addActionListener (ActionListener l)
{
listenerList.add(ActionListener.class, l);
}
/**
* Removes a listener from the list.
*/
public void removeActionListener (ActionListener l)
{
listenerList.remove(ActionListener.class, l);
}
/**
* Notifies our listeners when the selection changed.
*/
protected void fireActionPerformed ()
{
// guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// process the listeners last to first, notifying those that are
// interested in this event
for (int i = listeners.length-2; i >= 0; i -= 2) {
if (listeners[i] == ActionListener.class) {
// lazily create the event:
if (_actionEvent == null) {
_actionEvent = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, _actionCommand);
}
((ActionListener)listeners[i+1]).actionPerformed(_actionEvent);
}
}
}
// documentation inherited from interface
public void contentsChanged (ListDataEvent e)
{
// if this update is informing us of a new selection, reflect that
// in the UI
int start = e.getIndex0(), count = start - e.getIndex1() + 1;
if (start == -1) {
// figure out the selected index
int selidx = -1;
Object eitem = _model.getSelectedItem();
if (eitem != null) {
int ecount = _model.getSize();
for (int i = 0; i < ecount; i++) {
if (eitem == _model.getElementAt(i)) {
selidx = i;
break;
}
}
}
// and update it
updateSelection(selidx);
} else {
// replace the buttons in this range
removeButtons(start, count);
addButtons(start, count);
}
}
// documentation inherited from interface
public void intervalAdded (ListDataEvent e)
{
int start = e.getIndex0(), count = e.getIndex1() - start + 1;
// adjust the selected index
if (_selectedIndex >= start) {
_selectedIndex += count;
}
// add buttons for the new interval
addButtons(start, count);
}
// documentation inherited from interface
public void intervalRemoved (ListDataEvent e)
{
// remove the buttons in the specified interval
int start = e.getIndex0(), count = e.getIndex1() - start + 1;
removeButtons(start, count);
revalidate();
repaint();
}
// documentation inherited from interface
public void mouseClicked (MouseEvent e)
{
}
// documentation inherited from interface
public void mousePressed (MouseEvent e)
{
// keep track of the selected button
_selectedButton = (JLabel)e.getSource();
_selectedButton.setBorder(SELECTED_BORDER);
_selectedButton.repaint();
}
// documentation inherited from interface
public void mouseReleased (MouseEvent e)
{
// if the mouse was released within the bounds of the button, go
// ahead and select it properly
if (_selectedButton != null) {
if (_selectedButton.contains(e.getX(), e.getY())) {
// figure out which button this is
int selidx = -1;
int ccount = getComponentCount();
for (int i = 0; i < ccount; i++) {
if (_selectedButton == getComponent(i)) {
selidx = i;
break;
}
}
// sanity check
if (selidx == -1) {
Log.warning("Got action from non-button component!? " +
"[event=" + e + "].");
} else {
// if the selection changed, update our display and
// the model
setSelectedIndex(selidx);
}
} else {
_selectedButton.setBorder(DESELECTED_BORDER);
_selectedButton.repaint();
}
// clear out the selected button indicator
_selectedButton = null;
}
}
// documentation inherited from interface
public void mouseEntered (MouseEvent e)
{
}
// documentation inherited from interface
public void mouseExited (MouseEvent e)
{
}
/**
* Adds buttons for the specified range of model elements.
*/
protected void addButtons (int start, int count)
{
Object selobj = _model.getSelectedItem();
for (int i = start; i < count; i++) {
Object elem = _model.getElementAt(i);
if (selobj == elem) {
_selectedIndex = i;
}
JLabel ibut = null;
if (elem instanceof Image) {
ibut = new JLabel(new ImageIcon((Image)elem));
} else {
ibut = new JLabel(elem.toString());
}
ibut.addMouseListener(this);
ibut.setBorder((_selectedIndex == i) ?
SELECTED_BORDER : DESELECTED_BORDER);
add(ibut, i);
}
revalidate();
repaint();
}
/**
* Removes the buttons in the specified interval.
*/
protected void removeButtons (int start, int count)
{
while (count-- > 0) {
remove(start);
}
// adjust the selected index
if (_selectedIndex >= start) {
if (start + count > _selectedIndex) {
_selectedIndex = -1;
} else {
_selectedIndex -= count;
}
}
}
/**
* Sets the selection to the specified index and updates the buttons
* to reflect the change. This does not update the model.
*/
protected void updateSelection (int selidx)
{
// do nothing if this element is already selected
if (selidx == _selectedIndex) {
return;
}
// unhighlight the old component
if (_selectedIndex != -1) {
JLabel but = (JLabel)getComponent(_selectedIndex);
but.setBorder(DESELECTED_BORDER);
}
// save the new selection
_selectedIndex = selidx;
// if the selection is valid, highlight the new component
if (_selectedIndex != -1) {
JLabel but = (JLabel)getComponent(_selectedIndex);
but.setBorder(SELECTED_BORDER);
}
repaint();
}
/** The contents of the box. */
protected ComboBoxModel _model;
/** The index of the selected button. */
protected int _selectedIndex = -1;
/** The button over which the mouse was pressed. */
protected JLabel _selectedButton;
/** Used when notifying our listeners. */
protected ActionEvent _actionEvent;
/** The action command we generate when the selection changes. */
protected String _actionCommand;
/** The border used for selected components. */
protected static final Border SELECTED_BORDER =
BorderFactory.createLoweredBevelBorder();
/** The border used for non-selected components. */
protected static final Border DESELECTED_BORDER =
BorderFactory.createRaisedBevelBorder();
}
@@ -0,0 +1,47 @@
//
// $Id: TestComboButtonBox.java,v 1.1 2002/03/10 05:10:37 mdb Exp $
package com.samskivert.swing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JFrame;
/**
* Tests the image button box.
*/
public class TestComboButtonBox
{
protected static Image createImage (Color color)
{
BufferedImage img =
new BufferedImage(24, 24, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
g.setColor(color);
g.fillRect(0, 0, 24, 24);
g.dispose();
return img;
}
public static void main (String[] args)
{
JFrame frame = new JFrame("Test ComboButtonBox");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement(createImage(Color.blue));
model.addElement(createImage(Color.green));
model.addElement(createImage(Color.red));
model.addElement(createImage(Color.yellow));
ComboButtonBox box =
new ComboButtonBox(ComboButtonBox.HORIZONTAL, model);
frame.getContentPane().add(box);
frame.pack();
frame.show();
}
}