/* @ITMillApache2LicenseForJavaFiles@ */ package com.vaadin.ui; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Map; import com.vaadin.data.Property; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; /** * A generic button component. * * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ @SuppressWarnings("serial") public class Button extends AbstractField { /* Private members */ boolean switchMode = false; /** * Creates a new push button. The value of the push button is false and it * is immediate by default. * */ public Button() { setValue(new Boolean(false)); setSwitchMode(false); } /** * Creates a new push button. * * The value of the push button is false and it is immediate by default. * * @param caption * the Button caption. */ public Button(String caption) { this(); setCaption(caption); } /** * Creates a new push button with click listener. * * @param caption * the Button caption. * @param listener * the Button click listener. */ public Button(String caption, ClickListener listener) { this(caption); addListener(listener); } /** * Creates a new push button with a method listening button clicks. Using * this method is discouraged because it cannot be checked during * compilation. Use * {@link #Button(String, com.vaadin.ui.Button.ClickListener)} * instead. The method must have either no parameters, or only one parameter * of Button.ClickEvent type. * * @param caption * the Button caption. * @param target * the Object having the method for listening button clicks. * @param methodName * the name of the method in target object, that receives button * click events. */ public Button(String caption, Object target, String methodName) { this(caption); addListener(ClickEvent.class, target, methodName); } /** * Creates a new switch button with initial value. * * @param state * the Initial state of the switch-button. * @param initialState */ public Button(String caption, boolean initialState) { setCaption(caption); setValue(new Boolean(initialState)); setSwitchMode(true); } /** * Creates a new switch button that is connected to a boolean property. * * @param state * the Initial state of the switch-button. * @param dataSource */ public Button(String caption, Property dataSource) { setCaption(caption); setSwitchMode(true); setPropertyDataSource(dataSource); } /** * Gets component UIDL tag. * * @return the Component UIDL tag as string. */ @Override public String getTag() { return "button"; } /** * Paints the content of this component. * * @param event * the PaintEvent. * @throws IOException * if the writing failed due to input/output error. * @throws PaintException * if the paint operation failed. */ @Override public void paintContent(PaintTarget target) throws PaintException { super.paintContent(target); if (isSwitchMode()) { target.addAttribute("type", "switch"); } target.addVariable(this, "state", booleanValue()); } /** * Invoked when the value of a variable has changed. Button listeners are * notified if the button is clicked. * * @param source * @param variables */ @Override public void changeVariables(Object source, Map variables) { super.changeVariables(source, variables); if (!isReadOnly() && variables.containsKey("state")) { // Gets the new and old button states final Boolean newValue = (Boolean) variables.get("state"); final Boolean oldValue = (Boolean) getValue(); if (isSwitchMode()) { // For switch button, the event is only sent if the // switch state is changed if (newValue != null && !newValue.equals(oldValue) && !isReadOnly()) { setValue(newValue); fireClick(); } } else { // Only send click event if the button is pushed if (newValue.booleanValue()) { fireClick(); } // If the button is true for some reason, release it if (oldValue.booleanValue()) { setValue(new Boolean(false)); } } } } /** * Checks if it is switchMode. * * @return true if it is in Switch Mode, otherwise * false. */ public boolean isSwitchMode() { return switchMode; } /** * Sets the switchMode. * * @param switchMode * The switchMode to set. */ public void setSwitchMode(boolean switchMode) { this.switchMode = switchMode; if (!switchMode) { setImmediate(true); if (booleanValue()) { setValue(new Boolean(false)); } } } /** * Get the boolean value of the button state. * * @return True iff the button is pressed down or checked. */ public boolean booleanValue() { boolean state; try { state = ((Boolean) getValue()).booleanValue(); } catch (final NullPointerException e) { state = false; } return state; } /** * Sets immediate mode. Push buttons can not be set in non-immediate mode. * * @see com.vaadin.ui.AbstractComponent#setImmediate(boolean) */ @Override public void setImmediate(boolean immediate) { // Push buttons are always immediate super.setImmediate(!isSwitchMode() || immediate); } /** * The type of the button as a property. * * @see com.vaadin.data.Property#getType() */ @Override public Class getType() { return Boolean.class; } /* Click event */ private static final Method BUTTON_CLICK_METHOD; /* Button style with no decorations. Looks like a link, acts like a button */ public static final String STYLE_LINK = "link"; static { try { BUTTON_CLICK_METHOD = ClickListener.class.getDeclaredMethod( "buttonClick", new Class[] { ClickEvent.class }); } catch (final java.lang.NoSuchMethodException e) { // This should never happen throw new java.lang.RuntimeException( "Internal error finding methods in Button"); } } /** * Click event. This event is thrown, when the button is clicked. * * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ public class ClickEvent extends Component.Event { /** * New instance of text change event. * * @param source * the Source of the event. */ public ClickEvent(Component source) { super(source); } /** * Gets the Button where the event occurred. * * @return the Source of the event. */ public Button getButton() { return (Button) getSource(); } } /** * Button click listener * * @author IT Mill Ltd. * @version * @VERSION@ * @since 3.0 */ public interface ClickListener extends Serializable { /** * Button has been pressed. * * @param event * Button click event. */ public void buttonClick(ClickEvent event); } /** * Adds the button click listener. * * @param listener * the Listener to be added. */ public void addListener(ClickListener listener) { addListener(ClickEvent.class, listener, BUTTON_CLICK_METHOD); } /** * Removes the button click listener. * * @param listener * the Listener to be removed. */ public void removeListener(ClickListener listener) { removeListener(ClickEvent.class, listener, BUTTON_CLICK_METHOD); } /** * Emits the options change event. */ protected void fireClick() { fireEvent(new Button.ClickEvent(this)); } @Override protected void setInternalValue(Object newValue) { // Make sure only booleans get through if (!(newValue instanceof Boolean)) { throw new IllegalArgumentException(getClass().getSimpleName() + " only accepts Boolean values"); } super.setInternalValue(newValue); } }