| /******************************************************************************* |
| * Copyright (c) 2012 Laurent CARON |
| * 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: |
| * Laurent CARON (laurent.caron at gmail dot com) - initial API and implementation |
| *******************************************************************************/ |
| package org.mihalis.opal.obutton; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| 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.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Canvas; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Widget; |
| |
| /** |
| * Instances of this class represent a selectable user interface object that issues notification when pressed and released. |
| * <dl> |
| * <dt><b>Styles:</b></dt> |
| * <dd>ARROW, PUSH, TOGGLE</dd> |
| * <dd>UP, DOWN, LEFT, RIGHT, CENTER</dd> |
| * <dt><b>Events:</b></dt> |
| * <dd>Selection</dd> |
| * </dl> |
| * <p> |
| * Note: Only one of the styles ARROW, PUSH and TOGGLE may be specified. |
| * </p> |
| * <p> |
| * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified. |
| * </p> |
| * <p> |
| * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified when the ARROW style is specified. |
| * </p> |
| */ |
| public class OButton extends Canvas { |
| |
| /** The Constant IS_BUTTON_PRESSED. */ |
| private static final String IS_BUTTON_PRESSED = OButton.class.toString() + "_pressed"; |
| |
| /** The selection listeners. */ |
| private final List<SelectionListener> selectionListeners; |
| |
| /** The alignment. */ |
| int alignment = SWT.LEFT; |
| |
| /** The button renderer. */ |
| private ButtonRenderer buttonRenderer; |
| |
| /** The selected. */ |
| boolean selected; |
| |
| /** The hover. */ |
| boolean hover; |
| |
| /** The text. */ |
| String text; |
| |
| /** The image. */ |
| Image image; |
| |
| /** The width. */ |
| private int width; |
| |
| /** The height. */ |
| private int height; |
| |
| /** The clicked. */ |
| private boolean clicked; |
| |
| /** |
| * Constructs a new instance of this class given its parent and a style |
| * value describing its behavior and appearance. |
| * <p> |
| * The style value is either one of the style constants defined in class |
| * <code>SWT</code> which is applicable to instances of this class, or must |
| * be built by <em>bitwise OR</em>'ing together (that is, using the |
| * <code>int</code> "|" operator) two or more of those <code>SWT</code> |
| * style constants. The class description lists the style constants that are |
| * applicable to the class. Style bits are also inherited from superclasses. |
| * </p> |
| * |
| * @param parent a composite control which will be the parent of the new |
| * instance (cannot be null) |
| * @param style the style of control to construct |
| * @see SWT#ARROW |
| * @see SWT#CHECK |
| * @see SWT#PUSH |
| * @see SWT#RADIO |
| * @see SWT#TOGGLE |
| * @see SWT#FLAT |
| * @see SWT#UP |
| * @see SWT#DOWN |
| * @see SWT#LEFT |
| * @see SWT#RIGHT |
| * @see SWT#CENTER |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
| * thread that created the parent</li> |
| * <li>ERROR_INVALID_SUBCLASS - if this class is not an |
| * allowed subclass</li> |
| * </ul> |
| */ |
| public OButton(final Composite parent, final int style) { |
| super(parent, checkStyle(style) | SWT.DOUBLE_BUFFERED); |
| this.selectionListeners = new ArrayList<SelectionListener>(); |
| this.buttonRenderer = DefaultButtonRenderer.getInstance(); |
| this.width = this.height = -1; |
| addListeners(); |
| } |
| |
| /** |
| * Check style. |
| * |
| * @param style the style |
| * @return the int |
| */ |
| private static int checkStyle(int style) { |
| style = checkBits(style, SWT.PUSH, SWT.ARROW, SWT.TOGGLE, 0); |
| if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) { |
| return checkBits(style, SWT.CENTER, SWT.LEFT, SWT.RIGHT, 0); |
| } |
| if ((style & SWT.ARROW) != 0) { |
| return checkBits(style, SWT.UP, SWT.DOWN, SWT.LEFT, SWT.RIGHT); |
| } |
| return style; |
| } |
| |
| /** |
| * Check bits. |
| * |
| * @param style the style |
| * @param int0 the int0 |
| * @param int1 the int1 |
| * @param int2 the int2 |
| * @param int3 the int3 |
| * @return the int |
| */ |
| private static int checkBits(int style, final int int0, final int int1, final int int2, final int int3) { |
| final int mask = int0 | int1 | int2 | int3; |
| if ((style & mask) == 0) { |
| style |= int0; |
| } |
| if ((style & int0) != 0) { |
| style = style & ~mask | int0; |
| } |
| if ((style & int1) != 0) { |
| style = style & ~mask | int1; |
| } |
| if ((style & int2) != 0) { |
| style = style & ~mask | int2; |
| } |
| if ((style & int3) != 0) { |
| style = style & ~mask | int3; |
| } |
| return style; |
| } |
| |
| /** |
| * Adds the listeners. |
| */ |
| private void addListeners() { |
| final Listener listener = new Listener() { |
| |
| @Override |
| public void handleEvent(final Event event) { |
| switch (event.type) { |
| case SWT.MouseEnter: |
| OButton.this.hover = true; |
| redraw(); |
| update(); |
| break; |
| case SWT.MouseExit: |
| OButton.this.hover = false; |
| redraw(); |
| update(); |
| break; |
| case SWT.MouseDown: |
| OButton.this.clicked = true; |
| OButton.this.setData(IS_BUTTON_PRESSED, "*"); |
| redraw(); |
| update(); |
| break; |
| case SWT.MouseUp: |
| if (OButton.this.getData(IS_BUTTON_PRESSED) == null) { |
| return; |
| } |
| OButton.this.setData(IS_BUTTON_PRESSED, null); |
| OButton.this.clicked = false; |
| OButton.this.selected = !OButton.this.selected; |
| redraw(); |
| update(); |
| fireSelectionEvent(); |
| break; |
| case SWT.Paint: |
| handlePaintEvent(event); |
| break; |
| case SWT.KeyDown: |
| if (event.keyCode == SWT.TAB) { |
| if (event.stateMask == SWT.SHIFT) { |
| OButton.this.traverse(SWT.TRAVERSE_TAB_PREVIOUS); |
| } else { |
| OButton.this.traverse(SWT.TRAVERSE_TAB_NEXT); |
| } |
| } |
| break; |
| case SWT.Dispose: |
| OButton.this.buttonRenderer.dispose(); |
| break; |
| |
| } |
| |
| } |
| }; |
| |
| final int[] events = new int[] { SWT.MouseEnter, SWT.MouseExit, SWT.MouseDown, SWT.MouseUp, SWT.Paint, SWT.Dispose }; |
| for (final int event : events) { |
| addListener(event, listener); |
| } |
| } |
| |
| /** |
| * Fire selection event. |
| */ |
| protected void fireSelectionEvent() { |
| final Event event = new Event(); |
| event.widget = this; |
| event.display = getDisplay(); |
| event.type = SWT.Selection; |
| for (final SelectionListener selectionListener : this.selectionListeners) { |
| selectionListener.widgetSelected(new SelectionEvent(event)); |
| } |
| } |
| |
| /** |
| * Handle paint event. |
| * |
| * @param event the event |
| */ |
| private void handlePaintEvent(final Event event) { |
| if (!isEnabled()) { |
| this.buttonRenderer.drawButtonWhenDisabled(event.gc, this); |
| return; |
| } |
| |
| if (this.clicked) { |
| this.buttonRenderer.drawButtonWhenClicked(event.gc, this); |
| return; |
| } |
| |
| if (this.hover) { |
| this.buttonRenderer.drawButtonWhenMouseHover(event.gc, this); |
| return; |
| } |
| |
| final boolean isToggleButton = (getStyle() & SWT.TOGGLE) == SWT.TOGGLE; |
| if (isToggleButton && this.selected) { |
| this.buttonRenderer.drawButtonWhenSelected(event.gc, this); |
| return; |
| } |
| |
| this.buttonRenderer.drawButton(event.gc, this); |
| } |
| |
| /** |
| * Adds the listener to the collection of listeners who will be notified |
| * when the control is selected by the user, by sending it one of the |
| * messages defined in the <code>SelectionListener</code> interface. |
| * <p> |
| * <code>widgetSelected</code> is called when the control is selected by the |
| * user. <code>widgetDefaultSelected</code> is not called. |
| * </p> |
| * |
| * @param listener the listener which should be notified |
| * @see SelectionListener |
| * @see #removeSelectionListener |
| * @see SelectionEvent |
| * @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> |
| */ |
| public void addSelectionListener(final SelectionListener listener) { |
| checkWidget(); |
| if (listener == null) { |
| SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| } |
| this.selectionListeners.add(listener); |
| } |
| |
| /** |
| * Compute size. |
| * |
| * @param wHint the w hint |
| * @param hHint the h hint |
| * @param changed the changed |
| * @return the point |
| * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean) |
| */ |
| @Override |
| public Point computeSize(final int wHint, final int hHint, final boolean changed) { |
| checkWidget(); |
| final Point computedSize = this.buttonRenderer.computeSize(this, wHint, hHint, changed); |
| |
| if (wHint != SWT.DEFAULT) { |
| computedSize.x = wHint; |
| } |
| if (hHint != SWT.DEFAULT) { |
| computedSize.y = hHint; |
| } |
| setWidth(computedSize.x); |
| setHeight(computedSize.y); |
| return computedSize; |
| } |
| |
| /** |
| * Returns a value which describes the position of the text or image in the receiver. The value will be one of <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code> unless the receiver is an <code>ARROW</code> button, in which case, the |
| * alignment will indicate the direction of the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, <code>UP</code> or <code>DOWN</code>). |
| * |
| * @return the alignment |
| * |
| * @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 getAlignment() { |
| checkWidget(); |
| return this.alignment; |
| } |
| |
| /** |
| * Gets the button renderer. |
| * |
| * @return the associated button renderer |
| */ |
| public ButtonRenderer getButtonRenderer() { |
| checkWidget(); |
| return this.buttonRenderer; |
| } |
| |
| /** |
| * Returns the whole height of the widget. |
| * |
| * @return the receiver's height |
| * |
| * @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 getHeight() { |
| checkWidget(); |
| if (this.height == -1) { |
| return this.computeSize(SWT.DEFAULT, SWT.DEFAULT, false).y; |
| } |
| return this.height; |
| } |
| |
| /** |
| * Returns the receiver's image if it has one, or null if it does not. |
| * |
| * @return the receiver's image |
| * |
| * @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 Image getImage() { |
| checkWidget(); |
| return this.image; |
| } |
| |
| /** |
| * Returns <code>true</code> if the receiver is selected, and false otherwise. |
| * |
| * @return the selection state |
| * |
| * @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 getSelection() { |
| checkWidget(); |
| return this.selected; |
| } |
| |
| /** |
| * Returns the receiver's text, which will be an empty string if it has never been set or if the receiver is an <code>ARROW</code> button. |
| * |
| * @return the receiver's text |
| * |
| * @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 getText() { |
| checkWidget(); |
| return this.text; |
| } |
| |
| /** |
| * Returns the whole width of the widget. |
| * |
| * @return the receiver's height |
| * |
| * @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 getWidth() { |
| checkWidget(); |
| if (this.width == -1) { |
| return computeSize(SWT.DEFAULT, SWT.DEFAULT, false).x; |
| } |
| return this.width; |
| } |
| |
| /** |
| * Removes the listener from the collection of listeners who will be |
| * notified when the control is selected by the user. |
| * |
| * @param listener the listener which should no longer be notified |
| * @see SelectionListener |
| * @see #addSelectionListener |
| * @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> |
| */ |
| public void removeSelectionListener(final SelectionListener listener) { |
| checkWidget(); |
| if (listener == null) { |
| SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| } |
| this.selectionListeners.remove(listener); |
| } |
| |
| /** |
| * Controls how text, images and arrows will be displayed in the receiver. The argument should be one of <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code> unless the receiver is an <code>ARROW</code> button, in which case, the argument |
| * indicates the direction of the arrow (one of <code>LEFT</code>, <code>RIGHT</code>, <code>UP</code> or <code>DOWN</code>). |
| * |
| * @param alignment the new alignment |
| * |
| * @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 setAlignment(final int alignment) { |
| checkWidget(); |
| if (alignment != SWT.LEFT && alignment != SWT.RIGHT && alignment != SWT.CENTER) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| this.alignment = alignment; |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the button renderer. |
| * |
| * @param buttonRenderer the button renderer to set |
| */ |
| public void setButtonRenderer(final ButtonRenderer buttonRenderer) { |
| this.buttonRenderer = buttonRenderer; |
| } |
| |
| /** |
| * Sets the height of the receiver. |
| * <p> |
| * Note: Attempting to set the width or height of the receiver to a negative number will cause that value to be set to zero instead. |
| * </p> |
| * |
| * @param height the new width |
| * |
| * @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 setHeight(final int height) { |
| checkWidget(); |
| this.height = Math.max(height, 0); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the receiver's image to the argument, which may be <code>null</code> indicating that no image should be displayed. |
| * |
| * @param image the image to display on the receiver (may be <code>null</code>) |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</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 setImage(final Image image) { |
| checkWidget(); |
| if (image != null && image.isDisposed()) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| this.image = image; |
| this.buttonRenderer.createDisabledImage(); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the selection state of the receiver, if it is of type <code>TOGGLE</code>. |
| * |
| * @param selected the new selection state |
| * |
| * @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 setSelection(final boolean selected) { |
| checkWidget(); |
| this.selected = selected; |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the receiver's text. |
| * |
| * @param string the new text |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the text 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 setText(final String string) { |
| checkWidget(); |
| this.text = string; |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the width of the receiver. |
| * <p> |
| * Note: Attempting to set the width or height of the receiver to a negative number will cause that value to be set to zero instead. |
| * </p> |
| * |
| * @param width the new width |
| * |
| * @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 setWidth(final int width) { |
| checkWidget(); |
| this.width = Math.max(0, width); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the enabled. |
| * |
| * @param enabled the new enabled |
| * @see org.eclipse.swt.widgets.Control#setEnabled(boolean) |
| */ |
| @Override |
| public void setEnabled(final boolean enabled) { |
| super.setEnabled(enabled); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the size. |
| * |
| * @param width the width |
| * @param height the height |
| * @see org.eclipse.swt.widgets.Control#setSize(int, int) |
| */ |
| @Override |
| public void setSize(final int width, final int height) { |
| super.setSize(width, height); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the size. |
| * |
| * @param size the new size |
| * @see org.eclipse.swt.widgets.Control#setSize(org.eclipse.swt.graphics.Point) |
| */ |
| @Override |
| public void setSize(final Point size) { |
| super.setSize(size); |
| redraw(); |
| update(); |
| } |
| |
| /** |
| * Sets the visible. |
| * |
| * @param visible the new visible |
| * @see org.eclipse.swt.widgets.Control#setVisible(boolean) |
| */ |
| @Override |
| public void setVisible(final boolean visible) { |
| super.setVisible(visible); |
| redraw(); |
| update(); |
| } |
| |
| } |