| /******************************************************************************* |
| * Copyright (c) 2011 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@gmail.com) - initial API and implementation |
| *******************************************************************************/ |
| package org.mihalis.opal.angles; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.GC; |
| 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.mihalis.opal.utils.SWTGraphicUtil; |
| |
| /** |
| * Instances of this class provide a selectable user interface object that can |
| * be used to pick angles. Inspired by the Swing AngleSlider by Jeremy |
| * (http://javagraphics.blogspot.com/2008/05/angles-need-gui-widget-for-angles. |
| * html) |
| * <p> |
| * <dl> |
| * <dt><b>Styles:</b></dt> |
| * <dd>BORDER</dd> |
| * <dt><b>Events:</b></dt> |
| * <dd>Selection</dd> |
| * </dl> |
| */ |
| public class AngleSlider extends Canvas { |
| |
| /** The Constant WHOLE_RADIUS. */ |
| private static final int WHOLE_RADIUS = 40; |
| |
| /** The Constant BUTTON_RADIUS. */ |
| private static final int BUTTON_RADIUS = 10; |
| |
| /** The Constant STEP. */ |
| private static final int STEP = 5; |
| |
| /** The background image. */ |
| private final Image backgroundImage; |
| |
| /** The button focus. */ |
| private final Image buttonFocus; |
| |
| /** The button no focus. */ |
| private final Image buttonNoFocus; |
| |
| /** The selection. */ |
| private int selection; |
| |
| /** The selection listeners. */ |
| private final List<SelectionListener> selectionListeners; |
| |
| /** The mouse pressed. */ |
| private boolean mousePressed; |
| |
| /** |
| * Constructs a new instance of this class given its parent. |
| * |
| * @param parent a widget which will be the parent of the new instance |
| * (cannot be null) |
| * @param style not used |
| * |
| * @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> |
| * </ul> |
| */ |
| public AngleSlider(final Composite parent, final int style) { |
| super(parent, style | SWT.DOUBLE_BUFFERED); |
| |
| final ClassLoader loader = org.mihalis.opal.angles.AngleSlider.class.getClassLoader(); |
| |
| backgroundImage = new Image(getDisplay(), loader.getResourceAsStream("images/angleBackground.png")); |
| buttonFocus = new Image(getDisplay(), loader.getResourceAsStream("images/angleButtonFocus.png")); |
| buttonNoFocus = new Image(getDisplay(), loader.getResourceAsStream("images/angleButtonFocusLost.png")); |
| |
| addListeners(); |
| |
| selection = 0; |
| selectionListeners = new ArrayList<SelectionListener>(); |
| } |
| |
| /** |
| * Add listeners. |
| */ |
| private void addListeners() { |
| addListener(SWT.Paint, new Listener() { |
| |
| @Override |
| public void handleEvent(final Event event) { |
| paintControl(event); |
| } |
| }); |
| |
| addDisposeListener(new DisposeListener() { |
| |
| @Override |
| public void widgetDisposed(final DisposeEvent arg0) { |
| SWTGraphicUtil.safeDispose(backgroundImage); |
| SWTGraphicUtil.safeDispose(buttonFocus); |
| SWTGraphicUtil.safeDispose(buttonNoFocus); |
| } |
| }); |
| |
| final int[] listeners = new int[] { SWT.MouseDown, SWT.MouseUp, SWT.MouseMove }; |
| |
| for (final int listener : listeners) { |
| addListener(listener, createMouseListener()); |
| } |
| addListener(SWT.KeyDown, createKeyListener()); |
| |
| } |
| |
| /** |
| * Paint control. |
| * |
| * @param event the event |
| */ |
| private void paintControl(final Event event) { |
| final GC gc = event.gc; |
| |
| gc.drawImage(backgroundImage, 0, 0); |
| |
| float angle = selection / 360f; |
| angle = (float) (angle * 2 * Math.PI - 0.5 * Math.PI); |
| |
| final float centerX = WHOLE_RADIUS / 2f; |
| final float centerY = WHOLE_RADIUS / 2f; |
| final float radius = BUTTON_RADIUS; |
| final float x = (float) (centerX - radius * Math.cos(angle)); |
| final float y = (float) (centerY - radius * Math.sin(angle)); |
| |
| if (isFocusControl()) { |
| gc.drawImage(buttonFocus, (int) x - 2, (int) y - 2); |
| } else { |
| gc.drawImage(buttonNoFocus, (int) x - 2, (int) y - 2); |
| } |
| |
| if (!isEnabled()) { |
| gc.setAlpha(127); |
| gc.setAntialias(SWT.ON); |
| gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); |
| gc.fillOval(4, 4, WHOLE_RADIUS - 7, WHOLE_RADIUS - 7); |
| } |
| } |
| |
| /** |
| * Creates the mouse listener. |
| * |
| * @return the listener |
| */ |
| private Listener createMouseListener() { |
| return new Listener() { |
| |
| @Override |
| public void handleEvent(final Event event) { |
| if (!isEnabled()) { |
| return; |
| } |
| |
| if (event.type == SWT.MouseDown) { |
| mousePressed = true; |
| } |
| if (event.type == SWT.MouseDown || event.type == SWT.MouseMove && mousePressed) { |
| final float deltaX = event.x - WHOLE_RADIUS / 2f; |
| final float deltaY = event.y - WHOLE_RADIUS / 2f; |
| final double angle = Math.atan2(deltaX, deltaY); |
| selection = 360 - (int) (360 * angle / (2 * Math.PI) + 360) % 360; |
| |
| redraw(); |
| } |
| if (event.type == SWT.MouseUp) { |
| mousePressed = false; |
| fireSelectionListeners(event); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Fire selection listeners. |
| * |
| * @param event the event |
| */ |
| private void fireSelectionListeners(final Event event) { |
| for (final SelectionListener selectionListener : selectionListeners) { |
| selectionListener.widgetSelected(new SelectionEvent(event)); |
| } |
| } |
| |
| /** |
| * Creates the key listener. |
| * |
| * @return the listener |
| */ |
| private Listener createKeyListener() { |
| return new Listener() { |
| |
| @Override |
| public void handleEvent(final Event event) { |
| if (!isEnabled()) { |
| return; |
| } |
| if (event.type != SWT.KeyDown) { |
| return; |
| } |
| if (event.keyCode == SWT.ARROW_UP || event.keyCode == SWT.ARROW_LEFT) { |
| setSelection(selection + STEP); |
| } |
| if (event.keyCode == SWT.ARROW_DOWN || event.keyCode == SWT.ARROW_RIGHT) { |
| setSelection(selection - STEP); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Adds the selection listener. |
| * |
| * @param selectionListener the selection listener |
| * @see org.eclipse.swt.widgets.Scale#addSelectionListener(org.eclipse.swt.events.SelectionListener) |
| */ |
| public void addSelectionListener(final SelectionListener selectionListener) { |
| checkWidget(); |
| selectionListeners.add(selectionListener); |
| } |
| |
| /** |
| * 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(); |
| return new Point(WHOLE_RADIUS, WHOLE_RADIUS); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.widgets.Widget#dispose() |
| */ |
| @Override |
| public void dispose() { |
| super.dispose(); |
| backgroundImage.dispose(); |
| buttonFocus.dispose(); |
| buttonNoFocus.dispose(); |
| } |
| |
| /** |
| * Gets the selection. |
| * |
| * @return the selection |
| * @see org.eclipse.swt.widgets.Scale#getSelection() |
| */ |
| public int getSelection() { |
| checkWidget(); |
| return selection; |
| } |
| |
| /** |
| * Removes the selection listener. |
| * |
| * @param selectionListener the selection listener |
| * @see org.eclipse.swt.widgets.Scale#removeSelectionListener(org.eclipse.swt.events.SelectionListener) |
| */ |
| public void removeSelectionListener(final SelectionListener selectionListener) { |
| checkWidget(); |
| selectionListeners.remove(selectionListener); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| /** |
| * Sets the selection. |
| * |
| * @param selection the new selection |
| * @see org.eclipse.swt.widgets.Scale#setSelection(int) |
| */ |
| public void setSelection(final int selection) { |
| checkWidget(); |
| if (selection < 0 || selection > 360) { |
| SWT.error(SWT.ERROR_CANNOT_SET_SELECTION); |
| } |
| this.selection = selection; |
| fireSelectionListeners(new Event()); |
| redraw(); |
| } |
| |
| } |