/******************************************************************************* * Copyright (c) 2000, 2010, 2012 IBM Corporation, Gerhardt Informatics Kft. 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 * Gerhardt Informatics Kft. - GEFGWT port *******************************************************************************/ package org.eclipse.draw2d; import java.util.Iterator; import org.eclipse.draw2d.geometry.Rectangle; /** * A Clickable responds to mouse clicks in some way (determined by a * ClickBehavior) and fires action events. No visual appearance of feedback is * offered. Depends on a model holder and an event handler which understands the * model and updates the model accordingly. {@link ButtonModel} is used by * default. Any figure can be set as contents to a Clickable. * Clickable->EventHandler->Model->ModelObserver->Listeners of actions. */ public class Clickable extends Figure { private static final int ROLLOVER_ENABLED_FLAG = Figure.MAX_FLAG << 1, STYLE_BUTTON_FLAG = Figure.MAX_FLAG << 2, STYLE_TOGGLE_FLAG = Figure.MAX_FLAG << 3; /** * The highest reserved flag used by this class */ protected static int MAX_FLAG = STYLE_TOGGLE_FLAG; /** * Style constant that defines a push button. The button will be pressed * when the mouse is pressed on top of it. The button will be released when * the mouse is released or is move off of the button. * */ public static final int STYLE_BUTTON = STYLE_BUTTON_FLAG; /** * Style constant that defines a toggle button. The button will toggle * between 2 states when the mouse is clicked on the button. */ public static final int STYLE_TOGGLE = STYLE_TOGGLE_FLAG; /** * An action is performed every time the mouse is released. */ public static final int DEFAULT_FIRING = 0; /** * Firing starts as soon as the mouse is pressed on this Clickable, and * keeps firing at prefixed intervals until the mouse is released. */ public static final int REPEAT_FIRING = 1; /** * Observes the model for action and state changes. * * @see ActionListener * @see ChangeListener */ static interface ModelObserver extends ActionListener, ChangeListener { } private ClickableEventHandler eventHandler; private ButtonModel model; private ModelObserver modelObserver; { init(); setRequestFocusEnabled(true); setFocusTraversable(true); } /** * Constructs a Clickable with no contents. */ public Clickable() { } /** * Constructs a Clickable whose contents are provided as input. The content * figure occupies the entire region of the Clickable. * * @param contents * The content figure */ public Clickable(IFigure contents) { this(contents, 0); } /** * Constructs a Clickable whose contents are provided as input. The content * figure occupies the entire region of the Clickable. Sets the style to the * given <i>style</i> (either {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE} * ). * * @param contents * The content figure * @param style * The button style */ public Clickable(IFigure contents, int style) { setContents(contents); setStyle(style); } /** * Adds the given listener to the list of action listeners of this Figure. * Listener is called whenever an action is performed. * * @param listener * The ActionListener to be added * @since 2.0 */ public void addActionListener(ActionListener listener) { addListener(ActionListener.class, listener); } /** * Adds the given listener to the list of state change listeners of this * figure. A ChangeListener is informed if there is any state change in the * model requiring action by the listener. * * @param listener * The ChangeListener to be added * @since 2.0 */ public void addChangeListener(ChangeListener listener) { addListener(ChangeListener.class, listener); } /** * Returns a newly created ButtonModel as the default model to be used by * this Clickable based on the button style. * * @return The model to be used by default * @since 2.0 */ protected ButtonModel createDefaultModel() { if (isStyle(STYLE_TOGGLE)) return new ToggleModel(); else return new ButtonModel(); } /** * Returns a newly created event handler for this Clickable and its model. * * @return The event handler * @since 2.0 */ protected ClickableEventHandler createEventHandler() { return new ClickableEventHandler(); } /** * Returns a newly created model observer which listens to the model, and * fires any action or state changes. A ModelObserver holds both an action * listener and a state change listener. * * @return The newly created model observer * @since 2.0 */ protected ModelObserver createModelObserver() { return new ModelObserver() { public void actionPerformed(ActionEvent action) { fireActionPerformed(); } public void handleStateChanged(ChangeEvent change) { fireStateChanged(change); } }; } /** * Fires an action performed event. * * @since 2.0 */ public void doClick() { fireActionPerformed(); } /** * Called when there has been an action performed by this Clickable, which * is determined by the model. Notifies all ActionListener type listeners of * an action performed. * * @since 2.0 */ protected void fireActionPerformed() { ActionEvent action = new ActionEvent(this); Iterator listeners = getListeners(ActionListener.class); while (listeners.hasNext()) ((ActionListener) listeners.next()) // Leave newline for debug // stepping .actionPerformed(action); } /** * Called when there has been a change of state in the model of this * clickable. Notifies all ChangeListener type listeners of the state * change. * * @param modelChange * The ChangeEvent * @since 2.0 */ protected void fireStateChanged(ChangeEvent modelChange) { ChangeEvent change = new ChangeEvent(this, modelChange.getPropertyName()); Iterator listeners = getListeners(ChangeListener.class); while (listeners.hasNext()) ((ChangeListener) listeners.next()) // Leave newline for debug // stepping .handleStateChanged(change); } /** * Returns the behavior model used by this Clickable. * * @return The model used by this Clickable * @since 2.0 */ public ButtonModel getModel() { return model; } /** * Adds the given ClickableEventHandler to this clickable. A * ClickableEventHandler should be a MouseListener, MouseMotionListener, * ChangeListener, KeyListener, and FocusListener. * * @param handler * The new event handler * @since 2.0 */ protected void hookEventHandler(ClickableEventHandler handler) { if (handler == null) return; addMouseListener(handler); addMouseMotionListener(handler); addChangeListener(handler); addKeyListener(handler); addFocusListener(handler); } /** * Initializes this Clickable by setting a default model and adding a * clickable event handler for that model. * * @since 2.0 */ protected void init() { setModel(createDefaultModel()); setEventHandler(createEventHandler()); } /** * Returns <code>true</code> if rollover feedback is enabled. * * @return <code>true</code> rollover feedback is enabled * @since 2.0 */ public boolean isRolloverEnabled() { return (flags & ROLLOVER_ENABLED_FLAG) != 0; } /** * Returns <code>true</code> if this Clickable is in a selected state. The * model is the one which holds all this state based information. * * @return <code>true</code> if this Clickable is in a selected state * @since 2.0 */ public boolean isSelected() { return getModel().isSelected(); } /** * Returns <code>true</code> if this Clickable's style is the same as the * passed style. * * @param style * The style to be checked * @return <code>true</code> if this Clickable's style is the same as the * passed style * @since 2.0 */ public boolean isStyle(int style) { return ((style & flags) == style); } /** * If this Clickable has focus, this method paints a focus rectangle. * * @param graphics * Graphics handle for painting */ protected void paintBorder(Graphics graphics) { super.paintBorder(graphics); if (hasFocus()) { graphics.setForegroundColor(ColorConstants.black); graphics.setBackgroundColor(ColorConstants.white); Rectangle area = getClientArea(); if (isStyle(STYLE_BUTTON)) graphics.drawFocus(area.x, area.y, area.width, area.height); else graphics.drawFocus(area.x, area.y, area.width - 1, area.height - 1); } } /** * Paints the area of this figure excluded by the borders. Induces a (1,1) * pixel shift in the painting if the mouse is armed, giving it the pressed * appearance. * * @param graphics * Graphics handle for painting * @since 2.0 */ protected void paintClientArea(Graphics graphics) { if (isStyle(STYLE_BUTTON) && (getModel().isArmed() || getModel().isSelected())) { graphics.translate(1, 1); graphics.pushState(); super.paintClientArea(graphics); graphics.popState(); graphics.translate(-1, -1); } else super.paintClientArea(graphics); } /** * Removes the given listener from the list of ActionListener's of this * Clickable. * * @param listener * Listener to be removed from this figure * @since 2.0 */ public void removeActionListener(ActionListener listener) { removeListener(ActionListener.class, listener); } /** * Removes the given listener from the list of ChangeListener's of this * clickable. * * @param listener * Listener to be removed from this figure * @since 2.0 */ public void removeChangeListener(ChangeListener listener) { removeListener(ChangeListener.class, listener); } /** * Sets the Figure which is the contents of this Clickable. This Figure * occupies the entire clickable region. * * @param contents * Contents of the clickable * @since 2.0 */ protected void setContents(IFigure contents) { setLayoutManager(new StackLayout()); if (getChildren().size() > 0) remove((IFigure) getChildren().get(0)); add(contents); } /** * @see org.eclipse.draw2d.IFigure#setEnabled(boolean) */ public void setEnabled(boolean value) { if (isEnabled() == value) return; super.setEnabled(value); getModel().setEnabled(value); setChildrenEnabled(value); } /** * Sets the event handler which interacts with the model to determine the * behavior of this Clickable. * * @param h * Event handler for this clickable * @since 2.0 */ public void setEventHandler(ClickableEventHandler h) { if (eventHandler != null) unhookEventHandler(eventHandler); eventHandler = h; if (eventHandler != null) hookEventHandler(eventHandler); } /** * Determines how this clickable is to fire notifications to its listeners. * In the default firing method ({@link #DEFAULT_FIRING}), an action is * performed every time the mouse is released. In the repeat firing method ( * {@link #REPEAT_FIRING}), firing starts as soon as it is pressed on this * clickable, and keeps firing at prefixed intervals until the mouse is * released. * * @param type * Type of firing * @since 2.0 */ public void setFiringMethod(int type) { getModel().setFiringBehavior(type); } /** * Sets the model to be used by this clickable for its state and behavior * determination. This clickable removes any observers from the previous * model before adding new ones to the new model. * * @param model * The new model of this Clickable * @since 2.0 */ public void setModel(ButtonModel model) { if (this.model != null) { this.model.removeChangeListener(modelObserver); this.model.removeActionListener(modelObserver); modelObserver = null; } this.model = model; if (model != null) { modelObserver = createModelObserver(); model.addActionListener(modelObserver); model.addChangeListener(modelObserver); } } /** * Enables or disables rollover feedback of this figure. Generally used in * conjunction with the model to determine if feedback is to be shown. * * @param value * The rollover state to be set * @since 2.0 */ public void setRolloverEnabled(boolean value) { if (isRolloverEnabled() == value) return; setFlag(ROLLOVER_ENABLED_FLAG, value); repaint(); } /** * Sets the selected state of this Clickable. Since the model is responsible * for all state based information, it is informed of the state change. * Extending classes can choose selection information, if they do not * represent any selection. * * @param value * New selected state of this clickable. * @see #isSelected() * @since 2.0 */ public void setSelected(boolean value) { getModel().setSelected(value); } /** * Sets this Clickable's style to the passed value, either * {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE}. * * @param style * The button style * @since 2.0 */ public void setStyle(int style) { if ((style & STYLE_BUTTON) != 0) { setFlag(STYLE_BUTTON_FLAG, true); if (!(getBorder() instanceof ButtonBorder)) setBorder(new ButtonBorder()); setOpaque(true); } else { setFlag(STYLE_BUTTON_FLAG, false); setOpaque(false); } if ((style & STYLE_TOGGLE) != 0) { setFlag(STYLE_TOGGLE_FLAG, true); setModel(createDefaultModel()); } } /** * Removes the given ClickableEventHandler containing listeners from this * Clickable. * * @param handler * The event handler to be removed * @since 2.0 */ protected void unhookEventHandler(ClickableEventHandler handler) { if (handler == null) return; removeMouseListener(handler); removeMouseMotionListener(handler); removeChangeListener(handler); } }