/*******************************************************************************
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
*******************************************************************************/
package com.liferay.ide.project.ui.upgrade.animated;
import com.liferay.ide.project.ui.upgrade.animated.UpgradeView.PageActionListener;
import com.liferay.ide.project.ui.upgrade.animated.UpgradeView.PageNavigatorListener;
import com.liferay.ide.project.ui.upgrade.animated.UpgradeView.PageValidationListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
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.Composite;
import org.eclipse.swt.widgets.Display;
/**
* @author Andy Wu
* @author Simon Jiang
*/
public class GearControl extends AbstractCanvas
implements PageNavigatorListener, PageActionListener, PageValidationListener
{
private float angle;
private final int BORDER = 20;
private Display display;
private Image errorImage;
private Image warningImage;
private Map<String, PageValidateEvent> validationMessageMap = new HashMap<String, PageValidateEvent>();
private final Color[] gearBackground = new Color[2];
private final Color[] gearForeground = new Color[2];
private int gearMaxNumber = 10;
private final Path[] gearPaths = new Path[gearMaxNumber];
private final Point[] tooltipPoints = new Point[gearMaxNumber];
private Color DARK_GRAY;
private Color GRAY;
private Color WHITE;
private Color tooltipColor;
private int hover = NONE;
private boolean needRedraw = false;
private Font numberFont;
private int oldHover = NONE;
private int oldSelection = NONE;
private boolean overflow;
private final double RADIAN = 2 * Math.PI / 360;
private float radius;
private int selection;
private final List<SelectionChangedListener> selectionChangedListeners =
Collections.synchronizedList( new ArrayList<SelectionChangedListener>() );
private float speed;
private long startAnimation;
private final int TEETH = 8;
private final float ANGLE = 360 / TEETH;
private Font tooltipFont;
public GearControl( Composite parent, int style )
{
super( parent, style | SWT.DOUBLE_BUFFERED );
init();
scheduleRun();
}
public void addSelectionChangedListener( SelectionChangedListener listener )
{
selectionChangedListeners.add( listener );
}
private 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;
}
public final int getSelection()
{
return selection;
}
protected void init()
{
super.init();
display = getDisplay();
errorImage = JFaceResources.getImage( Dialog.DLG_IMG_MESSAGE_ERROR );
warningImage = JFaceResources.getImage( Dialog.DLG_IMG_MESSAGE_WARNING );
WHITE = display.getSystemColor( SWT.COLOR_WHITE );
GRAY = display.getSystemColor( SWT.COLOR_GRAY );
DARK_GRAY = display.getSystemColor( SWT.COLOR_DARK_GRAY );
Font initialFont = getFont();
FontData[] fontData = initialFont.getFontData();
for( int i = 0; i < fontData.length; i++ )
{
fontData[i].setHeight( 16 );
fontData[i].setStyle( SWT.BOLD );
}
baseFont = new Font( display, fontData );
numberFont = createFont( 24 );
tooltipFont = createFont( 24 );
radius = 32;
setSize( (int) ( gearMaxNumber * 2 * radius ), (int) ( 2 * radius ) );
// 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 );
tooltipColor = createColor( 253, 232, 206 );
}
@Override
protected boolean needRedraw()
{
boolean retVal = false;
if( needRedraw )
{
needRedraw = false;
retVal = true;
}
if( overflow )
{
overflow = false;
retVal = true;
}
if( hover != oldHover )
{
retVal = true;
}
if( speed >= ANGLE )
{
startAnimation = 0;
return retVal;
}
long now = currentTime;
if( startAnimation == 0 )
{
startAnimation = now;
}
long timeSinceStart = now - startAnimation;
speed = timeSinceStart * ANGLE / 400;
angle += speed;
return true;
}
@Override
protected void onMouseDown( int x, int y )
{
if( x != Integer.MIN_VALUE && y != Integer.MIN_VALUE )
{
GC gc = new GC( this );
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 );
}
}
}
}
}
@Override
protected void onMouseMove( int x, int y )
{
if( x != Integer.MIN_VALUE && y != Integer.MIN_VALUE )
{
GC gc = new GC( this );
int number = UpgradeView.getPageNumber();
for( int i = 0; i < number; i++ )
{
Path path = gearPaths[i];
if( path != null && path.contains( x, y, gc, false ) )
{
if( i != hover )
{
hover = i;
}
return;
}
}
}
hover = NONE;
}
@Override
public void onPageAction( PageActionEvent event )
{
int targetPageIndex = event.getTargetPageIndex();
needRedraw = true;
if( targetPageIndex != NONE )
{
setSelection( targetPageIndex );
}
}
@Override
public void onPageNavigate( PageNavigateEvent event )
{
setSelection( event.getTargetPage() );
}
@Override
public void onValidation( PageValidateEvent event )
{
String pageId = event.getPageId();
validationMessageMap.put( pageId, event );
Page page = UpgradeView.getPage( selection );
if( page.getPageId().equals( pageId ) )
{
needRedraw = true;
}
}
@Override
protected void paint( GC gc )
{
gc.setFont( getBaseFont() );
gc.setLineWidth( 3 );
gc.setAntialias( SWT.ON );
int alpha = Math.min( (int) ( 255 * speed / ANGLE ), 255 );
int number = UpgradeView.getPageNumber();
for( int i = 0; i < number; i++ )
{
tooltipPoints[i] = paintGear( gc, i, alpha );
}
if( hover >= 0 && hover < tooltipPoints.length )
{
Point point = tooltipPoints[hover];
String title = UpgradeView.getPage( 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 );
}
paintValidationMessage( gc );
oldHover = hover;
}
private void paintValidationMessage( GC gc )
{
Page page = UpgradeView.getPage( selection );
String pageId = page.getPageId();
PageValidateEvent event = validationMessageMap.get( pageId );
if( event != null )
{
String message = event.getMessage();
if( message != null && !message.equals( "ok" ) )
{
if( event.getType().equals( PageValidateEvent.ERROR ) )
{
drawImage( gc, errorImage, 30, 130 );
}
else
{
drawImage( gc, warningImage, 30, 130 );
}
gc.setBackground( tooltipColor );
gc.setForeground( DARK_GRAY );
gc.setLineWidth( 1 );
Rectangle rectangle = drawTextNotCenter( gc, 40, 120, message, 2 );
gc.drawRectangle( rectangle );
}
}
}
private Point paintBadge( GC gc, double x, double y, double outerR, int i, int alpha )
{
if( selection >= gearMaxNumber )
{
gc.setAlpha( 255 - alpha );
}
else if( oldSelection >= gearMaxNumber )
{
gc.setAlpha( alpha );
}
Image badgeImage = null;
Page page = UpgradeView.getPage( i );
PageAction pageAction = page.getSelectedAction();
if( pageAction != null )
{
badgeImage = pageAction.getBageImage();
}
if( badgeImage != null )
{
gc.drawImage( badgeImage, (int) ( x - badgeImage.getBounds().width / 2 ), (int) ( y - outerR - 12 ) );
gc.setAlpha( 255 );
}
return new Point( (int) x, (int) ( y + outerR ) );
}
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 ) / 7;
gc.setForeground( hovered ? DARK_GRAY : gearForeground[selected] );
gc.setBackground( hovered ? GRAY : gearBackground[selected] );
if( outerR < 32.0 )
{
outerR = 32.0D;
}
if( innerR < 25.0 )
{
innerR = 25.600000381469727D;
}
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 < gearMaxNumber )
{
String number = Integer.toString( i + 1 );
gc.setForeground( selected == 1 ? gearForeground[1] : GRAY );
gc.setFont( numberFont );
drawText( gc, x, y - 1, number );
}
return paintBadge( gc, x, y, outerR, i, alpha );
}
public void restart()
{
angle = 0;
speed = 0;
}
public final void setSelection( int selection )
{
hover = NONE;
oldHover = NONE;
if( selection < 0 )
{
selection = 0;
overflow = true;
}
else if( selection > gearMaxNumber - 1 )
{
selection = gearMaxNumber - 1;
overflow = true;
}
if( overflow )
{
overflow = false;
while( needRedraw() )
{
}
overflow = true;
return;
}
oldSelection = this.selection;
this.selection = selection;
for( SelectionChangedListener listener : selectionChangedListeners )
{
listener.onSelectionChanged( selection );
}
restart();
}
}