package org.csstudio.sds.components.ui.internal.figures; import java.util.ArrayList; import java.util.List; import org.csstudio.sds.components.ui.internal.figureparts.AlphaLabel; import org.csstudio.swt.xygraph.linearscale.AbstractScale.LabelSide; import org.csstudio.swt.xygraph.linearscale.LinearScale; import org.csstudio.swt.xygraph.linearscale.LinearScale.Orientation; import org.csstudio.swt.xygraph.linearscale.LinearScaledMarker; import org.csstudio.ui.util.CustomMediaFactory; import org.eclipse.draw2d.AbstractLayout; import org.eclipse.draw2d.FigureListener; import org.eclipse.draw2d.FigureUtilities; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.LineBorder; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.MouseMotionListener; import org.eclipse.draw2d.Polygon; import org.eclipse.draw2d.RectangleFigure; 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.Cursor; import org.eclipse.swt.graphics.Pattern; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** * A Scaled Slider figure * @author Xihui Chen * */ public class ScaledSliderFigure extends AbstractLinearMarkedFigure { private Color fillColor; private Color fillBackgroundColor; private Color thumbColor = CustomMediaFactory.getInstance().getColor( new RGB(172,172, 172)); private Color outlineColor; private boolean effect3D; private boolean horizontal; private final static Color WHITE_COLOR = CustomMediaFactory.getInstance().getColor( CustomMediaFactory.COLOR_WHITE); //border color for track and thumb private final static Color GRAY_COLOR = CustomMediaFactory.getInstance().getColor( CustomMediaFactory.COLOR_GRAY); private final static Color GREEN_COLOR = CustomMediaFactory.getInstance().getColor( CustomMediaFactory.COLOR_GREEN); private final static Color LABEL_COLOR = CustomMediaFactory.getInstance().getColor( new RGB(255, 255, 150)); /** The alpha (0 is transparency and 255 is opaque) for disabled paint */ private static final int DISABLED_ALPHA = 100; private final Track track; private final Thumb thumb; private final AlphaLabel label; private double increment = 1; /** * Listeners that react on slider events. */ private final List<IScaledSliderListener> _sliderListeners = new ArrayList<IScaledSliderListener>(); public ScaledSliderFigure() { super(); scale.setScaleLineVisible(false); scale.setForegroundColor(outlineColor); scale.setTickLabelSide(LabelSide.Secondary); if(horizontal) { ((LinearScale)scale).setOrientation(Orientation.HORIZONTAL); scale.setTickLabelSide(LabelSide.Primary); marker.setLabelSide(LabelSide.Secondary); }else { ((LinearScale)scale).setOrientation(Orientation.VERTICAL); scale.setTickLabelSide(LabelSide.Secondary); marker.setLabelSide(LabelSide.Primary); } track = new Track(); thumb = new Thumb(); label = new AlphaLabel(); label.setBackgroundColor(LABEL_COLOR); //label.setOpaque(true); label.setBorder(new LineBorder(GRAY_COLOR)); label.setVisible(false); setLayoutManager(new XSliderLayout()); add(scale, XSliderLayout.SCALE); add(marker, XSliderLayout.MARKERS); add(track, XSliderLayout.TRACK); add(thumb, XSliderLayout.THUMB); add(label, "label"); addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { revalidate(); } }); } @Override public void paint(final Graphics graphics) { super.paint(graphics); Rectangle figureBounds = getBounds().getCopy(); paintAdapter(graphics); } /** * Add a slider listener. * * @param listener * The slider listener to add. */ public void addSliderListener(final IScaledSliderListener listener) { _sliderListeners.add(listener); } /** * Inform all slider listeners, that the manual value has changed. * * @param newManualValue * the new manual value */ private void fireManualValueChange(final double newManualValue) { for (IScaledSliderListener l : _sliderListeners) { l.sliderValueChanged(newManualValue); } } @Override public boolean isOpaque() { return false; } @Override public void setEnabled(final boolean value) { super.setEnabled(value); repaint(); } /** * @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); } } @Override public void setForegroundColor(final Color fg) { super.setForegroundColor(fg); outlineColor = fg; } /** * @param fillColor the fillColor to set */ public void setFillColor(final Color fillColor) { this.fillColor = fillColor; } /** * @param fillBackgroundColor the fillBackgroundColor to set */ public void setFillBackgroundColor(final Color fillBackgroundColor) { this.fillBackgroundColor = fillBackgroundColor; } /** * @param thumbColor the thumbColor to set */ public void setThumbColor(final Color thumbColor) { this.thumbColor = thumbColor; } /** * @param effect3D the effect3D to set */ public void setEffect3D(final boolean effect3D) { this.effect3D = effect3D; } @Override public void setValue(final double value) { super.setValue(value); revalidate(); } /** * @param horizontal the horizontal to set */ public void setHorizontal(final boolean horizontal) { if(this.horizontal == horizontal) { return; } this.horizontal = horizontal; if(horizontal) { ((LinearScale)scale).setOrientation(Orientation.HORIZONTAL); scale.setTickLabelSide(LabelSide.Primary); marker.setLabelSide(LabelSide.Secondary); }else { ((LinearScale)scale).setOrientation(Orientation.VERTICAL); scale.setTickLabelSide(LabelSide.Secondary); marker.setLabelSide(LabelSide.Primary); } revalidate(); } /**Convert the difference of two points to the corresponding value to be changed. * @param difference the difference between two points. * difference = endPoint - startPoint * @param oldValue the old value before this change * @return the value to be changed */ private double calcValueChange(final Dimension difference, final double oldValue) { double change; double dragRange = ((LinearScale)scale).getTickLength(); if(scale.isLogScaleEnabled()) { double c = dragRange/( Math.log10(scale.getRange().getUpper()) - Math.log10(scale.getRange().getLower())); if(horizontal) { change = oldValue * (Math.pow(10, difference.width/c) - 1); } else { change = oldValue * (Math.pow(10, -difference.height/c) - 1); } } else { if(horizontal) { change = (scale.getRange().getUpper() - scale.getRange().getLower()) * difference.width / dragRange; } else { change = -(scale.getRange().getUpper() - scale.getRange().getLower()) * difference.height / dragRange; } } return change; } /** * Definition of listeners that react on slider events. * * @author Xihui Chen * */ public interface IScaledSliderListener { /** * React on a slider event. * * @param newValue * The new slider value. */ void sliderValueChanged(double newValue); } class Track extends RectangleFigure { public static final int TRACK_BREADTH = 6; public Track() { super(); setOutline(false); setForegroundColor(GRAY_COLOR); Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); setCursor(handCursor); addMouseListener(new MouseListener.Stub(){ @Override public void mousePressed(final MouseEvent me) { Point start = thumb.getLocation(); if(horizontal) { start.x = start.x + thumb.getBounds().width/2; } else { start.y = start.y + thumb.getBounds().height/2; } Dimension difference = me.getLocation().getDifference(start); double valueChange = calcValueChange(difference, value); if((increment <= 0) || (Math.abs(valueChange) > increment/2.0)) { if(increment > 0) { setValue(value + increment * Math.round(valueChange/increment)); } else { setValue(value + valueChange); } fireManualValueChange(value); ScaledSliderFigure.this.revalidate(); ScaledSliderFigure.this.repaint(); } } }); } @Override protected void fillShape(final Graphics graphics) { graphics.setAntialias(SWT.ON); int valuePosition = ((LinearScale) scale).getValuePosition(value, false); if(effect3D) { setOutline(false); //fill background graphics.setBackgroundColor(fillBackgroundColor); super.fillShape(graphics); Pattern backGroundPattern; if(horizontal) { backGroundPattern= new Pattern(Display.getCurrent(), bounds.x, bounds.y, bounds.x, bounds.y + bounds.height, WHITE_COLOR, 255, fillBackgroundColor, 0); } else { backGroundPattern= new Pattern(Display.getCurrent(), bounds.x, bounds.y, bounds.x + bounds.width, bounds.y, WHITE_COLOR, 255, fillBackgroundColor, 0); } graphics.setBackgroundPattern(backGroundPattern); super.fillShape(graphics); graphics.setForegroundColor(fillBackgroundColor); outlineShape(graphics); backGroundPattern.dispose(); //fill value if(horizontal) { backGroundPattern = new Pattern(Display.getCurrent(), bounds.x, bounds.y, bounds.x, bounds.y + bounds.height, WHITE_COLOR, 255, fillColor, 0); } else { backGroundPattern = new Pattern(Display.getCurrent(), bounds.x, bounds.y, bounds.x + bounds.width, bounds.y, WHITE_COLOR, 255, fillColor, 0); } graphics.setBackgroundColor(fillColor); graphics.setForegroundColor(fillColor); if(horizontal){ int fillWidth = valuePosition - bounds.x; graphics.fillRectangle(new Rectangle(bounds.x, bounds.y, fillWidth, bounds.height)); graphics.setBackgroundPattern(backGroundPattern); graphics.fillRectangle(new Rectangle(bounds.x, bounds.y, fillWidth, bounds.height)); graphics.drawRectangle(new Rectangle(bounds.x + lineWidth / 2, bounds.y + lineWidth / 2, fillWidth - Math.max(1, lineWidth), bounds.height - Math.max(1, lineWidth))); }else { int fillHeight = bounds.height - (valuePosition - bounds.y) - getLineWidth(); graphics.fillRectangle(new Rectangle(bounds.x, valuePosition, bounds.width, fillHeight)); graphics.setBackgroundPattern(backGroundPattern); graphics.fillRectangle(new Rectangle(bounds.x, valuePosition, bounds.width, fillHeight)); graphics.drawRectangle(new Rectangle(bounds.x + lineWidth / 2, valuePosition+ lineWidth / 2, bounds.width- Math.max(1, lineWidth), fillHeight - Math.max(1, lineWidth))); } backGroundPattern.dispose(); }else { setOutline(true); graphics.setBackgroundColor(fillBackgroundColor); super.fillShape(graphics); graphics.setBackgroundColor(fillColor); if(horizontal) { graphics.fillRectangle(new Rectangle(bounds.x + lineWidth, bounds.y + lineWidth, valuePosition - bounds.x - lineWidth, bounds.height - 2*lineWidth)); } else { graphics.fillRectangle(new Rectangle(bounds.x + lineWidth, valuePosition, bounds.width - 2* lineWidth, bounds.height - (valuePosition - bounds.y))); } graphics.setForegroundColor(outlineColor); } } } class Thumb extends Polygon { public static final int LENGTH = 20; public static final int BREADTH = 11; public final PointList horizontalThumbPointList = new PointList(new int[] { 0,0, 0, BREADTH, LENGTH*4/5, BREADTH, LENGTH, BREADTH/2, LENGTH*4/5, 0}) ; public final PointList verticalThumbPointList = new PointList(new int[] { 0,0, 0, LENGTH*4/5, BREADTH/2, LENGTH, BREADTH, LENGTH*4/5, BREADTH, 0}) ; private Color temp; class ThumbDragger extends MouseMotionListener.Stub implements MouseListener { private static final int LABEL_MARGIN = 3; protected Point start; protected boolean armed; private void setLabel() { String text = scale.format(value); Dimension textSize = FigureUtilities.getStringExtents(text, label.getFont()); label.setText(text); if(horizontal) { label.setBounds(new Rectangle( Thumb.this.getBounds().x + Thumb.this.getBounds().width/2 - (textSize.width + 2*LABEL_MARGIN)/2, Thumb.this.getBounds().y - textSize.height - 2*LABEL_MARGIN, textSize.width + 2 * LABEL_MARGIN, textSize.height+LABEL_MARGIN)); } else { label.setBounds(new Rectangle( Thumb.this.getBounds().x - textSize.width - 3*LABEL_MARGIN, Thumb.this.getBounds().y + Thumb.this.getBounds().height/2 - (textSize.height + LABEL_MARGIN)/2, textSize.width + 2 * LABEL_MARGIN, textSize.height+LABEL_MARGIN)); } label.setVisible(true); } @Override public void mousePressed(final MouseEvent me) { armed = true; start = me.getLocation(); setLabel(); me.consume(); } @Override public void mouseDragged(final MouseEvent me) { if (!armed) { return; } Dimension difference = me.getLocation().getDifference(start); double valueChange = calcValueChange(difference, value); if((increment <= 0) || (Math.abs(valueChange) > increment/2.0)) { if(increment > 0) { setValue(value + increment * Math.round(valueChange/increment)); } else { setValue(value + valueChange); } double valuePosition = ((LinearScale)scale).getValuePosition(value, false); start = new Point( horizontal? valuePosition: 0, horizontal ? 0 : valuePosition); fireManualValueChange(value); ScaledSliderFigure.this.layout(); ScaledSliderFigure.this.repaint(); setLabel(); } me.consume(); } @Override public void mouseReleased(final MouseEvent me) { if (!armed) { return; } armed = false; me.consume(); } @Override public void mouseEntered(final MouseEvent me) { temp = thumbColor; thumbColor = GREEN_COLOR; repaint(); } @Override public void mouseExited(final MouseEvent me) { thumbColor = temp; label.setVisible(false); repaint(); } @Override public void mouseDoubleClicked(final MouseEvent me) { } } public Thumb() { super(); setOutline(true); setFill(true); setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_HAND)); setForegroundColor(GRAY_COLOR); setLineWidth(1); ThumbDragger thumbDragger = new ThumbDragger(); addMouseMotionListener(thumbDragger); addMouseListener(thumbDragger); } @Override protected void fillShape(final Graphics g) { g.setAntialias(SWT.ON); g.setClip(new Rectangle(getBounds().x, getBounds().y, getBounds().width, getBounds().height)); g.setBackgroundColor(WHITE_COLOR); super.fillShape(g); Point leftPoint = getPoints().getPoint(0); Point rightPoint; if(horizontal) { rightPoint = getPoints().getPoint(4); } else { rightPoint = getPoints().getPoint(1);//.translate(0, -BREADTH/2); } Pattern thumbPattern = null; if(effect3D) { thumbPattern = new Pattern(Display.getCurrent(), leftPoint.x, leftPoint.y, rightPoint.x, rightPoint.y, WHITE_COLOR, 0, thumbColor, 255); g.setBackgroundPattern(thumbPattern); } else { g.setBackgroundColor(thumbColor); } g.fillPolygon(getPoints()); if(effect3D) { thumbPattern.dispose(); } } } class XSliderLayout extends AbstractLayout { private static final int GAP_BTW_THUMB_SCALE = 5; private static final int ADDITIONAL_MARGIN = 3; /** Used as a constraint for the scale. */ public static final String SCALE = "scale"; //$NON-NLS-1$ /** Used as a constraint for the pipe indicator. */ public static final String TRACK = "track"; //$NON-NLS-1$ /** Used as a constraint for the alarm ticks */ public static final String MARKERS = "markers"; //$NON-NLS-1$ /** Used as a constraint for the thumb */ public static final String THUMB = "thumb"; //$NON-NLS-1$ private LinearScale scale; private LinearScaledMarker marker; private Track track; private Thumb thumb; @Override public void setConstraint(final IFigure child, final Object constraint) { if(constraint.equals(SCALE)) { scale = (LinearScale)child; } else if (constraint.equals(MARKERS)) { marker = (LinearScaledMarker) child; } else if (constraint.equals(TRACK)) { track = (Track) child; } else if (constraint.equals(THUMB)) { thumb = (Thumb) child; } } @Override protected Dimension calculatePreferredSize(final IFigure container, final int w, final int h) { Insets insets = container.getInsets(); Dimension d = new Dimension(64, 4*64); d.expand(insets.getWidth(), insets.getHeight()); return d; } @Override public void layout(final IFigure container) { if(horizontal) { horizontalLayout(container); } else { verticalLayout(container); } } private void horizontalLayout(final IFigure container) { Rectangle area = container.getClientArea().getCopy(); area.x += ADDITIONAL_MARGIN; area.width -= 2*ADDITIONAL_MARGIN; Dimension scaleSize = new Dimension(0, 0); Dimension markerSize = new Dimension(0, 0); if(scale != null) { scaleSize = scale.getPreferredSize(area.width, -1); scale.setBounds(new Rectangle(area.x, area.y + area.height/2 + Thumb.LENGTH/2 + GAP_BTW_THUMB_SCALE, scaleSize.width, scaleSize.height)); } if((marker != null) && marker.isVisible()) { markerSize = marker.getPreferredSize(); marker.setBounds(new Rectangle(marker.getScale().getBounds().x, area.y + area.height/2 - markerSize.height - Thumb.LENGTH/2 - GAP_BTW_THUMB_SCALE, markerSize.width, markerSize.height)); } if(track != null) { track.setBounds(new Rectangle( scale.getValuePosition(scale.getRange().getLower(), false) - track.getLineWidth(), area.y + area.height/2 - Track.TRACK_BREADTH/2, scale.getTickLength()+ 2*track.getLineWidth(), Track.TRACK_BREADTH)); } if(thumb != null) { PointList newPointList = thumb.verticalThumbPointList.getCopy(); newPointList.translate(scale.getValuePosition(value, false) - Thumb.BREADTH/2, area.y + area.height/2 - Thumb.LENGTH/2 + 1 ); thumb.setPoints(newPointList); } } private void verticalLayout(final IFigure container) { Rectangle area = container.getClientArea(); Dimension scaleSize = new Dimension(0, 0); Dimension markerSize = new Dimension(0, 0); if(scale != null) { scaleSize = scale.getPreferredSize(-1, area.height); scale.setBounds(new Rectangle(area.x + area.width/2 + Thumb.LENGTH/2 + GAP_BTW_THUMB_SCALE, area.y, scaleSize.width, scaleSize.height)); } if((marker != null) && marker.isVisible()) { markerSize = marker.getPreferredSize(); marker.setBounds(new Rectangle( area.x + area.width/2 - markerSize.width - Thumb.LENGTH/2 - GAP_BTW_THUMB_SCALE, marker.getScale().getBounds().y, markerSize.width, markerSize.height)); } if(track != null) { track.setBounds(new Rectangle( area.x + area.width/2 - Track.TRACK_BREADTH/2, scale.getValuePosition(scale.getRange().getUpper(), false) - track.getLineWidth(), Track.TRACK_BREADTH, scale.getTickLength()+ 2*track.getLineWidth())); } if(thumb != null) { PointList newPointList = thumb.horizontalThumbPointList.getCopy(); newPointList.translate(area.x + area.width/2 - Thumb.LENGTH/2 + 1, scale.getValuePosition(value, false) - Thumb.BREADTH/2); thumb.setPoints(newPointList); } } } }