Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.mihalis.opal/src/main/java/org/mihalis/opal/imageSelector/ImageSelector.java')
-rw-r--r--org.mihalis.opal/src/main/java/org/mihalis/opal/imageSelector/ImageSelector.java679
1 files changed, 679 insertions, 0 deletions
diff --git a/org.mihalis.opal/src/main/java/org/mihalis/opal/imageSelector/ImageSelector.java b/org.mihalis.opal/src/main/java/org/mihalis/opal/imageSelector/ImageSelector.java
new file mode 100644
index 0000000..8cf7d58
--- /dev/null
+++ b/org.mihalis.opal/src/main/java/org/mihalis/opal/imageSelector/ImageSelector.java
@@ -0,0 +1,679 @@
+/*******************************************************************************
+ * 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.imageSelector;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+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 are controls that allow the user to select images.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * Work inspired by Romain Guy's work
+ * (http://www.curious-creature.org/2005/07/09/a-music-shelf-in-java2d/)
+ */
+public class ImageSelector extends Canvas {
+
+ /** The items. */
+ private List<ISItem> items;
+
+ /** The original items. */
+ private List<ISItem> originalItems;
+
+ /** The font. */
+ private Font font;
+
+ /** The Constant DEFAULT_WIDTH. */
+ private static final int DEFAULT_WIDTH = 148;
+
+ /** The max item width. */
+ private int maxItemWidth = DEFAULT_WIDTH;
+
+ /** The index. */
+ private int index = -1;
+
+ /** The sigma. */
+ private double sigma;
+
+ /** The rho. */
+ private double rho;
+
+ /** The exp multiplier. */
+ private double expMultiplier;
+
+ /** The exp member. */
+ private double expMember;
+
+ /** The spacing. */
+ private float spacing = 0.4f;
+
+ /** The gradient start. */
+ private Color gradientStart;
+
+ /** The gradient end. */
+ private Color gradientEnd;
+
+ /** The animation step. */
+ private double animationStep = -1d;
+
+ /** The Constant TIMER_INTERVAL. */
+ private static final int TIMER_INTERVAL = 50;
+
+ /** The page increment. */
+ private int pageIncrement = 5;
+
+ /** The cached image. */
+ private Image cachedImage;
+
+ /** The cached gc. */
+ private GC cachedGC;
+
+ /**
+ * 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 ImageSelector(final Composite parent, final int style) {
+ super(parent, style | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
+ final Font defaultFont = new Font(getDisplay(), "Lucida Sans", 24, SWT.NONE);
+ font = defaultFont;
+ SWTGraphicUtil.addDisposer(this, defaultFont);
+
+ setSigma(0.5);
+ gradientStart = getDisplay().getSystemColor(SWT.COLOR_BLACK);
+ gradientEnd = SWTGraphicUtil.getDefaultColor(this, 110, 110, 110);
+
+ addListeners();
+ SWTGraphicUtil.addDisposer(this, cachedGC);
+ SWTGraphicUtil.addDisposer(this, cachedImage);
+ }
+
+ /**
+ * Adds the listeners.
+ */
+ private void addListeners() {
+ addKeyListener();
+ addMouseListeners();
+
+ addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(final PaintEvent e) {
+ ImageSelector.this.paintControl(e);
+ }
+ });
+
+ addListener(SWT.Resize, new Listener() {
+ @Override
+ public void handleEvent(final Event event) {
+ if (cachedGC == null) {
+ return;
+ }
+ cachedGC.dispose();
+ cachedImage.dispose();
+ cachedImage = new Image(getDisplay(), getClientArea());
+ cachedGC = new GC(cachedImage);
+ cachedGC.setAntialias(SWT.ON);
+ }
+ });
+ }
+
+ /**
+ * Add the key listener.
+ */
+ private void addKeyListener() {
+ this.addKeyListener(new KeyAdapter() {
+ /**
+ * @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
+ */
+ @Override
+ public void keyReleased(final KeyEvent e) {
+ switch (e.keyCode) {
+ case SWT.ARROW_LEFT:
+ case SWT.ARROW_UP:
+ scrollAndAnimateBy(-1);
+ break;
+ case SWT.ARROW_RIGHT:
+ case SWT.ARROW_DOWN:
+ scrollAndAnimateBy(1);
+ break;
+ case SWT.HOME:
+ scrollBy(-1 * index);
+ break;
+ case SWT.END:
+ scrollBy(index);
+ break;
+ case SWT.PAGE_UP:
+ scrollBy(-1 * pageIncrement);
+ break;
+ case SWT.PAGE_DOWN:
+ scrollBy(pageIncrement);
+ break;
+ }
+ }
+ });
+ }
+
+ /**
+ * Add mouse listeners.
+ */
+ private void addMouseListeners() {
+ addMouseMoveListener(new MouseMoveListener() {
+ @Override
+ public void mouseMove(final MouseEvent e) {
+ for (final ISItem item : items) {
+ if (item.getUpperLeftCorner() != null && item.getLowerRightCorner() != null && e.x >= item.getUpperLeftCorner().x && e.x <= item.getLowerRightCorner().x && e.y >= item.getUpperLeftCorner().y
+ && e.y <= item.getLowerRightCorner().y) {
+ setCursor(getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+ return;
+ }
+ }
+ setCursor(getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
+ }
+ });
+
+ addMouseListener(new MouseAdapter() {
+ /**
+ * @see org.eclipse.swt.events.MouseAdapter#mouseUp(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseUp(final MouseEvent e) {
+ for (final ISItem item : items) {
+ if (item.getUpperLeftCorner() != null && item.getLowerRightCorner() != null && e.x >= item.getUpperLeftCorner().x && e.x <= item.getLowerRightCorner().x && e.y >= item.getUpperLeftCorner().y
+ && e.y <= item.getLowerRightCorner().y) {
+ scrollAndAnimateBy(originalItems.indexOf(item) - index);
+ return;
+ }
+ }
+ }
+ });
+
+ addListener(SWT.MouseWheel, new Listener() {
+ @Override
+ public void handleEvent(final Event event) {
+ scrollBy(-1 * event.count);
+ }
+ });
+ }
+
+ /**
+ * Set the sigma value for the gaussian curve.
+ *
+ * @param sigma new sigma parameter
+ */
+ public void setSigma(final double sigma) {
+ this.sigma = sigma;
+ rho = 1.0;
+ computeEquationParts();
+ rho = computeModifierUnbounded(0.0);
+ computeEquationParts();
+ redraw();
+ }
+
+ /**
+ * Compute the value of the modifier. The value is bounded between -1 and +1
+ *
+ * @param x input value
+ * @return the value of the modifier between -1 and +1
+ */
+ private double computeModifierBounded(final double x) {
+ double result = computeModifierUnbounded(x);
+ if (result > 1.0) {
+ result = 1.0;
+ } else if (result < -1.0) {
+ result = -1.0;
+ }
+ return result;
+ }
+
+ /**
+ * Compute the value of the modifier.
+ *
+ * @param x input value
+ * @return the value of the function
+ */
+ private double computeModifierUnbounded(final double x) {
+ return expMultiplier * Math.exp(-x * x / expMember);
+ }
+
+ /**
+ * Computer both members of the equation.
+ */
+ private void computeEquationParts() {
+ expMultiplier = Math.sqrt(2.0 * Math.PI) / sigma / rho;
+ expMember = 4.0 * sigma * sigma;
+ }
+
+ /**
+ * Draw the widget.
+ *
+ * @param e the paintEvent
+ */
+ private void paintControl(final PaintEvent e) {
+
+ if (cachedImage == null) {
+ cachedImage = new Image(getDisplay(), getClientArea());
+ cachedGC = new GC(cachedImage);
+ cachedGC.setAntialias(SWT.ON);
+ }
+
+ // Draw gradient
+ drawBackground();
+
+ // Draw the items
+ drawItems();
+
+ // Draw the title
+ if (animationStep < 0d) {
+ drawTitle();
+ }
+
+ // Draw the offscreen buffer to the screen
+ e.gc.drawImage(cachedImage, 0, 0);
+ }
+
+ /**
+ * Draw the background.
+ */
+ private void drawBackground() {
+ final Rectangle rect = getClientArea();
+
+ cachedGC.setForeground(gradientStart);
+ cachedGC.setBackground(gradientEnd);
+ cachedGC.fillGradientRectangle(rect.x, rect.y, rect.width, rect.height / 2, true);
+
+ cachedGC.setForeground(gradientEnd);
+ cachedGC.setBackground(gradientStart);
+ cachedGC.fillGradientRectangle(rect.x, rect.height / 2, rect.width, rect.height / 2, true);
+ }
+
+ /**
+ * Draw the items.
+ */
+ private void drawItems() {
+ if (animationStep < 0d) {
+ items.clear();
+ items.addAll(originalItems);
+ for (int i = 0; i < items.size(); i++) {
+ final ISItem item = items.get(i);
+ item.setzPosition((i - index) * spacing);
+ }
+ Collections.sort(items);
+ }
+
+ for (final ISItem item : items) {
+ drawItem(item);
+ }
+ }
+
+ /**
+ * Draw a given item.
+ *
+ * @param item item to draw
+ */
+ private void drawItem(final ISItem item) {
+
+ final int size = computeSize(item);
+ final int centerX = computeZPosition(item);
+ final int centerY = getClientArea().height / 2;
+
+ if (size <= 0 || centerX < 0 || centerX > getBounds().width) {
+ item.resetCornerToNull();
+ return;
+ }
+
+ final int alpha = computeAlpha(item);
+
+ final Image newImage = SWTGraphicUtil.createReflectedResizedImage(item.getImage(), size, size);
+ cachedGC.setAlpha(alpha);
+
+ final int x = centerX - newImage.getBounds().width / 2;
+ final int y = centerY - newImage.getBounds().height / 2;
+
+ cachedGC.drawImage(newImage, x, y);
+
+ item.setUpperLeftCorner(x, y);
+ item.setLowerRightCorner(x + newImage.getBounds().width, (int) (y + newImage.getBounds().height / 1.5));
+
+ newImage.dispose();
+ }
+
+ /**
+ * Compute the z position for a given item.
+ *
+ * @param item item
+ * @return the z position of the item
+ */
+ private int computeZPosition(final ISItem item) {
+ final int totalWidth = getClientArea().width / 2;
+ final int centerX = getClientArea().width / 2;
+ return (int) (centerX + item.getzPosition() * totalWidth);
+ }
+
+ /**
+ * Compute size for a given item.
+ *
+ * @param item item
+ * @return the size of the item
+ */
+ private int computeSize(final ISItem item) {
+ return (int) (computeModifierBounded(item.getzPosition()) * maxItemWidth);
+ }
+
+ /**
+ * Compute the alpha value of a given item.
+ *
+ * @param item item
+ * @return the alpha value of the item
+ */
+ private int computeAlpha(final ISItem item) {
+ return (int) (255 - 150 * Math.abs(item.getzPosition()));
+ }
+
+ /**
+ * Draw the title under the selected item.
+ */
+ private void drawTitle() {
+ final String title = originalItems.get(index).getText();
+ if (title == null || title.trim().equals("")) {
+ return;
+ }
+ cachedGC.setFont(getFont());
+ final Point textSize = cachedGC.stringExtent(title);
+
+ cachedGC.setFont(getFont());
+ cachedGC.setForeground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
+ cachedGC.setAlpha(255);
+
+ final int centerX = getClientArea().width / 2;
+ final int centerY = (getClientArea().height + maxItemWidth) / 2;
+
+ cachedGC.drawString(title, centerX - textSize.x / 2, centerY - textSize.y / 2, true);
+
+ }
+
+ /**
+ * Scroll the selected item.
+ *
+ * @param increment increment value
+ */
+ private void scrollBy(final int increment) {
+ index += increment;
+ if (index < 0) {
+ index = 0;
+ }
+
+ if (index >= items.size()) {
+ index = items.size() - 1;
+ }
+ redraw();
+ }
+
+ /**
+ * Scroll the selected item with an animation.
+ *
+ * @param increment increment value
+ */
+ private void scrollAndAnimateBy(final int increment) {
+ if (increment == 0 || index == 0 && increment < 0 || index == items.size() - 1 && increment > 0) {
+ return;
+ }
+
+ final double step = Math.abs(increment) / (300d / TIMER_INTERVAL);
+ ImageSelector.this.animationStep = step;
+ setCursor(getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
+
+ getDisplay().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ startAnimation(increment, step);
+ }
+
+ private void startAnimation(final int increment, final double step) {
+ items.clear();
+ items.addAll(originalItems);
+ for (int i = 0; i < items.size(); i++) {
+ final ISItem item = items.get(i);
+ item.setzPosition((i - index + animationStep * (increment > 0 ? -1d : 1d)) * spacing);
+ }
+ Collections.sort(items);
+ if (!isDisposed()) {
+ redraw();
+ }
+
+ animationStep += step;
+ if (animationStep >= 1d) {
+ animationStep = -1d;
+ index += increment;
+ setCursor(getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
+ } else {
+ if (!isDisposed()) {
+ getDisplay().timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Gets the items.
+ *
+ * @return the items displayed by this widget
+ */
+ public List<ISItem> getItems() {
+ return originalItems;
+ }
+
+ /**
+ * Sets the items.
+ *
+ * @param items the items that are displayed in this widget to set
+ */
+ public void setItems(final List<ISItem> items) {
+ this.items = new ArrayList<ISItem>(items);
+ originalItems = items;
+ index = this.items.size() / 2;
+ redraw();
+ }
+
+ /**
+ * Gets the font.
+ *
+ * @return the font used for the title
+ */
+ @Override
+ public Font getFont() {
+ return font;
+ }
+
+ /**
+ * Sets the font.
+ *
+ * @param font the font used for the title to set
+ */
+ @Override
+ public void setFont(final Font font) {
+ this.font = font;
+ redraw();
+ }
+
+ /**
+ * Gets the index.
+ *
+ * @return the index of the selected image
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Sets the index.
+ *
+ * @param index the index of the selected image to set
+ */
+ public void setIndex(final int index) {
+ this.index = index;
+ redraw();
+ }
+
+ /**
+ * Gets the max item width.
+ *
+ * @return the maximum items width
+ */
+ public int getMaxItemWidth() {
+ return maxItemWidth;
+ }
+
+ /**
+ * Sets the max item width.
+ *
+ * @param maxItemWidth the the maximum items width to set
+ */
+ public void setMaxItemWidth(final int maxItemWidth) {
+ this.maxItemWidth = maxItemWidth;
+ redraw();
+ }
+
+ /**
+ * Gets the sigma.
+ *
+ * @return the sigma value
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+ /**
+ * Gets the spacing.
+ *
+ * @return the spacing between 2 items
+ */
+ public float getSpacing() {
+ return spacing;
+ }
+
+ /**
+ * Sets the spacing.
+ *
+ * @param spacing the the spacing between 2 items to set
+ */
+ public void setSpacing(final float spacing) {
+ this.spacing = spacing;
+ redraw();
+ }
+
+ /**
+ * Gets the gradient start.
+ *
+ * @return the gradient start color
+ */
+ public Color getGradientStart() {
+ return gradientStart;
+ }
+
+ /**
+ * Sets the gradient start.
+ *
+ * @param gradientStart the the gradient start color to set
+ */
+ public void setGradientStart(final Color gradientStart) {
+ this.gradientStart = gradientStart;
+ redraw();
+ }
+
+ /**
+ * Gets the gradient end.
+ *
+ * @return the the gradient end color
+ */
+ public Color getGradientEnd() {
+ return gradientEnd;
+ }
+
+ /**
+ * Sets the gradient end.
+ *
+ * @param gradientEnd the the gradient end color to set
+ */
+ public void setGradientEnd(final Color gradientEnd) {
+ this.gradientEnd = gradientEnd;
+ redraw();
+ }
+
+ /**
+ * Gets the page increment.
+ *
+ * @return the page increment when the user uses PgUp and PgDown
+ */
+ public int getPageIncrement() {
+ return pageIncrement;
+ }
+
+ /**
+ * Sets the page increment.
+ *
+ * @param pageIncrement the page increment to set
+ */
+ public void setPageIncrement(final int pageIncrement) {
+ this.pageIncrement = pageIncrement;
+ }
+
+}

Back to the top