blob: b7b78c79a5106ca1a4c41050c0515f179d166c34 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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.draw2d;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
//import org.eclipse.draw2d.internal.Timer;
/**
* A model for buttons containing several properties, including enabled,
* pressed, selected, rollover enabled and mouseover.
*/
public class ButtonModel {
/** Enabled property */
public static final String ENABLED_PROPERTY = "enabled"; //$NON-NLS-1$
/** Pressed property */
public static final String PRESSED_PROPERTY = "pressed"; //$NON-NLS-1$
/** Selected property */
public static final String SELECTED_PROPERTY = "selected"; //$NON-NLS-1$
/** Rollover Enabled property */
public static final String ROLLOVER_ENABLED_PROPERTY = "rollover enabled"; //$NON-NLS-1$
/** Mouseover property */
public static final String MOUSEOVER_PROPERTY = "mouseover"; //$NON-NLS-1$
/** Armed property */
public static final String ARMED_PROPERTY = "armed"; //$NON-NLS-1$
/** Flag for armed button state */
protected static final int ARMED_FLAG = 1;
/** Flag for pressed button state */
protected static final int PRESSED_FLAG = 2;
/** Flag for mouseOver state */
protected static final int MOUSEOVER_FLAG = 4;
/** Flag for selected button state */
protected static final int SELECTED_FLAG = 8;
/** Flag for enablement button state */
protected static final int ENABLED_FLAG = 16;
/** Flag for rollover enablement button state */
protected static final int ROLLOVER_ENABLED_FLAG = 32;
/** Flag that can be used by subclasses to define more states */
protected static final int MAX_FLAG = ROLLOVER_ENABLED_FLAG;
private int state = ENABLED_FLAG;
private Object data;
/**
* Action performed events are not fired until the mouse button is released.
*/
public static final int DEFAULT_FIRING_BEHAVIOR = 0;
/**
* Action performed events fire repeatedly until the mouse button is
* released.
*/
public static final int REPEAT_FIRING_BEHAVIOR = 1;
/**
* The name of the action associated with this button.
*/
protected String actionName;
/**
* The ButtonGroup this button belongs to (if any).
*/
protected ButtonGroup group = null;
private EventListenerList listeners = new EventListenerList();
/**
* Listens to button state transitions and fires action performed events
* based on the desired behavior ({@link #DEFAULT_FIRING_BEHAVIOR} or
* {@link #REPEAT_FIRING_BEHAVIOR}).
*/
protected ButtonStateTransitionListener firingBehavior;
{
installFiringBehavior();
}
/**
* Registers the given listener as an ActionListener.
*
* @param listener
* The ActionListener to add
* @since 2.0
*/
public void addActionListener(ActionListener listener) {
if (listener == null)
throw new IllegalArgumentException();
listeners.addListener(ActionListener.class, listener);
}
/**
* Registers the given listener as a ChangeListener.
*
* @param listener
* The ChangeListener to add
* @since 2.0
*/
public void addChangeListener(ChangeListener listener) {
if (listener == null)
throw new IllegalArgumentException();
listeners.addListener(ChangeListener.class, listener);
}
/**
* Registers the given listener as a ButtonStateTransitionListener.
*
* @param listener
* The ButtonStateTransitionListener to add
* @since 2.0
*/
public void addStateTransitionListener(
ButtonStateTransitionListener listener) {
if (listener == null)
throw new IllegalArgumentException();
listeners.addListener(ButtonStateTransitionListener.class, listener);
}
/**
* Notifies any ActionListeners on this ButtonModel that an action has been
* performed.
*
* @since 2.0
*/
protected void fireActionPerformed() {
Iterator iter = listeners.getListeners(ActionListener.class);
ActionEvent action = new ActionEvent(this);
while (iter.hasNext())
((ActionListener) iter.next()).actionPerformed(action);
}
/**
* Notifies any listening ButtonStateTransitionListener that the pressed
* state of this button has been cancelled.
*
* @since 2.0
*/
protected void fireCanceled() {
Iterator iter = listeners
.getListeners(ButtonStateTransitionListener.class);
while (iter.hasNext())
((ButtonStateTransitionListener) iter.next()).canceled();
}
/**
* Notifies any listening ButtonStateTransitionListener that this button has
* been pressed.
*
* @since 2.0
*/
protected void firePressed() {
Iterator iter = listeners
.getListeners(ButtonStateTransitionListener.class);
while (iter.hasNext())
((ButtonStateTransitionListener) iter.next()).pressed();
}
/**
* Notifies any listening ButtonStateTransitionListener that this button has
* been released.
*
* @since 2.0
*/
protected void fireReleased() {
Iterator iter = listeners
.getListeners(ButtonStateTransitionListener.class);
while (iter.hasNext())
((ButtonStateTransitionListener) iter.next()).released();
}
/**
* Notifies any listening ButtonStateTransitionListeners that this button
* has resumed activity.
*
* @since 2.0
*/
protected void fireResume() {
Iterator iter = listeners
.getListeners(ButtonStateTransitionListener.class);
while (iter.hasNext())
((ButtonStateTransitionListener) iter.next()).resume();
}
/**
* Notifies any listening ChangeListeners that this button's state has
* changed.
*
* @param property
* The name of the property that changed
* @since 2.0
*/
protected void fireStateChanged(String property) {
Iterator iter = listeners.getListeners(ChangeListener.class);
ChangeEvent change = new ChangeEvent(this, property);
while (iter.hasNext())
((ChangeListener) iter.next()).handleStateChanged(change);
}
/**
* Notifies any listening ButtonStateTransitionListeners that this button
* has suspended activity.
*
* @since 2.0
*/
protected void fireSuspend() {
Iterator iter = listeners
.getListeners(ButtonStateTransitionListener.class);
while (iter.hasNext())
((ButtonStateTransitionListener) iter.next()).suspend();
}
boolean getFlag(int which) {
return (state & which) != 0;
}
/**
* Returns the group to which this model belongs.
*
* @return The ButtonGroup to which this model belongs
* @since 2.0
*/
public ButtonGroup getGroup() {
return group;
}
/**
* Returns an object representing user data.
*
* @return User data
* @since 2.0
*/
public Object getUserData() {
return data;
}
/**
* Sets the firing behavior for this button.
*
* @since 2.0
*/
protected void installFiringBehavior() {
setFiringBehavior(DEFAULT_FIRING_BEHAVIOR);
}
/**
* Returns <code>true</code> if this button is armed. If a button is armed,
* it will fire an ActionPerformed when released.
*
* @return <code>true</code> if this button is armed
* @since 2.0
*/
public boolean isArmed() {
return (state & ARMED_FLAG) != 0;
}
/**
* Returns <code>true</code> if this button is enabled.
*
* @return <code>true</code> if this button is enabled
* @since 2.0
*/
public boolean isEnabled() {
return (state & ENABLED_FLAG) != 0;
}
/**
* Returns <code>true</code> if the mouse is over this button.
*
* @return <code>true</code> if the mouse is over this button
* @since 2.0
*/
public boolean isMouseOver() {
return (state & MOUSEOVER_FLAG) != 0;
}
/**
* Returns <code>true</code> if this button is pressed.
*
* @return <code>true</code> if this button is pressed
* @since 2.0
*/
public boolean isPressed() {
return (state & PRESSED_FLAG) != 0;
}
/**
* Returns the selection state of this model. If this model belongs to any
* group, the group is queried for selection state, else the flags are used.
*
* @return <code>true</code> if this button is selected
* @since 2.0
*/
public boolean isSelected() {
if (group == null) {
return (state & SELECTED_FLAG) != 0;
} else {
return group.isSelected(this);
}
}
/**
* Removes the given ActionListener.
*
* @param listener
* The ActionListener to remove
* @since 2.0
*/
public void removeActionListener(ActionListener listener) {
listeners.removeListener(ActionListener.class, listener);
}
/**
* Removes the given ChangeListener.
*
* @param listener
* The ChangeListener to remove
* @since 2.0
*/
public void removeChangeListener(ChangeListener listener) {
listeners.removeListener(ChangeListener.class, listener);
}
/**
* Removes the given ButtonStateTransitionListener.
*
* @param listener
* The ButtonStateTransitionListener to remove
* @since 2.0
*/
public void removeStateTransitionListener(
ButtonStateTransitionListener listener) {
listeners.removeListener(ButtonStateTransitionListener.class, listener);
}
/**
* Sets this button to be armed. If a button is armed, it will fire an
* ActionPerformed when released.
*
* @param value
* The armed state
* @since 2.0
*/
public void setArmed(boolean value) {
if (isArmed() == value)
return;
if (!isEnabled())
return;
setFlag(ARMED_FLAG, value);
fireStateChanged(ARMED_PROPERTY);
}
/**
* Sets this button to be enabled.
*
* @param value
* The enabled state
* @since 2.0
*/
public void setEnabled(boolean value) {
if (isEnabled() == value)
return;
if (!value) {
setMouseOver(false);
setArmed(false);
setPressed(false);
}
setFlag(ENABLED_FLAG, value);
fireStateChanged(ENABLED_PROPERTY);
}
/**
* Sets the firing behavior for this button.
* {@link #DEFAULT_FIRING_BEHAVIOR} is the default behavior, where action
* performed events are not fired until the mouse button is released.
* {@link #REPEAT_FIRING_BEHAVIOR} causes action performed events to fire
* repeatedly until the mouse button is released.
*
* @param type
* The firing behavior type
* @since 2.0
*
*/
public void setFiringBehavior(int type) {
if (firingBehavior != null)
removeStateTransitionListener(firingBehavior);
switch (type) {
case REPEAT_FIRING_BEHAVIOR:
firingBehavior = new RepeatFiringBehavior();
break;
default:
firingBehavior = new DefaultFiringBehavior();
}
addStateTransitionListener(firingBehavior);
}
void setFlag(int flag, boolean value) {
if (value)
state |= flag;
else
state &= ~flag;
}
/**
* Sets the ButtonGroup to which this model belongs to. Adds this model as a
* listener to the group.
*
* @param bg
* The group to which this model belongs.
* @since 2.0
*/
public void setGroup(ButtonGroup bg) {
if (group == bg)
return;
if (group != null)
group.remove(this);
group = bg;
if (group != null)
group.add(this);
}
/**
* Sets the mouseover property of this button.
*
* @param value
* The value the mouseover property will be set to
* @since 2.0
*/
public void setMouseOver(boolean value) {
if (isMouseOver() == value)
return;
if (isPressed())
if (value)
fireResume();
else
fireSuspend();
setFlag(MOUSEOVER_FLAG, value);
fireStateChanged(MOUSEOVER_PROPERTY);
}
/**
* Sets the pressed property of this button.
*
* @param value
* The value the pressed property will be set to
* @since 2.0
*/
public void setPressed(boolean value) {
if (isPressed() == value)
return;
setFlag(PRESSED_FLAG, value);
if (value)
firePressed();
else {
if (isArmed())
fireReleased();
else
fireCanceled();
}
fireStateChanged(PRESSED_PROPERTY);
}
/**
* Sets this button to be selected.
*
* @param value
* The value the selected property will be set to
* @since 2.0
*/
public void setSelected(boolean value) {
if (group == null) {
if (isSelected() == value)
return;
} else {
group.setSelected(this, value);
if (getFlag(SELECTED_FLAG) == isSelected())
return;
}
setFlag(SELECTED_FLAG, value);
fireStateChanged(SELECTED_PROPERTY);
}
/**
* Sets user data.
*
* @param data
* The user data
* @since 2.0
*/
public void setUserData(Object data) {
this.data = data;
}
class DefaultFiringBehavior extends ButtonStateTransitionListener {
public void released() {
fireActionPerformed();
}
}
class RepeatFiringBehavior extends ButtonStateTransitionListener {
protected static final int INITIAL_DELAY = 250, STEP_DELAY = 40;
protected int stepDelay = INITIAL_DELAY, initialDelay = STEP_DELAY;
protected Timer timer;
public void pressed() {
fireActionPerformed();
if (!isEnabled())
return;
timer = new Timer();
TimerTask runAction = new Task(timer);
timer.scheduleAtFixedRate(runAction, INITIAL_DELAY, STEP_DELAY);
}
public void canceled() {
suspend();
}
public void released() {
suspend();
}
public void resume() {
timer = new Timer();
TimerTask runAction = new Task(timer);
timer.scheduleAtFixedRate(runAction, STEP_DELAY, STEP_DELAY);
}
public void suspend() {
if (timer == null)
return;
timer.cancel();
timer = null;
}
}
class Task extends TimerTask {
private Timer timer;
public Task(Timer timer) {
this.timer = timer;
}
public void run() {
org.eclipse.swt.widgets.Display.getDefault().syncExec(
new Runnable() {
public void run() {
if (!isEnabled())
timer.cancel();
fireActionPerformed();
}
});
}
}
}