blob: 692a07ed0ea142d85db9c489ff966aa7faa713d1 [file] [log] [blame]
/*******************************************************************************
* 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 at gmail dot com) - Initial implementation and API
*******************************************************************************/
package org.mihalis.opal.launcher;
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.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.mihalis.opal.utils.SWTGraphicUtil;
/**
* Instances of this class are a launcher composed of buttons. When one clicks
* on the button, an animation is started and a selection event is fired
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>Selection</dd>
* </dl>
*/
public class Launcher extends Composite {
/** The items. */
private final List<LauncherItem> items;
/** The selection listeners. */
private final List<SelectionListener> selectionListeners;
/** The need redraw. */
private boolean needRedraw;
/** The selection. */
private int selection = -1;
/**
* 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
*
* @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 Launcher(final Composite parent, final int style) {
super(parent, style | SWT.BORDER);
this.items = new ArrayList<LauncherItem>();
this.selectionListeners = new ArrayList<SelectionListener>();
this.needRedraw = true;
setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
this.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(final Event event) {
drawLauncher();
}
});
this.addListener(SWT.KeyUp, new Listener() {
@Override
public void handleEvent(final Event event) {
handleKeyPressedEvent(event);
}
});
}
/**
* Draw the launcher.
*/
private void drawLauncher() {
if (!this.needRedraw) {
return;
}
disposePreviousContent();
createButtons();
pack();
this.needRedraw = false;
}
/**
* Dispose the content before a redraw.
*/
private void disposePreviousContent() {
for (final Control c : this.getChildren()) {
c.dispose();
}
}
/**
* Create the buttons that will compose the launcher.
*/
private void createButtons() {
final GridLayout gridLayout = new GridLayout(this.items.size() / 2, true);
gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
this.setLayout(gridLayout);
for (final LauncherItem item : this.items) {
createItem(item);
}
}
/**
* Creates the item.
*
* @param item the item
*/
private void createItem(final LauncherItem item) {
final LauncherLabel label = createLauncherLabel(item);
addListenerToLabel(label);
}
/**
* Creates the launcher label.
*
* @param item the item
* @return the launcher label
*/
private LauncherLabel createLauncherLabel(final LauncherItem item) {
final LauncherLabel label = new LauncherLabel(this, SWT.CENTER);
label.setText(item.title);
label.setImage(SWTGraphicUtil.createImageFromFile(item.image));
label.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
final GridData gd = new GridData(GridData.FILL, GridData.FILL, true, false);
gd.widthHint = 192;
gd.heightHint = 220;
label.setLayoutData(gd);
item.label = label;
return label;
}
/**
* Adds the listener to label.
*
* @param label the label
*/
private void addListenerToLabel(final LauncherLabel label) {
label.addListener(SWT.KeyUp, new Listener() {
@Override
public void handleEvent(final Event event) {
handleKeyPressedEvent(event);
}
});
label.addListener(SWT.MouseUp, new Listener() {
@Override
public void handleEvent(final Event event) {
handleClickEvent(event);
}
});
label.addListener(SWT.MouseDoubleClick, new Listener() {
@Override
public void handleEvent(final Event event) {
handleDoubleClickEvent(event);
}
});
}
/**
* Code executed when a key is pressed.
*
* @param event Event
*/
private void handleKeyPressedEvent(final Event event) {
switch (event.keyCode) {
case SWT.ARROW_LEFT:
if (this.selection == -1) {
this.selection = 0;
changeColor(this.selection, true);
return;
}
if (this.selection % 2 != 0) {
changeColor(this.selection, false);
this.selection--;
changeColor(this.selection, true);
}
break;
case SWT.ARROW_UP:
if (this.selection == -1) {
this.selection = 0;
changeColor(this.selection, true);
return;
}
if (this.selection >= 2) {
changeColor(this.selection, false);
this.selection -= 2;
changeColor(this.selection, true);
}
break;
case SWT.ARROW_RIGHT:
if (this.selection == -1) {
this.selection = 0;
changeColor(this.selection, true);
return;
}
if (this.selection % 2 == 0) {
changeColor(this.selection, false);
this.selection++;
changeColor(this.selection, true);
}
break;
case SWT.ARROW_DOWN:
if (this.selection == -1) {
this.selection = 0;
changeColor(this.selection, true);
return;
}
if (this.selection <= this.items.size() - 2) {
changeColor(this.selection, false);
this.selection += 2;
changeColor(this.selection, true);
}
break;
case SWT.HOME:
changeColor(this.selection, false);
this.selection = 0;
changeColor(this.selection, true);
break;
case SWT.END:
changeColor(this.selection, false);
this.selection = this.items.size() - 1;
changeColor(this.selection, true);
break;
}
}
/**
* Code executed when one clicks on the button.
*
* @param event Event
*/
private void handleClickEvent(final Event event) {
for (int i = 0; i < this.items.size(); i++) {
final LauncherItem item = this.items.get(i);
if (item.label != null && item.label.equals(event.widget)) {
if (this.selection != i) {
changeColor(this.selection, false);
this.selection = i;
changeColor(this.selection, true);
}
return;
}
}
}
/**
* Change the background color of a given button.
*
* @param index index of the button
* @param isSelected if <code>true</code>, the background is the light
* shadow. Otherwise, the background color is white
*/
private void changeColor(final int index, final boolean isSelected) {
if (index != -1 && this.items.get(index).label != null) {
this.items.get(index).label.setBackground(isSelected ? getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW) : getDisplay().getSystemColor(SWT.COLOR_WHITE));
}
}
/**
* Code executed when one double-clicks on a button.
*
* @param event Event
*/
private void handleDoubleClickEvent(final Event event) {
for (int i = 0; i < this.items.size(); i++) {
final LauncherItem item = this.items.get(i);
if (item.label != null && item.label.equals(event.widget)) {
if (this.selection != i) {
changeColor(this.selection, false);
this.selection = i;
changeColor(this.selection, true);
}
startAnimation(i, event);
return;
}
}
}
/**
* Start the animation for a given button.
*
* @param index index of the selected button
* @param event event (propagated to the selection listeners)
*/
private void startAnimation(final int index, final Event event) {
final LauncherLabel label = this.items.get(index).label;
getDisplay().timerExec(0, new Runnable() {
@Override
public void run() {
if (label.incrementAnimation()) {
getDisplay().timerExec(20, this);
} else {
fireSelectionListeners(event);
}
}
});
}
/**
* Fire the selection listeners.
*
* @param originalEvent mouse event
* @return <code>true</code> if the selection could be changed,
* <code>false</code> otherwise
*/
private boolean fireSelectionListeners(final Event originalEvent) {
final Event event = new Event();
event.button = originalEvent.button;
event.display = this.getDisplay();
event.item = null;
event.widget = this;
event.data = null;
event.time = originalEvent.time;
event.x = originalEvent.x;
event.y = originalEvent.y;
for (final SelectionListener listener : this.selectionListeners) {
final SelectionEvent selEvent = new SelectionEvent(event);
listener.widgetSelected(selEvent);
if (!selEvent.doit) {
return false;
}
}
return true;
}
/**
* Add an item to the launcher.
*
* @param title text associated to this item
* @param image image associated to this item
*/
public void addItem(final String title, final String image) {
checkWidget();
this.items.add(new LauncherItem(title, image));
this.needRedraw = true;
}
/**
* 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);
}
/**
* 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);
}
/**
* Return the selected button.
*
* @return the index of the selected button
* @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 getSelection() {
checkWidget();
return this.selection;
}
}