| /******************************************************************************* |
| * Copyright (c) 2005, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jface.examples.databinding.radioGroup; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.jface.examples.databinding.ducks.DuckType; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| |
| /** |
| * This object decorates a bunch of SWT.RADIO buttons and provides saner |
| * selection semantics than you get by default with those radio buttons. |
| * <p> |
| * Its API is basically the same API as List, but with unnecessary methods |
| * removed. |
| */ |
| public class RadioGroup { |
| |
| private final IRadioButton[] buttons; |
| private final Object[] values; |
| IRadioButton oldSelection = null; |
| IRadioButton selectedButton = null; |
| IRadioButton potentialNewSelection = null; |
| |
| /** (Non-API) |
| * Interface IRadioButton. A duck interface that is used internally by RadioGroup |
| * and by RadioGroup's unit tests. |
| */ |
| public static interface IRadioButton { |
| void setData(String string, Object object); |
| void addSelectionListener(SelectionListener selectionListener); |
| void setSelection(boolean b); |
| boolean getSelection(); |
| boolean isFocusControl(); |
| String getText(); |
| void setText(String string); |
| void notifyListeners(int eventType, Event object); |
| } |
| |
| /** |
| * Constructs an instance of this widget given an array of Button objects to wrap. |
| * The Button objects must have been created with the SWT.RADIO style bit set, |
| * and they must all be in the same Composite. |
| * |
| * @param radioButtons Object[] an array of radio buttons to wrap. |
| * @param values Object[] an array of objects corresponding to the value of each radio button. |
| */ |
| public RadioGroup(Object[] radioButtons, Object[] values) { |
| IRadioButton[] buttons = new IRadioButton[radioButtons.length]; |
| if (buttons.length < 1) { |
| throw new IllegalArgumentException("A RadioGroup must manage at least one Button"); |
| } |
| for (int i = 0; i < buttons.length; i++) { |
| if (!DuckType.instanceOf(IRadioButton.class, radioButtons[i])) { |
| throw new IllegalArgumentException("A radio button was not passed"); |
| } |
| buttons[i] = (IRadioButton) DuckType.implement(IRadioButton.class, radioButtons[i]); |
| buttons[i].setData(Integer.toString(i), new Integer(i)); |
| buttons[i].addSelectionListener(selectionListener); |
| } |
| this.buttons = buttons; |
| this.values = values; |
| } |
| |
| /** |
| * Returns the object corresponding to the currently-selected radio button |
| * or null if no radio button is selected. |
| * |
| * @return the object corresponding to the currently-selected radio button |
| * or null if no radio button is selected. |
| */ |
| public Object getSelection() { |
| int selectionIndex = getSelectionIndex(); |
| if (selectionIndex < 0) |
| return ""; |
| return values[selectionIndex]; |
| } |
| |
| /** |
| * Sets the selected radio button to the radio button whose model object |
| * equals() the object specified by newSelection. If !newSelection.equals() |
| * any model object managed by this radio group, deselects all radio buttons. |
| * |
| * @param newSelection A model object corresponding to one of the model |
| * objects associated with one of the radio buttons. |
| */ |
| public void setSelection(Object newSelection) { |
| deselectAll(); |
| for (int i = 0; i < values.length; i++) { |
| if (values[i].equals(newSelection)) { |
| setSelection(i); |
| return; |
| } |
| } |
| } |
| |
| private SelectionListener selectionListener = new SelectionListener() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| widgetSelected(e); |
| } |
| |
| public void widgetSelected(SelectionEvent e) { |
| potentialNewSelection = getButton(e); |
| if (! potentialNewSelection.getSelection()) { |
| return; |
| } |
| if (potentialNewSelection.equals(selectedButton)) { |
| return; |
| } |
| |
| if (fireWidgetChangeSelectionEvent(e)) { |
| oldSelection = selectedButton; |
| selectedButton = potentialNewSelection; |
| if (oldSelection == null) { |
| oldSelection = selectedButton; |
| } |
| |
| fireWidgetSelectedEvent(e); |
| } |
| } |
| |
| private IRadioButton getButton(SelectionEvent e) { |
| // If the actual IRadioButton is a test fixture, then the test fixture can't |
| // set e.widget, so the button object will be in e.data instead and a dummy |
| // Widget will be in e.widget. |
| if (e.data != null) { |
| return (IRadioButton) e.data; |
| } |
| return (IRadioButton) DuckType.implement(IRadioButton.class, e.widget); |
| } |
| }; |
| |
| private List widgetChangeListeners = new LinkedList(); |
| |
| protected boolean fireWidgetChangeSelectionEvent(SelectionEvent e) { |
| for (Iterator listenersIter = widgetChangeListeners.iterator(); listenersIter.hasNext();) { |
| VetoableSelectionListener listener = (VetoableSelectionListener) listenersIter.next(); |
| listener.canWidgetChangeSelection(e); |
| if (!e.doit) { |
| rollbackSelection(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void rollbackSelection() { |
| Display.getCurrent().asyncExec(new Runnable() { |
| public void run() { |
| potentialNewSelection.setSelection(false); |
| selectedButton.setSelection(true); |
| // selectedButton.notifyListeners(SWT.Selection, null); |
| } |
| }); |
| } |
| |
| |
| /** |
| * Adds the listener to the collection of listeners who will |
| * be notified when the receiver's selection is about to change, by sending |
| * it one of the messages defined in the <code>VetoableSelectionListener</code> |
| * interface. |
| * <p> |
| * <code>widgetSelected</code> is called when the selection changes. |
| * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. |
| * </p> |
| * |
| * @param listener the listener which should be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see VetoableSelectionListener |
| * @see #removeVetoableSelectionListener |
| * @see SelectionEvent |
| */ |
| public void addVetoableSelectionListener(VetoableSelectionListener listener) { |
| widgetChangeListeners.add(listener); |
| } |
| |
| /** |
| * Removes the listener from the collection of listeners who will |
| * be notified when the receiver's selection is about to change. |
| * |
| * @param listener the listener which should no longer be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see VetoableSelectionListener |
| * @see #addVetoableSelectionListener |
| */ |
| public void removeVetoableSelectionListener(VetoableSelectionListener listener) { |
| widgetChangeListeners.remove(listener); |
| } |
| |
| |
| private List widgetSelectedListeners = new ArrayList(); |
| |
| protected void fireWidgetSelectedEvent(SelectionEvent e) { |
| for (Iterator listenersIter = widgetSelectedListeners.iterator(); listenersIter.hasNext();) { |
| SelectionListener listener = (SelectionListener) listenersIter.next(); |
| listener.widgetSelected(e); |
| } |
| } |
| |
| protected void fireWidgetDefaultSelectedEvent(SelectionEvent e) { |
| fireWidgetSelectedEvent(e); |
| } |
| |
| /** |
| * Adds the listener to the collection of listeners who will |
| * be notified when the receiver's selection changes, by sending |
| * it one of the messages defined in the <code>SelectionListener</code> |
| * interface. |
| * <p> |
| * <code>widgetSelected</code> is called when the selection changes. |
| * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. |
| * </p> |
| * |
| * @param listener the listener which should be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #removeSelectionListener |
| * @see SelectionEvent |
| */ |
| public void addSelectionListener(SelectionListener listener) { |
| widgetSelectedListeners.add(listener); |
| } |
| |
| /** |
| * Removes the listener from the collection of listeners who will |
| * be notified when the receiver's selection changes. |
| * |
| * @param listener the listener which should no longer be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #addSelectionListener |
| */ |
| public void removeSelectionListener(SelectionListener listener) { |
| widgetSelectedListeners.remove(listener); |
| } |
| |
| /** |
| * Deselects the item at the given zero-relative index in the receiver. |
| * If the item at the index was already deselected, it remains |
| * deselected. Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to deselect |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselect (int index) { |
| if (index < 0 || index >= buttons.length) |
| return; |
| buttons[index].setSelection(false); |
| } |
| |
| /** |
| * Deselects all selected items in the receiver. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselectAll () { |
| for (int i = 0; i < buttons.length; i++) |
| buttons[i].setSelection(false); |
| } |
| |
| /** |
| * Returns the zero-relative index of the item which currently |
| * has the focus in the receiver, or -1 if no item has focus. |
| * |
| * @return the index of the selected item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getFocusIndex () { |
| for (int i = 0; i < buttons.length; i++) { |
| if (buttons[i].isFocusControl()) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Returns the item at the given, zero-relative index in the |
| * receiver. Throws an exception if the index is out of range. |
| * |
| * @param index the index of the item to return |
| * @return the item at the given index |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * FIXME: tck - this should be renamed to getItemText() |
| */ |
| public String getItem (int index) { |
| if (index < 0 || index >= buttons.length) |
| SWT.error(SWT.ERROR_INVALID_RANGE, null, "getItem for a nonexistant item"); |
| return buttons[index].getText(); |
| } |
| |
| /** |
| * Returns the number of items contained in the receiver. |
| * |
| * @return the number of items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getItemCount () { |
| return buttons.length; |
| } |
| |
| /** |
| * Returns a (possibly empty) array of <code>String</code>s which |
| * are the items in the receiver. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its list of items, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * |
| * @return the items in the receiver's list |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public String [] getItems () { |
| List itemStrings = new ArrayList(); |
| for (int i = 0; i < buttons.length; i++) { |
| itemStrings.add(buttons[i].getText()); |
| } |
| return (String[]) itemStrings.toArray(new String[itemStrings.size()]); |
| } |
| |
| public Object[] getButtons() { |
| return buttons; |
| } |
| |
| /** |
| * Returns the zero-relative index of the item which is currently |
| * selected in the receiver, or -1 if no item is selected. |
| * |
| * @return the index of the selected item or -1 |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getSelectionIndex () { |
| for (int i = 0; i < buttons.length; i++) { |
| if (buttons[i].getSelection() == true) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Gets the index of an item. |
| * <p> |
| * The list is searched starting at 0 until an |
| * item is found that is equal to the search item. |
| * If no item is found, -1 is returned. Indexing |
| * is zero based. |
| * |
| * @param string the search item |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf (String string) { |
| for (int i = 0; i < buttons.length; i++) { |
| if (buttons[i].getText().equals(string)) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Searches the receiver's list starting at the given, |
| * zero-relative index until an item is found that is equal |
| * to the argument, and returns the index of that item. If |
| * no item is found or the starting index is out of range, |
| * returns -1. |
| * |
| * @param string the search item |
| * @param start the zero-relative index at which to start the search |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf (String string, int start) { |
| for (int i = start; i < buttons.length; i++) { |
| if (buttons[i].getText().equals(string)) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Returns <code>true</code> if the item is selected, |
| * and <code>false</code> otherwise. Indices out of |
| * range are ignored. |
| * |
| * @param index the index of the item |
| * @return the visibility state of the item at the index |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public boolean isSelected (int index) { |
| return buttons[index].getSelection(); |
| } |
| |
| /** |
| * Selects the item at the given zero-relative index in the receiver's |
| * list. If the item at the index was already selected, it remains |
| * selected. Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void select (int index) { |
| if (index < 0 || index >= buttons.length) |
| return; |
| buttons[index].setSelection(true); |
| } |
| |
| /** |
| * Sets the text of the item in the receiver's list at the given |
| * zero-relative index to the string argument. This is equivalent |
| * to <code>remove</code>'ing the old item at the index, and then |
| * <code>add</code>'ing the new item at that index. |
| * |
| * @param index the index for the item |
| * @param string the new text for the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setItem (int index, String string) { |
| if (index < 0 || index >= buttons.length) |
| SWT.error(SWT.ERROR_INVALID_RANGE, null, "setItem for a nonexistant item"); |
| buttons[index].setText(string); |
| } |
| |
| /** |
| * Selects the item at the given zero-relative index in the receiver. |
| * If the item at the index was already selected, it remains selected. |
| * The current selection is first cleared, then the new item is selected. |
| * Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * @see List#deselectAll() |
| * @see List#select(int) |
| */ |
| public void setSelection (int index) { |
| if (index < 0 || index > buttons.length - 1) { |
| return; |
| } |
| buttons[index].setSelection(true); |
| } |
| |
| } |