package org.csstudio.sds.components.ui.internal.figures;
import java.util.ArrayList;
import java.util.List;
import org.csstudio.sds.components.ui.internal.figureparts.Bulb;
import org.csstudio.sds.components.ui.internal.figureparts.PolarPoint;
import org.csstudio.sds.components.ui.internal.figureparts.RoundScale;
import org.csstudio.sds.components.ui.internal.figureparts.RoundScaledRamp;
import org.csstudio.sds.util.RotationUtil;
import org.csstudio.ui.util.CustomMediaFactory;
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.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.widgets.Display;
/**
* The figure of knob
* @author Xihui Chen
*
*/
public class KnobFigure extends AbstractRoundRampedFigure {
private final static Color WHITE_COLOR = CustomMediaFactory.getInstance().getColor(
CustomMediaFactory.COLOR_WHITE);
private final static Color GRAY_COLOR = CustomMediaFactory.getInstance().getColor(
CustomMediaFactory.COLOR_GRAY);
private final static Font DEFAULT_LABEL_FONT = CustomMediaFactory.getInstance().getFont(
new FontData("Arial", 12, SWT.BOLD));
private final static int BORDER_WIDTH = 2;
/** The alpha (0 is transparency and 255 is opaque) for disabled paint */
private static final int DISABLED_ALPHA = 100;
private boolean effect3D = true;
private double increment = 1;
private boolean manualSetValue = false;
private final Thumb thumb;
private final Bulb bulb;
private Color thumbColor;
private final Label valueLabel;
/**
* Listeners that react on slider events.
*/
private final List<IKnobListener> knobListeners =
new ArrayList<IKnobListener>();
public KnobFigure() {
super();
transparent = true;
scale.setScaleLineVisible(false);
ramp.setRampWidth(12);
valueLabel = new Label();
valueLabel.setText("20.00");
valueLabel.setFont(DEFAULT_LABEL_FONT);
bulb = new Bulb();
thumb = new Thumb();
thumb.setOutline(false);
setLayoutManager(new KnobLayout());
add(ramp, KnobLayout.RAMP);
add(bulb, KnobLayout.BULB);
add(scale, KnobLayout.SCALE);
add(valueLabel, KnobLayout.VALUE_LABEL);
add(thumb, KnobLayout.THUMB);
addFigureListener(new FigureListener() {
@Override
public void figureMoved(final IFigure source) {
ramp.setDirty(true);
revalidate();
}
});
}
@Override
public void paint(final Graphics graphics) {
super.paint(graphics);
Rectangle figureBounds = getBounds().getCopy();
paintAdapter(graphics);
}
/**
* Add a knob listener.
*
* @param listener
* The knob listener to add.
*/
public void addKnobListener(final IKnobListener listener) {
knobListeners.add(listener);
}
@Override
public void setBounds(final Rectangle rect) {
super.setBounds(rect);
}
@Override
public void setValue(final double value) {
super.setValue(value);
valueLabel.setText(scale.format(manualSetValue? this.value : value));
manualSetValue = false;
}
/**
* @param increment the increment to set
*/
public void setIncrement(final double increment) {
this.increment = increment;
}
@Override
protected void paintClientArea(final Graphics graphics) {
super.paintClientArea(graphics);
if(!isEnabled()) {
graphics.setAlpha(DISABLED_ALPHA);
graphics.setBackgroundColor(GRAY_COLOR);
graphics.fillRectangle(bounds);
}
Rectangle figureBounds = getBounds().getCopy();
paintAdapter(graphics);
}
/**
* @param color the bulb color to set
*/
public void setBulbColor(final Color color) {
bulb.setBulbColor(color);
}
/**
* @param effect3D the effect3D to set
*/
public void setEffect3D(final boolean effect3D) {
this.effect3D = effect3D;
bulb.setEffect3D(effect3D);
}
public void setThumbColor(final Color color) {
this.thumbColor = color;
}
public void setValueLabelVisibility(final boolean visible) {
valueLabel.setVisible(visible);
}
/**Convert the difference of two points to the corresponding value to be changed.
* @param difference the different theta value in degrees between two polar points.
* @param oldValue the old value before this change
* @return the value to be changed
*/
private double calcValueChange(final double difference, final double oldValue) {
double change;
double dragRange = ((RoundScale)scale).getLengthInDegrees();
if(scale.isLogScaleEnabled()) {
double c = dragRange/(
Math.log10(scale.getRange().getUpper()) -
Math.log10(scale.getRange().getLower()));
change = oldValue * (Math.pow(10, -difference/c) - 1);
} else {
change = -(scale.getRange().getUpper() - scale.getRange().getLower())
* difference / dragRange;
}
return change;
}
/**
* Inform all knob listeners, that the manual value has changed.
*
* @param newManualValue
* the new manual value
*/
private void fireManualValueChange(final double newManualValue) {
for (IKnobListener l : knobListeners) {
l.knobValueChanged(newManualValue);
}
}
/**
* Definition of listeners that react on knob events.
*
* @author Xihui Chen
*
*/
public interface IKnobListener {
/**
* React on a knob event.
*
* @param newValue
* The new slider value.
*/
void knobValueChanged(double newValue);
}
class Thumb extends Ellipse {
class ThumbDragger
extends MouseMotionListener.Stub
implements MouseListener {
private PolarPoint startPP;
protected double oldValuePosition;
protected boolean armed;
Point pole;
@Override
public void mousePressed(final MouseEvent me) {
armed = true;
pole = scale.getBounds().getCenter();
startPP = PolarPoint.point2PolarPoint(pole, bounds.getCenter());
//rotate axis to endAngle
startPP.rotateAxis(((RoundScale)scale).getEndAngle(), false);
oldValuePosition = ((RoundScale)scale).getValuePosition(
value, true);
me.consume();
}
@Override
public void mouseDragged(final MouseEvent me) {
if (!armed) {
return;
}
PolarPoint currentPP =
PolarPoint.point2PolarPoint(pole, me.getLocation());
//rotate axis to endAngle
currentPP.rotateAxis(((RoundScale)scale).getEndAngle(), false);
//coerce currentPP to min or max
if(currentPP.theta * 180.0/Math.PI > (((RoundScale)scale).getLengthInDegrees())) {
if(Math.abs(((RoundScale)scale).getValuePosition(value, true)-
(((RoundScale)scale).getLengthInDegrees())) < ((RoundScale)scale).getLengthInDegrees()/2.0) {
currentPP.theta = ((RoundScale)scale).getLengthInDegrees() * Math.PI/180.0;
} else {
currentPP.theta = 0;
}
}
double difference = currentPP.theta * 180.0/Math.PI - oldValuePosition;
double valueChange = calcValueChange(difference, value);
if((increment <= 0) || (Math.abs(valueChange) > increment/2.0)) {
manualSetValue = true;
if(increment > 0) {
setValue(value + increment * Math.round(valueChange/increment));
} else {
setValue(value + valueChange);
}
oldValuePosition = ((RoundScale)scale).getValuePosition(
value, true);
fireManualValueChange(value);
KnobFigure.this.revalidate();
KnobFigure.this.repaint();
}
me.consume();
}
@Override
public void mouseReleased(final MouseEvent me) {
if (!armed) {
return;
}
armed = false;
me.consume();
}
@Override
public void mouseDoubleClicked(final MouseEvent me) {
}
}
public Thumb() {
setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_HAND));
ThumbDragger thumbDragger = new ThumbDragger();
addMouseMotionListener(thumbDragger);
addMouseListener(thumbDragger);
}
@Override
protected void fillShape(final Graphics graphics) {
graphics.setAntialias(SWT.ON);
Pattern pattern = null;
graphics.setBackgroundColor(thumbColor);
if(effect3D){
graphics.setBackgroundColor(thumbColor);
super.fillShape(graphics);
pattern = new Pattern(Display.getCurrent(), bounds.x, bounds.y,
bounds.x + bounds.width, bounds.y + bounds.height,
WHITE_COLOR, 0, WHITE_COLOR, 255);
graphics.setBackgroundPattern(pattern);
}
super.fillShape(graphics);
if(effect3D) {
pattern.dispose();
}
graphics.setForegroundColor(thumbColor);
Rectangle figureBounds = getBounds().getCopy();
paintAdapter(graphics);
}
}
class KnobLayout extends AbstractLayout {
private static final int GAP_BTW_BULB_SCALE = 4;
/** Used as a constraint for the scale. */
public static final String SCALE = "scale"; //$NON-NLS-1$
/** Used as a constraint for the bulb. */
public static final String BULB = "bulb"; //$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 thumb */
public static final String THUMB = "thumb"; //$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 Bulb bulb;
private Thumb thumb;
private Label valueLabel;
@Override
public void setConstraint(final IFigure child, final Object constraint) {
if(constraint.equals(SCALE)) {
scale = (RoundScale)child;
} else if (constraint.equals(RAMP)) {
ramp = (RoundScaledRamp) child;
} else if (constraint.equals(BULB)) {
bulb = (Bulb) child;
} else if (constraint.equals(THUMB)) {
thumb = (Thumb) child;
} else if (constraint.equals(VALUE_LABEL)) {
valueLabel = (Label)child;
}
}
@Override
protected Dimension calculatePreferredSize(final IFigure container, final int w,
final int h) {
Insets insets = container.getInsets();
Dimension d = new Dimension(256, 256);
d.expand(insets.getWidth(), insets.getHeight());
return d;
}
@Override
public void layout(final 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();
Rectangle bulbBounds = null;
if(scale != null) {
scale.setBounds(area);
bulbBounds = area.getCopy();
bulbBounds.shrink(area.width/2 - scale.getInnerRadius() + GAP_BTW_BULB_SCALE,
area.height/2 - scale.getInnerRadius() + GAP_BTW_BULB_SCALE);
}
if((scale != null) && (ramp != null) && ramp.isVisible()) {
Rectangle rampBounds = area.getCopy();
ramp.setBounds(rampBounds.shrink(area.width/2 - scale.getInnerRadius() - ramp.getRampWidth()+2,
area.height/2 - scale.getInnerRadius() - ramp.getRampWidth()+2));
}
if((valueLabel != null) && valueLabel.isVisible()) {
Dimension labelSize = valueLabel.getPreferredSize();
valueLabel.setBounds(new Rectangle(bulbBounds.x + bulbBounds.width/2 - labelSize.width/2,
bulbBounds.y + bulbBounds.height * 3/4 - labelSize.height/2,
labelSize.width, labelSize.height));
}
if((bulb != null) && (scale != null) && bulb.isVisible()) {
bulb.setBounds(bulbBounds);
}
if((scale != null) && (thumb != null) && thumb.isVisible()){
Point thumbCenter = new Point(bulbBounds.x + bulbBounds.width*7.0/8.0,
bulbBounds.y + bulbBounds.height/2);
double valuePosition = 360 - scale.getValuePosition(value, false);
thumbCenter = RotationUtil.rotate(thumbCenter, valuePosition, center);
int thumbDiameter = bulbBounds.width/6;
thumb.setBounds(new Rectangle(thumbCenter.x - thumbDiameter/2,
thumbCenter.y - thumbDiameter/2,
thumbDiameter, thumbDiameter));
}
}
}
}