/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* 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
******************************************************************************/
package org.csstudio.swt.widgets.figures;
import org.csstudio.swt.widgets.figureparts.RoundScale;
import org.csstudio.swt.widgets.figureparts.RoundScaleTickMarks;
import org.csstudio.swt.widgets.figureparts.RoundScaledRamp;
import org.csstudio.swt.widgets.util.GraphicsUtil;
import org.csstudio.swt.widgets.util.PointsUtil;
import org.csstudio.swt.xygraph.linearscale.AbstractScale.LabelSide;
import org.csstudio.swt.xygraph.util.XYGraphMediaFactory;
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.Polygon;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
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.Pattern;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
/**
* The figure of gauge
* @author Xihui Chen
*
*/
public class GaugeFigure extends AbstractRoundRampedFigure {
private final Color WHITE_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_WHITE);
private final Color BORDER_COLOR = XYGraphMediaFactory.getInstance().getColor(
new RGB(100, 100, 100));
private final Color GRAY_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_GRAY);
private final Color DEFAULT_NEEDLE_COLOR = XYGraphMediaFactory.getInstance().getColor(
XYGraphMediaFactory.COLOR_RED);
private final Font DEFAULT_LABEL_FONT = XYGraphMediaFactory.getInstance().getFont(
new FontData("Arial", 12, SWT.BOLD));
private final static int BORDER_WIDTH = 2;
private boolean effect3D = true;
private NeedleCenter needleCenter;
private Needle needle;
private Label valueLabel;
public GaugeFigure() {
super();
transparent = true;
scale.setScaleLineVisible(false);
scale.setTickLabelSide(LabelSide.Secondary);
ramp.setRampWidth(10);
valueLabel = new Label();
valueLabel.setFont(DEFAULT_LABEL_FONT);
needle = new Needle();
needle.setFill(true);
needle.setOutline(false);
needleCenter = new NeedleCenter();
needleCenter.setOutline(false);
setLayoutManager(new GaugeLayout());
add(ramp, GaugeLayout.RAMP);
add(scale, GaugeLayout.SCALE);
add(valueLabel, GaugeLayout.VALUE_LABEL);
add(needle, GaugeLayout.NEEDLE);
add(needleCenter, GaugeLayout.NEEDLE_CENTER);
addFigureListener(new FigureListener() {
@Override
public void figureMoved(IFigure source) {
ramp.setDirty(true);
revalidate();
}
});
}
@Override
public void setBounds(Rectangle rect) {
super.setBounds(rect);
}
@Override
public void setValue(double value) {
super.setValue(value);
valueLabel.setText(getValueText());
}
@Override
protected void paintClientArea(Graphics graphics) {
graphics.setAntialias(SWT.ON);
Rectangle area = getClientArea();
area.width = Math.min(area.width, area.height);
area.height = area.width;
Pattern pattern = null;
graphics.pushState();
graphics.setBackgroundColor(GRAY_COLOR);
boolean support3D = GraphicsUtil.testPatternSupported(graphics);
if(effect3D && support3D) {
//add this to eliminate the repaint bug on Mac
graphics.fillOval(new Rectangle());
pattern = new Pattern(Display.getCurrent(), area.x, area.y,
area.x+area.width, area.y + area.height, BORDER_COLOR, WHITE_COLOR);
graphics.setBackgroundPattern(pattern);
}
graphics.fillOval(area);
graphics.popState();
if(effect3D && support3D){
pattern.dispose();
area.shrink(BORDER_WIDTH, BORDER_WIDTH);
}else
area.shrink(1, 1);
graphics.fillOval(area);
super.paintClientArea(graphics);
//glossy effect
if(effect3D && support3D) {
graphics.pushState();
graphics.setAntialias(SWT.ON);
final double R = area.width/2.0d;
final double UD_FILL_PART = 9.5d/10d;
final double UP_DOWN_RATIO = 1d/2d;
final double LR_FILL_PART = 8.5d/10d;
final double UP_ANGLE = 0d * Math.PI/180d;
final double DOWN_ANGLE = 35d * Math.PI/180d;
//add this to eliminate the repaint bug on Mac
graphics.fillOval(new Rectangle());
Pattern glossyPattern = new Pattern(Display.getCurrent(),
area.x + area.width/2, (float)(area.y + area.height/2 - R * UD_FILL_PART),
area.x + area.width/2, (float) (area.y + area.height/2 + R * UP_DOWN_RATIO),
WHITE_COLOR, 90, WHITE_COLOR, 0);
graphics.setBackgroundPattern(glossyPattern);
Rectangle rectangle = new Rectangle(
(int)(area.x + area.width/2 - R * LR_FILL_PART *Math.cos(UP_ANGLE)),
(int)(area.y + area.height/2 - R * UD_FILL_PART),
(int)(2*R* LR_FILL_PART*Math.cos(UP_ANGLE)), (int)(R*UD_FILL_PART + R*UP_DOWN_RATIO));
graphics.fillOval(rectangle);
glossyPattern.dispose();
glossyPattern = new Pattern(Display.getCurrent(),
area.x + area.width/2, (float)(area.y + area.height/2 + R * UP_DOWN_RATIO -1),
area.x + area.width/2, (float) (area.y + area.height/2 + R * UD_FILL_PART + 1),
WHITE_COLOR, 0, WHITE_COLOR, 40);
graphics.setBackgroundPattern(glossyPattern);
rectangle = new Rectangle(
(int)(area.x + area.width/2 - R*LR_FILL_PART*Math.sin(DOWN_ANGLE)),
(int)Math.ceil(area.y + area.height/2 + R * UP_DOWN_RATIO),
(int)(2*R*LR_FILL_PART*Math.sin(DOWN_ANGLE)),
(int)Math.ceil(R*UD_FILL_PART - R*UP_DOWN_RATIO));
graphics.fillOval(rectangle);
glossyPattern.dispose();
graphics.popState();
}
}
/**
* @param needleColor the needleColor to set
*/
public void setNeedleColor(Color needleColor) {
needle.setBackgroundColor(needleColor);
}
public Color getNeedleColor(){
return needle.getBackgroundColor();
}
/**
* @param effect3D the effect3D to set
*/
public void setEffect3D(boolean effect3D) {
if(this.effect3D == effect3D)
return;
this.effect3D = effect3D;
repaint();
}
/**
* @return the effect3D
*/
public boolean isEffect3D() {
return effect3D;
}
class Needle extends Polygon {
public Needle() {
setBackgroundColor(DEFAULT_NEEDLE_COLOR);
}
@Override
protected void fillShape(Graphics g) {
g.setAntialias(SWT.ON);
super.fillShape(g);
}
}
class NeedleCenter extends Ellipse {
public static final int DIAMETER = 16;
@Override
protected void fillShape(Graphics graphics) {
graphics.setAntialias(SWT.ON);
Pattern pattern = null;
graphics.setBackgroundColor(GRAY_COLOR);
boolean support3D = GraphicsUtil.testPatternSupported(graphics);
if(effect3D && support3D){
//add this to eliminate the repaint bug on Mac
graphics.fillOval(new Rectangle());
pattern = new Pattern(Display.getCurrent(), bounds.x, bounds.y,
bounds.x + bounds.width, bounds.y + bounds.height, WHITE_COLOR, BORDER_COLOR);
graphics.setBackgroundPattern(pattern);
}
super.fillShape(graphics);
if(effect3D && support3D)
pattern.dispose();
}
}
class GaugeLayout extends AbstractLayout {
private static final int GAP_BTW_NEEDLE_SCALE = -1;
/** Used as a constraint for the scale. */
public static final String SCALE = "scale"; //$NON-NLS-1$
/** Used as a constraint for the Needle. */
public static final String NEEDLE = "needle"; //$NON-NLS-1$
/** Used as a constraint for the Ramp */
public static final String RAMP = "ramp"; //$NON-NLS-1$
/** Used as a constraint for the needleCenter */
public static final String NEEDLE_CENTER = "needleCenter"; //$NON-NLS-1$
/** Used as a constraint for the value label*/
public static final String VALUE_LABEL = "valueLabel"; //$NON-NLS-1$
private RoundScale scale;
private RoundScaledRamp ramp;
private Polygon needle;
private NeedleCenter needleCenter;
private Label valueLabel;
private PointList needlePoints = new PointList(new int[] {0,0,0,0,0,0});
@Override
public void setConstraint(IFigure child, Object constraint) {
if(constraint.equals(SCALE))
scale = (RoundScale)child;
else if (constraint.equals(RAMP))
ramp = (RoundScaledRamp) child;
else if (constraint.equals(NEEDLE))
needle = (Polygon) child;
else if (constraint.equals(NEEDLE_CENTER))
needleCenter = (NeedleCenter) child;
else if (constraint.equals(VALUE_LABEL))
valueLabel = (Label)child;
}
@Override
protected Dimension calculatePreferredSize(IFigure container, int w,
int h) {
Insets insets = container.getInsets();
Dimension d = new Dimension(256, 256);
d.expand(insets.getWidth(), insets.getHeight());
return d;
}
@Override
public void layout(IFigure container) {
Rectangle area = container.getClientArea();
area.width = Math.min(area.width, area.height);
area.height = area.width;
area.shrink(BORDER_WIDTH, BORDER_WIDTH);
Point center = area.getCenter();
if(scale != null) {
scale.setBounds(area);
}
if(ramp != null && ramp.isVisible()) {
Rectangle rampBounds = area.getCopy();
ramp.setBounds(rampBounds.shrink(area.width/4, area.height/4));
}
if(valueLabel != null) {
Dimension labelSize = valueLabel.getPreferredSize();
valueLabel.setBounds(new Rectangle(area.x + area.width/2 - labelSize.width/2,
area.y + area.height * 7/8 - labelSize.height/2,
labelSize.width, labelSize.height));
}
if(needle != null && scale != null) {
needlePoints.setPoint (
new Point(center.x, center.y - NeedleCenter.DIAMETER/2 + 3), 0);
scale.getScaleTickMarks();
needlePoints.setPoint(
new Point(center.x + area.width/2 - RoundScaleTickMarks.MAJOR_TICK_LENGTH
- GAP_BTW_NEEDLE_SCALE, center.y), 1);
needlePoints.setPoint(
new Point(center.x, center.y + NeedleCenter.DIAMETER/2 - 3), 2);
double valuePosition = 360 - scale.getValuePosition(getCoercedValue(), false);
if(maximum > minimum){
if(value > maximum)
valuePosition += 10;
else if(value < minimum)
valuePosition -=10;
}else{
if(value > minimum)
valuePosition -= 10;
else if(value < maximum)
valuePosition +=10;
}
needlePoints.setPoint(
PointsUtil.rotate(needlePoints.getPoint(0), valuePosition, center), 0);
needlePoints.setPoint(
PointsUtil.rotate(needlePoints.getPoint(1), valuePosition, center), 1);
needlePoints.setPoint(
PointsUtil.rotate(needlePoints.getPoint(2), valuePosition, center),2);
needle.setPoints(needlePoints);
}
if(needleCenter != null){
needleCenter.setBounds(new Rectangle(center.x - NeedleCenter.DIAMETER/2,
center.y - NeedleCenter.DIAMETER/2,
NeedleCenter.DIAMETER, NeedleCenter.DIAMETER));
}
}
}
}