blob: 6748ea098579c8ec691cf4baa0c61f9b2b7f14e0 [file] [log] [blame]
/*
* Copyright (c) 2014, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.setup.ui.questionnaire;
import org.eclipse.oomph.preferences.PreferencesFactory;
import org.eclipse.oomph.setup.ui.questionnaire.AnimatedCanvas.Animator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
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.Path;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eike Stepper
*/
public class GearAnimator extends Animator
{
public static final String RECORDER_PREFERENCE_KEY = "RECORDER_PREFERENCE_KEY";
public static final int NONE = -1;
public static final int PAGE_WIDTH = 620;
public static final int PAGE_HEIGHT = 420;
public static final int BORDER = 30;
public static final int GEARS = 7;
private static final int TEETH = 8;
private static final float ANGLE = 360 / TEETH;
private static final double RADIAN = 2 * Math.PI / 360;
private static final int EXIT = NONE - 1;
private static final int BACK = EXIT - 1;
private static final int NEXT = BACK - 1;
private static final int CHOICES = NEXT - 1;
private static final String[] TITLES = { "Welcome to Eclipse", "Refresh Resources Automatically?", "Show Line Numbers in Editors?",
"Check Spelling in Text Editors?", "Execute Jobs in Background?", "Encode Text Files with UTF-8?", "Enable Preference Recorder?" };
static final int BIG_FONT_PX = 48;
static final int NORMAL_FONT_PX = (int)(BIG_FONT_PX * .75);
private static Color WHITE;
private static Color GRAY;
private static Color DARK_GRAY;
private final List<Listener> listeners = new ArrayList<Listener>();
private Color purple;
private Color tooltipColor;
private Font tooltipFont;
private Font bigFont;
private Font hoverFont;
private Font normalFont;
private Font numberFont;
private Image exit;
private Image exitHover;
private Image question;
private final Image[] welcomeImages = new Image[2];
private final Image[] summaryImages = new Image[2];
private final Image[] backImages = new Image[2];
private final Image[] nextImages = new Image[2];
private final Image[] yesImages = new Image[5];
private final Image[] noImages = new Image[5];
private final Page[] pages = new Page[GEARS + 1];
private final Point[] tooltipPoints = new Point[pages.length];
private final Path[] gearPaths = new Path[GEARS + 1];
private final Color[] gearBackground = new Color[2];
private final Color[] gearForeground = new Color[2];
private float radius;
private int pageY;
private int answerY;
private int buttonR;
private long startAnimation;
private float speed;
private float angle;
private boolean overflow;
private int selection;
private int oldSelection = NONE;
private int hover = NONE;
private int oldHover = NONE;
private Image pageBuffer;
private GC pageGC;
private Image oldPageBuffer;
private GC oldPageGC;
private boolean pageBufferUpdated;
private boolean oldShowOverlay;
private boolean summaryShown;
private Rectangle exitBox;
private Rectangle backBox;
private Rectangle nextBox;
public GearAnimator(Display display)
{
super(display);
WHITE = display.getSystemColor(SWT.COLOR_WHITE);
GRAY = display.getSystemColor(SWT.COLOR_GRAY);
DARK_GRAY = display.getSystemColor(SWT.COLOR_DARK_GRAY);
}
@Override
protected void init()
{
super.init();
Display display = getDisplay();
bigFont = createFont(BIG_FONT_PX, PAGE_WIDTH, TITLES);
hoverFont = createFont(BIG_FONT_PX + 6, PAGE_WIDTH, TITLES);
normalFont = createFont(NORMAL_FONT_PX, PAGE_WIDTH, TITLES);
numberFont = createFont(24);
tooltipFont = createFont(16);
exit = loadImage("exit.png");
exitHover = loadImage("exit_hover.png");
question = loadImage("question.png");
welcomeImages[0] = loadImage("welcome.png");
welcomeImages[1] = loadImage("welcome_select.png");
summaryImages[0] = loadImage("summary.png");
summaryImages[1] = loadImage("summary_select.png");
backImages[0] = loadImage("back.png");
backImages[1] = loadImage("back_hover.png");
nextImages[0] = loadImage("next.png");
nextImages[1] = loadImage("next_hover.png");
buttonR = nextImages[0].getBounds().height / 2;
answerY = PAGE_HEIGHT + 4 * BORDER - buttonR;
yesImages[0] = loadImage("yes.png");
yesImages[1] = loadImage("yes_select.png");
yesImages[2] = loadImage("yes_hover.png");
yesImages[3] = loadImage("yes_big.png");
yesImages[4] = loadImage("yes_badge.png");
noImages[0] = loadImage("no.png");
noImages[1] = loadImage("no_select.png");
noImages[2] = loadImage("no_hover.png");
noImages[3] = loadImage("no_big.png");
noImages[4] = loadImage("no_badge.png");
radius = 32;
setSize((int)(GEARS * 2 * radius), (int)(2 * radius));
pageY = getHeight() + 2 * BORDER;
// Not selected.
gearBackground[0] = createColor(169, 171, 202);
gearForeground[0] = createColor(140, 132, 171);
// Selected.
gearBackground[1] = createColor(247, 148, 30);
gearForeground[1] = createColor(207, 108, 0);
purple = createColor(43, 34, 84);
tooltipColor = createColor(253, 232, 206);
pages[0] = new ImagePage(0, TITLES[0], 0, 0, 0, new TextAnswer(""));
pages[1] = new PreferencePage(1, TITLES[1], 0, 5, 29, "/instance/org.eclipse.core.resources/refresh.lightweight.enabled");
pages[2] = new PreferencePage(2, TITLES[2], 1, 19, 30, "/instance/org.eclipse.ui.editors/lineNumberRuler");
pages[3] = new PreferencePage(3, TITLES[3], 1, 186, 37, "/instance/org.eclipse.ui.editors/spellingEnabled");
pages[4] = new PreferencePage(4, TITLES[4], 0, 23, 160, "/instance/org.eclipse.ui.workbench/RUN_IN_BACKGROUND");
pages[5] = new PreferencePage(5, TITLES[5], 0, 181, 95, "/instance/org.eclipse.core.resources/encoding", "UTF-8", null);
pages[6] = new PreferencePage(6, TITLES[6], 1, 57, 82, RECORDER_PREFERENCE_KEY);
pages[7] = new SummaryPage(7, "Summary");
pageBuffer = new Image(display, PAGE_WIDTH, PAGE_HEIGHT);
pageGC = new GC(pageBuffer);
pageGC.setAdvanced(true);
oldPageBuffer = new Image(display, PAGE_WIDTH, PAGE_HEIGHT);
oldPageGC = new GC(oldPageBuffer);
oldPageGC.setAdvanced(true);
}
@Override
protected void dispose()
{
for (Path path : gearPaths)
{
if (path != null)
{
path.dispose();
}
}
for (Page page : pages)
{
if (page != null)
{
page.dispose();
}
}
pageGC.dispose();
pageBuffer.dispose();
oldPageGC.dispose();
oldPageBuffer.dispose();
super.dispose();
}
public Font getBigFont()
{
return bigFont;
}
public Font getHoverFont()
{
return hoverFont;
}
public Font getNormalFont()
{
return normalFont;
}
public final void addListener(Listener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
public final Listener[] getListeners()
{
synchronized (listeners)
{
return listeners.toArray(new Listener[listeners.size()]);
}
}
public final void removeListener(Listener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
public void restart()
{
angle = 0;
speed = 0;
}
public final Page[] getPages()
{
return pages;
}
public final int getSelection()
{
return selection;
}
public final void setSelection(int selection)
{
hover = NONE;
oldHover = NONE;
if (selection < 0)
{
selection = 0;
overflow = true;
}
else if (selection > pages.length - 1)
{
selection = pages.length - 1;
overflow = true;
}
if (overflow)
{
overflow = false;
while (advance())
{
// Just advance.
}
overflow = true;
return;
}
oldSelection = this.selection;
this.selection = selection;
if (getSelectedPage() instanceof SummaryPage)
{
summaryShown = true;
}
Image tmpPageBuffer = oldPageBuffer;
oldPageBuffer = pageBuffer;
pageBuffer = tmpPageBuffer;
GC tmpPageGC = oldPageGC;
oldPageGC = pageGC;
pageGC = tmpPageGC;
pageBufferUpdated = false;
restart();
}
public final int getOldSelection()
{
return oldSelection;
}
public final Page getSelectedPage()
{
return pages[selection];
}
@Override
protected boolean onKeyPressed(KeyEvent e)
{
if (e.keyCode == SWT.ESC)
{
exit();
return true;
}
if (e.keyCode == SWT.HOME)
{
setSelection(0);
return true;
}
if (e.keyCode == SWT.END)
{
setSelection(GearAnimator.GEARS);
return true;
}
if (e.keyCode == SWT.ARROW_RIGHT || e.keyCode == SWT.PAGE_DOWN)
{
setSelection(getSelection() + 1);
return true;
}
if (e.keyCode == SWT.ARROW_LEFT || e.keyCode == SWT.PAGE_UP)
{
setSelection(getSelection() - 1);
return true;
}
if (e.character >= '0' && e.character <= '6')
{
setSelection(e.character - '0');
return true;
}
if (e.character == 13)
{
Page page = getSelectedPage();
if (page instanceof SummaryPage)
{
page.doAnswer(0);
}
else
{
setSelection(getSelection() + 1);
}
return true;
}
if (e.character == 'y' || e.character == 'Y' || e.character == '+')
{
Page page = getSelectedPage();
if (page instanceof PreferencePage)
{
page.doAnswer(0);
}
return true;
}
if (e.character == 'n' || e.character == 'N' || e.character == '-')
{
Page page = getSelectedPage();
if (page instanceof PreferencePage)
{
page.doAnswer(1);
}
return true;
}
return false;
}
@Override
protected boolean onMouseMove(int x, int y)
{
if (x != Integer.MIN_VALUE && y != Integer.MIN_VALUE)
{
GC gc = new GC(getCanvas());
for (int i = 0; i < gearPaths.length; i++)
{
Path path = gearPaths[i];
if (path != null && path.contains(x, y, gc, false))
{
if (i != hover)
{
hover = i;
}
return true;
}
}
if (exitBox != null && exitBox.contains(x, y))
{
hover = EXIT;
return true;
}
Page page = getSelectedPage();
if (page != null)
{
if (page.showBack() && backBox != null && backBox.contains(x, y))
{
hover = BACK;
return true;
}
if (page.showNext() && nextBox != null && nextBox.contains(x, y))
{
hover = NEXT;
return true;
}
x -= BORDER;
y -= pageY;
hover = page.onMouseMove(x, y);
if (hover != NONE)
{
return true;
}
}
}
hover = NONE;
return false;
}
@Override
protected boolean onMouseDown(int x, int y)
{
if (x != Integer.MIN_VALUE && y != Integer.MIN_VALUE)
{
GC gc = new GC(getCanvas());
for (int i = 0; i < gearPaths.length; i++)
{
Path path = gearPaths[i];
if (path != null && path.contains(x, y, gc, false))
{
if (i != getSelection())
{
setSelection(i);
}
return true;
}
}
if (exitBox != null && exitBox.contains(x, y))
{
exit();
return true;
}
Page page = getSelectedPage();
if (page != null)
{
if (page.showBack() && backBox != null && backBox.contains(x, y))
{
setSelection(getSelection() - 1);
return true;
}
if (page.showNext() && nextBox != null && nextBox.contains(x, y))
{
setSelection(getSelection() + 1);
return true;
}
x -= BORDER;
y -= pageY;
if (page.onMouseDown(x, y))
{
return true;
}
}
}
return false;
}
protected void exit()
{
hover = EXIT;
AnimatedCanvas canvas = getCanvas();
canvas.redraw();
ExitShell exitShell = new ExitShell(this);
Boolean result = exitShell.openModal();
if (Boolean.TRUE.equals(result))
{
for (Listener listener : getListeners())
{
listener.onExit(GearAnimator.this, getSelectedPage());
}
}
hover = NONE;
if (!canvas.isDisposed())
{
canvas.redraw();
}
}
protected boolean shouldShowOverlay()
{
Page page = getSelectedPage();
if (page instanceof ImagePage)
{
ImagePage questionPage = (ImagePage)page;
if (hover <= CHOICES)
{
int hoveredChoice = -hover + CHOICES;
return hoveredChoice == questionPage.getOverlayChoice();
}
int choice = questionPage.getChoice();
if (choice != NONE)
{
return choice == questionPage.getOverlayChoice();
}
}
return (System.currentTimeMillis() / 1000 & 1) == 1;
}
@Override
protected boolean advance()
{
boolean needsRedraw = false;
if (overflow)
{
overflow = false;
needsRedraw = true;
}
boolean showOverlay = shouldShowOverlay();
if (showOverlay != oldShowOverlay)
{
oldShowOverlay = showOverlay;
updatePage();
needsRedraw = true;
}
if (hover != oldHover)
{
needsRedraw = true;
}
if (speed >= ANGLE)
{
startAnimation = 0;
return needsRedraw;
}
long now = System.currentTimeMillis();
if (startAnimation == 0)
{
startAnimation = now;
}
long timeSinceStart = now - startAnimation;
speed = timeSinceStart * ANGLE / 1900;
angle += speed;
return true;
}
@Override
protected void paint(GC gc, Image buffer)
{
gc.setFont(getBaseFont());
gc.setLineWidth(3);
gc.setAntialias(SWT.ON);
// Unselected gears
Page page = getSelectedPage();
int alpha = Math.min((int)(255 * speed / ANGLE), 255);
for (int i = 0; i < GEARS + 1; i++)
{
if (i != selection && (i < GEARS || summaryShown))
{
tooltipPoints[i] = paintGear(gc, i, alpha);
}
}
// Selected gear
tooltipPoints[selection] = paintGear(gc, selection, alpha);
// Exit button
int centerX = BORDER + PAGE_WIDTH - exit.getBounds().width / 2;
int centerY = BORDER + exit.getBounds().height / 2;
Image exitImage = hover == EXIT ? exitHover : exit;
exitBox = exitImage.getBounds();
exitBox.x = centerX - exitImage.getBounds().width / 2;
exitBox.y = centerY - exitImage.getBounds().height / 2;
gc.drawImage(exitImage, exitBox.x, exitBox.y);
// Selected page
if (!pageBufferUpdated)
{
updatePage();
pageBufferUpdated = true;
}
if (oldSelection == NONE)
{
gc.setAlpha(alpha);
gc.drawImage(pageBuffer, BORDER, pageY);
gc.setAlpha(255);
}
else
{
double progress = 2 * speed / ANGLE;
int slide = Math.min((int)(PAGE_WIDTH * progress * progress), PAGE_WIDTH);
gc.setAlpha(255 - alpha);
if (selection > oldSelection)
{
gc.drawImage(oldPageBuffer, slide, 0, PAGE_WIDTH - slide, PAGE_HEIGHT, BORDER, pageY, PAGE_WIDTH - slide, PAGE_HEIGHT);
gc.setAlpha(alpha);
gc.drawImage(pageBuffer, 0, 0, slide, PAGE_HEIGHT, BORDER + PAGE_WIDTH - slide, pageY, slide, PAGE_HEIGHT);
}
else
{
gc.drawImage(oldPageBuffer, 0, 0, PAGE_WIDTH - slide, PAGE_HEIGHT, BORDER + slide, pageY, PAGE_WIDTH - slide, PAGE_HEIGHT);
gc.setAlpha(alpha);
gc.drawImage(pageBuffer, PAGE_WIDTH - slide, 0, slide, PAGE_HEIGHT, BORDER, pageY, slide, PAGE_HEIGHT);
}
gc.setAlpha(255);
}
// Gear tooltip
if (hover >= 0 && hover < tooltipPoints.length)
{
Point point = tooltipPoints[hover];
String title = pages[hover].getTitle();
gc.setFont(tooltipFont);
gc.setForeground(DARK_GRAY);
gc.setBackground(tooltipColor);
Rectangle rectangle = drawText(gc, point.x, point.y + 14, title, 2);
gc.setForeground(GRAY);
gc.setLineWidth(1);
gc.drawRectangle(rectangle);
}
// Back button
if (page.showBack())
{
backBox = Animator.drawImage(gc, backImages[hover == BACK ? 1 : 0], BORDER + buttonR, answerY);
}
// Next button
if (page.showNext())
{
nextBox = Animator.drawImage(gc, nextImages[hover == NEXT ? 1 : 0], PAGE_WIDTH + BORDER - buttonR, answerY);
}
oldHover = hover;
}
private Point paintGear(GC gc, int i, int alpha)
{
double offset = 2 * i * radius;
double x = BORDER + radius + offset;
double y = BORDER + radius;
double r2 = (double)radius * .8f;
double r3 = (double)radius * .5f;
int selected = 0;
double factor = 1;
if (i == oldSelection)
{
if (speed < ANGLE / 2)
{
selected = 1;
}
}
else if (i == selection)
{
if (speed >= ANGLE / 2)
{
selected = 1;
factor += (ANGLE - speed) * .02;
}
else
{
factor += speed * .02;
}
}
boolean hovered = false;
if (i == hover)
{
factor += .1;
oldHover = hover;
if (selected == 0)
{
hovered = true;
}
}
double outerR = factor * radius;
double innerR = factor * r2;
float angleOffset = (angle + i * ANGLE) * (i % 2 == 1 ? -1 : 1);
gc.setForeground(hovered ? DARK_GRAY : gearForeground[selected]);
gc.setBackground(hovered ? GRAY : gearBackground[selected]);
Display display = getDisplay();
Path path = drawGear(gc, display, x, y, outerR, innerR, angleOffset);
if (gearPaths[i] != null)
{
gearPaths[i].dispose();
}
gearPaths[i] = path;
int ovalX = (int)(x - factor * r3);
int ovalY = (int)(y - factor * r3);
int ovalR = (int)(2 * factor * r3);
gc.setBackground(WHITE);
gc.fillOval(ovalX, ovalY, ovalR, ovalR);
gc.drawOval(ovalX, ovalY, ovalR, ovalR);
if (i == 0)
{
Animator.drawImage(gc, welcomeImages[selected], (int)x, (int)y);
}
else if (i < GEARS)
{
String number = Integer.toString(i);
gc.setForeground(selected == 1 ? gearForeground[1] : GRAY);
gc.setFont(numberFont);
Animator.drawText(gc, x, y - 1, number);
}
else
{
Animator.drawImage(gc, summaryImages[selected], (int)x, (int)y);
}
return paintBadge(gc, x, y, outerR, i, alpha);
}
private Point paintBadge(GC gc, double x, double y, double outerR, int i, int alpha)
{
if (selection >= GEARS)
{
gc.setAlpha(255 - alpha);
}
else if (oldSelection >= GEARS)
{
gc.setAlpha(alpha);
}
Page page = pages[i];
Answer answer = page.getChoiceAnswer();
if (answer instanceof ImageAnswer)
{
ImageAnswer imageAnswer = (ImageAnswer)answer;
Image image = imageAnswer.getImages()[4];
gc.drawImage(image, (int)(x - image.getBounds().width / 2), (int)(y - outerR - 12));
}
gc.setAlpha(255);
return new Point((int)x, (int)(y + outerR));
}
private Page updatePage()
{
pageGC.setBackground(WHITE);
pageGC.fillRectangle(pageBuffer.getBounds());
Page page = getSelectedPage();
page.paint(pageGC);
return page;
}
void updateOverlay(int x, int y)
{
Page page = getSelectedPage();
if (page instanceof ImagePage)
{
ImagePage questionPage = (ImagePage)page;
questionPage.overlayX += x;
questionPage.overlayY += y;
System.out.println("" + questionPage.overlayX + ", " + questionPage.overlayY);
updatePage();
overflow = true;
}
}
private static Path drawGear(GC gc, Display display, double cx, double cy, double outerR, double innerR, float angleOffset)
{
double radian2 = ANGLE / 2 * RADIAN;
double radian3 = .06;
Path path = new Path(display);
for (int i = 0; i < TEETH; i++)
{
double radian = (i * ANGLE + angleOffset) * RADIAN;
double x = cx + outerR * Math.cos(radian);
double y = cy - outerR * Math.sin(radian);
if (i == 0)
{
path.moveTo((int)x, (int)y);
}
double r1 = radian + radian3;
double r3 = radian + radian2;
double r2 = r3 - radian3;
double r4 = r3 + radian2;
x = cx + innerR * Math.cos(r1);
y = cy - innerR * Math.sin(r1);
path.lineTo((int)x, (int)y);
x = cx + innerR * Math.cos(r2);
y = cy - innerR * Math.sin(r2);
path.lineTo((int)x, (int)y);
x = cx + outerR * Math.cos(r3);
y = cy - outerR * Math.sin(r3);
path.lineTo((int)x, (int)y);
x = cx + outerR * Math.cos(r4);
y = cy - outerR * Math.sin(r4);
path.lineTo((int)x, (int)y);
}
path.close();
gc.fillPath(path);
gc.drawPath(path);
return path;
}
/**
* @author Eike Stepper
*/
public interface Listener
{
public void onAnswer(GearAnimator animator, Page page, Answer answer);
public void onExit(GearAnimator animator, Page page);
}
/**
* @author Eike Stepper
*/
public abstract class Answer
{
public Answer()
{
}
public abstract Point getSize(GC gc, Page page);
public abstract Rectangle paint(GC gc, Page page, int index, int x, int y, boolean hovered, boolean selected);
}
/**
* @author Eike Stepper
*/
public class TextAnswer extends Answer
{
private final String text;
public TextAnswer(String text)
{
this.text = text;
}
public final String getText()
{
return text;
}
@Override
public Point getSize(GC gc, Page page)
{
gc.setFont(hoverFont);
return gc.stringExtent(text);
}
@Override
public Rectangle paint(GC gc, Page page, int index, int x, int y, boolean hovered, boolean selected)
{
gc.setFont(hovered ? hoverFont : bigFont);
gc.setForeground(selected ? purple : DARK_GRAY);
Point extent = gc.stringExtent(text);
x -= extent.x / 2 - 60; // TODO -60 is a hack!
y -= extent.y / 2;
gc.drawText(text, x, y, true);
return new Rectangle(x, y, extent.x, extent.y);
}
}
/**
* @author Eike Stepper
*/
public class ImageAnswer extends Answer
{
private final Image[] images;
public ImageAnswer(Image... images)
{
this.images = images;
}
public final Image[] getImages()
{
return images;
}
@Override
public Point getSize(GC gc, Page page)
{
Rectangle bounds = images[2].getBounds();
return new Point(bounds.width, bounds.height);
}
@Override
public Rectangle paint(GC gc, Page page, int index, int x, int y, boolean hovered, boolean selected)
{
Image image = images[0];
if (hovered)
{
image = images[2];
}
else if (selected)
{
image = images[1];
}
else
{
if (page instanceof ImagePage && images.length > 3)
{
int overlayChoice = ((ImagePage)page).getOverlayChoice();
boolean overlayChoiceYes = overlayChoice == index;
boolean showOverlay = shouldShowOverlay();
if (showOverlay == overlayChoiceYes)
{
image = images[3];
}
}
}
return Animator.drawImage(gc, image, x, y);
}
}
/**
* @author Eike Stepper
*/
public class BackButton extends ImageAnswer
{
public BackButton()
{
super(backImages);
}
}
/**
* @author Eike Stepper
*/
public class NextButton extends ImageAnswer
{
public NextButton()
{
super(nextImages);
}
}
/**
* @author Eike Stepper
*/
public abstract class Page
{
private final int index;
private final String title;
private Answer[] answers;
private Rectangle[] answerBoxes;
private int choice = NONE;
public Page(int index, String title)
{
this.index = index;
this.title = title;
}
public final int getIndex()
{
return index;
}
public final String getTitle()
{
return title;
}
public final Answer[] getAnswers()
{
return answers;
}
public final void setAnswers(Answer[] answers)
{
this.answers = answers;
answerBoxes = new Rectangle[answers.length];
}
public final int getAnswer(int x, int y)
{
for (int i = 0; i < answers.length; i++)
{
Rectangle box = answerBoxes[i];
if (box != null && box.contains(x, y))
{
return i;
}
}
return NONE;
}
public final Answer getChoiceAnswer()
{
return choice == NONE ? null : answers[choice];
}
public final int getChoice()
{
return choice;
}
public final void setChoice(int choice)
{
this.choice = choice;
}
protected void dispose()
{
}
protected boolean showBack()
{
return index > 0;
}
protected boolean showNext()
{
return index < GEARS;
}
protected int onMouseMove(int x, int y)
{
int i = getAnswer(x, y);
if (i != NONE)
{
pageBufferUpdated = false;
return CHOICES - i;
}
if (hover <= CHOICES)
{
pageBufferUpdated = false;
}
return NONE;
}
protected boolean onMouseDown(int x, int y)
{
int i = getAnswer(x, y);
if (i != NONE)
{
doAnswer(i);
return true;
}
return false;
}
protected final void paint(GC gc)
{
String title = getTitle();
Font oldFont = gc.getFont();
gc.setFont(bigFont);
Point extent = gc.stringExtent(title);
gc.setForeground(purple);
gc.drawText(title, (PAGE_WIDTH - extent.x) / 2, 0, true);
gc.setFont(oldFont);
paintContent(gc);
paintAnswers(gc);
}
protected abstract void paintContent(GC gc);
protected int getAnswerY()
{
return answerY;
}
private void doAnswer(int i)
{
Answer answer = answers[i];
if (i == choice)
{
hover = NONE;
choice = NONE;
answer = null;
}
else
{
setChoice(i);
}
updatePage();
for (Listener listener : getListeners())
{
listener.onAnswer(GearAnimator.this, this, answer);
}
if (answer != null)
{
int selection = getSelection();
if (selection < GEARS)
{
setSelection(selection + 1);
}
}
}
private void paintAnswers(GC gc)
{
boolean selecteds[] = new boolean[answers.length];
boolean hovereds[] = new boolean[answers.length];
Point sizes[] = new Point[answers.length];
int width = (answers.length - 1) * BORDER;
int height = 0;
for (int i = 0; i < answers.length; i++)
{
selecteds[i] = i == choice;
if (CHOICES - i == hover)
{
oldHover = hover;
hovereds[i] = true;
}
sizes[i] = answers[i].getSize(gc, this);
width += sizes[i].x;
height = Math.max(height, sizes[i].y);
}
int x = (PAGE_WIDTH - width) / 2;
int y = getAnswerY() - pageY;
for (int i = 0; i < answers.length; i++)
{
Answer answer = answers[i];
answerBoxes[i] = answer.paint(gc, this, i, x, y, hovereds[i], selecteds[i]);
x += BORDER + sizes[i].x;
}
}
}
/**
* @author Eike Stepper
*/
public class ImagePage extends Page
{
private final Image image;
private final Image overlay;
private final int overlayChoice;
private int overlayX;
private int overlayY;
public ImagePage(int index, String title, int overlayChoice, int overlayX, int overlayY, Answer... answers)
{
super(index, title);
this.overlayChoice = overlayChoice;
setAnswers(answers);
Display display = getDisplay();
image = doLoadImage(display, index, "");
overlay = doLoadImage(display, index, "_ovr");
this.overlayX = overlayX;
this.overlayY = overlayY;
}
public ImagePage(int index, String title, int overlayChoice, int overlayX, int overlayY)
{
this(index, title, overlayChoice, overlayX, overlayY, new Answer[] { new ImageAnswer(yesImages), new ImageAnswer(noImages) });
}
public final int getOverlayChoice()
{
return overlayChoice;
}
@Override
protected void paintContent(GC gc)
{
if (image != null)
{
Rectangle bounds = image.getBounds();
int x = (PAGE_WIDTH - bounds.width) / 2;
int y = (PAGE_HEIGHT - bounds.height) / 2;
gc.drawImage(image, x, y);
if (overlay != null && shouldShowOverlay())
{
gc.drawImage(overlay, x + overlayX, y + overlayY);
}
}
}
private Image doLoadImage(final Display display, int index, String suffix)
{
try
{
return loadImage("page" + index + suffix + ".png");
}
catch (Exception ex)
{
return null;
}
}
}
/**
* @author Eike Stepper
*/
public class PreferencePage extends ImagePage
{
private final URI preferenceKey;
private final String yesValue;
private final String noValue;
public PreferencePage(int index, String title, int overlayChoice, int overlayX, int overlayY, String preferenceKey, String yesValue, String noValue)
{
super(index, title, overlayChoice, overlayX, overlayY, new Answer[] { new ImageAnswer(yesImages), new ImageAnswer(noImages) });
this.preferenceKey = PreferencesFactory.eINSTANCE.createURI(preferenceKey);
this.yesValue = yesValue;
this.noValue = noValue;
}
public PreferencePage(int index, String title, int overlayChoice, int overlayX, int overlayY, String preferenceKey)
{
this(index, title, overlayChoice, overlayX, overlayY, preferenceKey, "true", "false");
}
public final URI getPreferenceKey()
{
return preferenceKey;
}
public final String getYesValue()
{
return yesValue;
}
public final String getNoValue()
{
return noValue;
}
}
/**
* @author Eike Stepper
*/
public class SummaryPage extends Page
{
private Rectangle[] boxes;
public SummaryPage(int index, String title)
{
super(index, title);
setAnswers(new Answer[] { new TextAnswer("Finish") });
}
@Override
protected boolean showNext()
{
return false;
}
@Override
protected int onMouseMove(int x, int y)
{
if (boxes != null)
{
for (int i = 0; i < boxes.length; i++)
{
Rectangle box = boxes[i];
if (box.contains(x, y))
{
return i + 1;
}
}
}
return super.onMouseMove(x, y);
}
@Override
protected boolean onMouseDown(int x, int y)
{
if (boxes != null)
{
for (int i = 0; i < boxes.length; i++)
{
Rectangle box = boxes[i];
if (box.contains(x, y))
{
setSelection(i + 1);
return true;
}
}
}
return super.onMouseDown(x, y);
}
@Override
protected void paintContent(GC gc)
{
gc.setFont(normalFont);
boxes = new Rectangle[GEARS - 1];
int offsetX = yesImages[4].getBounds().width + 12;
int minWidth = Integer.MAX_VALUE;
int maxWidth = 0;
for (int i = 1; i < GEARS; i++)
{
Page page = pages[i];
Point extent = gc.stringExtent(page.getTitle());
int width = extent.x;
minWidth = Math.min(minWidth, width);
maxWidth = Math.max(maxWidth, width);
boxes[i - 1] = new Rectangle(0, 0, offsetX + width, extent.y + 4);
}
int width = (minWidth + maxWidth) / 2 + offsetX;
for (int i = 1; i < GEARS; i++)
{
int x = (PAGE_WIDTH - width) / 2;
int y = 40 * (1 + i);
ImagePage page = (ImagePage)pages[i];
ImageAnswer answer = (ImageAnswer)page.getChoiceAnswer();
if (answer != null)
{
gc.drawImage(answer.images[4], x, y + 8);
gc.setForeground(purple);
}
else
{
gc.drawImage(question, x, y + 8);
gc.setForeground(DARK_GRAY);
}
String title = page.getTitle();
if (title.endsWith("?"))
{
title = title.substring(0, title.length() - 1);
}
gc.drawText(title, x + offsetX, y);
boxes[i - 1].x = x;
boxes[i - 1].y = y + 4;
}
}
}
}