/* * Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron, Member of the Helmholtz * Association, (DESY), HAMBURG, GERMANY. * * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. WITHOUT WARRANTY OF ANY * KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, THE USER ASSUMES * THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY * CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER * EXCEPT UNDER THIS DISCLAIMER. DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, * MODIFICATION, USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY AT * HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM */ package org.csstudio.sds.components.ui.internal.figures; import java.text.NumberFormat; import java.util.HashMap; import java.util.Map; import org.csstudio.sds.components.ui.internal.utils.ShadedDrawing; import org.csstudio.sds.components.ui.internal.utils.TextPainter; import org.csstudio.sds.components.ui.internal.utils.Trigonometry; import org.csstudio.sds.ui.figures.BorderAdapter; import org.csstudio.sds.ui.figures.CrossedOutAdapter; import org.csstudio.sds.ui.figures.IBorderEquippedWidget; import org.csstudio.sds.ui.figures.ICrossedFigure; import org.csstudio.sds.ui.figures.IRhombusEquippedWidget; import org.csstudio.sds.ui.figures.RhombusAdapter; import org.csstudio.sds.util.AntialiasingUtil; import org.csstudio.sds.util.ChannelReferenceValidationException; import org.csstudio.sds.util.ChannelReferenceValidationUtil; import org.csstudio.ui.util.CustomMediaFactory; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.AbstractBorder; import org.eclipse.draw2d.FigureListener; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.XYLayout; import org.eclipse.draw2d.geometry.Insets; 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.RGB; /** * The class that draws a meter on the screen. The meter is displayed as a circular sector. * * @author jbercic, Kai Meyer * */ public final class RefreshableMeterFigure extends Shape implements IAdaptable { /** * A border adapter, which covers all border handlings. */ private IBorderEquippedWidget _borderAdapter; /** * This property defines the central angle of the sector, in degrees. */ private int _angle = 90; /** * <i>_innerAngle</i> is the angle of the actual display - it must be smaller than * <i>_angle</i>. */ private int _innerAngle = 80; /** * This defines the color, with which the needle is drawn. */ private Color _needleColor = CustomMediaFactory.getInstance().getColor(new RGB(0, 255, 0)); /** * The width of the display as a fraction of the total radius (height of the widget figure). */ private double _visibleRadius = 0.5; /** * The length of the major scale lines (fraction of the total radius). Major scale lines are * twice as long and twice as thick. */ private double _scaleRadius = 0.1; /** * The width of the scale value display area (as a fraction of the total radius). */ private double _textRadius = 0.1; /** * Step for the minor scale lines. */ private double _minorStep = 1.0; /** * Step for the major scale lines. */ private double _majorStep = 5.0; /** * The minimum value for the scale. */ private double _minValue = 0.0; /** * The maximum value for the scale. */ private double _maxValue = 10.0; /** * The current value. */ private double _value = 0.0; /** * Color value for the level LOLO. */ private Color _loloColor = CustomMediaFactory.getInstance().getColor(new RGB(255, 0, 0)); /** * Color value for the level LO. */ private Color _loColor = CustomMediaFactory.getInstance().getColor(new RGB(255, 81, 81)); /** * Color value for the level M. */ private Color _mColor = CustomMediaFactory.getInstance().getColor(new RGB(0, 255, 0)); /** * Color value for the level HI. */ private Color _hiColor = CustomMediaFactory.getInstance().getColor(new RGB(255, 81, 81)); /** * Color value for the level HIHI. */ private Color _hihiColor = CustomMediaFactory.getInstance().getColor(new RGB(255, 0, 0)); /** * Upper value for the level LOLO. */ private double _loloBound = 2.0; /** * Upper value for the level LO. */ private double _loBound = 4.0; /** * Upper value for the level M. */ private double _mBound = 6.0; /** * Upper value for the level HI. */ private double _hiBound = 8.0; /** * Upper value for the level HIHI. */ private double _hihiBound = 10.0; /** * The color for the scale. */ private Color _scaleColor = CustomMediaFactory.getInstance().getColor(new RGB(0, 0, 0)); /** * The width of the scale. */ private int _scaleWidth = 1; /** * The channel name the meter is connected to and the aliases defined for this meter. */ private String _channelName = "none"; /** * Is the background transparent? */ private boolean _transparent = true; /** * The current width of the widget. */ private int _imgWidth = 10; /** * The current height of the widget. */ private int _imgHeight = 10; /** * The radius of the whole meter. */ private double _r = 1.0; /** * The radius of the outer frame arc. */ private double _outR = 1.0; /** * The radius of the inner frame arc. */ private double _innR = 1.0; /** * The radius of the outer major scale lines. */ private double _scaleMajR = 1.0; /** * The radius of the outer minor scale lines. */ private double _scaleMinR = 1.0; /** * The radius of the scale value centers. */ private double _textR = 1.0; /** * y coordinate of the current value text. */ private int _valY = 10; /** * double the height of the channel font, so that everything can be moved down. */ private int _topDelta = 0; /** * Font properties for the values (scale and current) and the channel name. */ private Font _valuesFont = CustomMediaFactory.getInstance().getFont("Arial", 8, SWT.NONE); private Font _channelFont = CustomMediaFactory.getInstance().getFont("Arial", 8, SWT.NONE); /** * The potenz for the precision. */ private int _decimalPlaces = 2; /** * Subfigures for the background and the needle. */ private MeterBackground _background = null; private MeterNeedle _needle = null; /** * Does anything have to be recalculated? */ private boolean _doCalc = false; /** * The known aliases. */ private Map<String, String> _aliases; private CrossedOutAdapter _crossedOutAdapter; private RhombusAdapter _rhombusAdapter; public RefreshableMeterFigure() { setLayoutManager(new XYLayout()); _background = new MeterBackground(); add(_background); setConstraint(_background, new Rectangle(0, 0, 100, 100)); _needle = new MeterNeedle(); add(_needle); setConstraint(_needle, new Rectangle(0, 0, 100, 100)); addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure figure) { refreshConstraints(); _background.invalidate(); _needle.invalidate(); } }); } /** * {@inheritDoc} */ @Override protected boolean useLocalCoordinates() { return true; } /** * Fills the meter. Nothing to do here. */ @Override protected void fillShape(final Graphics gfx) { // nothing to do. } /** * Draws the outline of the meter. Nothing to do here. */ @Override protected void outlineShape(final Graphics gfx) { // nothing to do. } /** * Wrapper around the Trigonometry.cos function. * * @param angl * the angle, in degrees * @return the result of the trigonometric function */ private double cosine(final double angl) { if (angl % 90 == 0) { return Trigonometry.cos(angl - 1.0); } return Trigonometry.cos(angl); } /** * Wrappers around the Trigonometry.sin function. * * @param angl * the angle, in degrees * @return the result of the trigonometric function */ private double sine(final double angl) { if (angl % 180 == 0) { return Trigonometry.sin(angl - 1.0); } return Trigonometry.sin(angl); } private void refreshConstraints() { Rectangle figureBounds = getBounds().getCopy(); figureBounds.crop(this.getInsets()); setConstraint(_background, new Rectangle(0, 0, figureBounds.width, figureBounds.height)); setConstraint(_needle, new Rectangle(0, 0, figureBounds.width, figureBounds.height)); } public void refresh() { this.calculateRadii(_valuesFont.getFontData()[0].getHeight() * 2); } /** * Calculates all the needed radii and gets the current dimensions of the widget. */ private void calculateRadii(final int offset) { refreshConstraints(); Rectangle figureBounds = getBounds().getCopy(); figureBounds.crop(this.getInsets()); _imgWidth = figureBounds.width; _imgHeight = figureBounds.height - 2 * _channelFont.getFontData()[0].getHeight() - offset; _r = (_imgHeight) / (_visibleRadius + _scaleRadius + _textRadius + (1 - _visibleRadius - _scaleRadius - _textRadius) * (1 - cosine(_angle / 2))); if ((double) _imgWidth / 2 < sine(_angle / 2) * _r) { _r = (_imgWidth) / (2.0 * sine(_angle / 2)); } if (_angle > 180) { _r = (_imgHeight) / (1 - cosine(_angle / 2)); if (_imgWidth < 2.0 * _r) { _r = _imgWidth / 2.0; } } if (_textRadius + _scaleRadius + _visibleRadius > 1.0) { if ((_textRadius <= _scaleRadius) && (_textRadius <= _visibleRadius)) { double k1 = _scaleRadius / _textRadius; double k2 = _visibleRadius / _textRadius; _textRadius *= 1.0 / (1.0 + k1 + k2); _scaleRadius = k1 * _textRadius; _visibleRadius = k2 * _textRadius; } else { if ((_scaleRadius <= _textRadius) && (_scaleRadius <= _visibleRadius)) { double k1 = _textRadius / _scaleRadius; double k2 = _visibleRadius / _scaleRadius; _scaleRadius *= 1.0 / (1.0 + k1 + k2); _textRadius = k1 * _scaleRadius; _visibleRadius = k2 * _scaleRadius; } else { if ((_visibleRadius <= _textRadius) && (_visibleRadius <= _scaleRadius)) { double k1 = _textRadius / _visibleRadius; double k2 = _scaleRadius / _visibleRadius; _visibleRadius *= 1.0 / (1.0 + k1 + k2); _textRadius = k1 * _visibleRadius; _scaleRadius = k2 * _visibleRadius; } } } } _topDelta = _channelFont.getFontData()[0].getHeight() * 2; _outR = (1 - _textRadius - _scaleRadius) * _r; _innR = (1 - _textRadius - _scaleRadius - _visibleRadius) * _r; _scaleMinR = (1 - _textRadius - 0.5 * _scaleRadius) * _r; _scaleMajR = (1 - _textRadius) * _r; _textR = (1 - 0.5 * _textRadius) * _r; if (offset != 0) { _valY = _topDelta + _valuesFont.getFontData()[0].getHeight(); return; } if (_angle > 180) { _valY = (int) _r + _topDelta; } else { if ((int) (_innR - _innR * sine(90.0 - _angle / 2)) < _valuesFont.getFontData()[0] .getHeight() * 2) { calculateRadii(_valuesFont.getFontData()[0].getHeight() * 2); } else { _valY = _topDelta + (int) (_r - _innR + (_innR - _innR * sine(90.0 - _angle / 2)) / 2); } } } /** * Invalidates the background subfigure. */ public void invalidateBackground() { _doCalc = true; _background.invalidate(); } /** * Invalidates the needle subfigure. */ public void invalidateNeedle() { _needle.invalidate(); } /** * Sets the displayed channel name. * * @param channel * The name of the channel */ public void setChannelName(final String channel) { _channelName = channel; } /** * Sets the known aliases. * * @param aliases * A {@link Map} with the aliases */ public void setAliases(final Map<String, String> aliases) { if (aliases == null) { _aliases = new HashMap<String, String>(); } else { _aliases = aliases; } } public void setAngle(final int angl) { _angle = angl; invalidateBackground(); } public int getAngle() { return _angle; } public void setInnerAngle(final int angl) { _innerAngle = angl; invalidateBackground(); invalidateNeedle(); } public int getInnerAngle() { return _innerAngle; } public void setNeedleColor(final Color color) { _needleColor = color; invalidateNeedle(); } public void setVisibleRadius(final double newrad) { _visibleRadius = newrad; invalidateBackground(); invalidateNeedle(); } public double getVisibleRadius() { return _visibleRadius; } public void setScaleRadius(final double newrad) { _scaleRadius = newrad; invalidateBackground(); } public double getScaleRadius() { return _scaleRadius; } public void setMinorStep(final double minstep) { _minorStep = minstep; invalidateBackground(); } public double getMinorStep() { return _minorStep; } public void setMajorStep(final double maxstep) { _majorStep = maxstep; invalidateBackground(); } public double getMajorStep() { return _majorStep; } public void setMaxValue(final double max) { _maxValue = max; invalidateBackground(); invalidateNeedle(); } public double getMaxValue() { return _maxValue; } public void setMinValue(final double min) { _minValue = min; invalidateBackground(); invalidateNeedle(); } public double getMinValue() { return _minValue; } public void setValue(final double newval) { _value = newval; invalidateNeedle(); } public double getValue() { return _value; } public void setScaleColor(final Color newval) { _scaleColor = newval; invalidateBackground(); } public void setScaleWidth(final int newval) { _scaleWidth = newval; invalidateBackground(); } public int getScaleWidth() { return _scaleWidth; } public void setTextRadius(final double newval) { _textRadius = newval; invalidateBackground(); } public double getTextRadius() { return _textRadius; } public void setTransparent(final boolean newval) { _transparent = newval; } public boolean getTransparent() { return _transparent; } public void setMColor(final Color newval) { _mColor = newval; invalidateBackground(); } public void setLOLOColor(final Color newval) { _loloColor = newval; invalidateBackground(); } public void setLOColor(final Color newval) { _loColor = newval; invalidateBackground(); } public void setHIColor(final Color newval) { _hiColor = newval; invalidateBackground(); } public void setHIHIColor(final Color newval) { _hihiColor = newval; invalidateBackground(); } public void setMBound(final double newval) { _mBound = newval; invalidateBackground(); } public double getMBound() { return _mBound; } public void setLOLOBound(final double newval) { _loloBound = newval; invalidateBackground(); } public double getLOLOBound() { return _loloBound; } public void setLOBound(final double newval) { _loBound = newval; invalidateBackground(); } public double getLOBound() { return _loBound; } public void setHIBound(final double newval) { _hiBound = newval; invalidateBackground(); } public double getHIBound() { return _hiBound; } public void setHIHIBound(final double newval) { _hihiBound = newval; invalidateBackground(); } public double getHIHIBound() { return _hihiBound; } public void setValuesFont(final Font font) { _valuesFont = font; invalidateBackground(); invalidateNeedle(); } public Font getValuesFont() { return _valuesFont; } public void setChannelFont(final Font font) { _channelFont = font; invalidateBackground(); invalidateNeedle(); } public Font getChannelFont() { return _channelFont; } /** * Sets the count of decimal places for this Figure. * * @param decimalPlaces * The precision */ public void setDecimalPlaces(final int decimalPlaces) { _decimalPlaces = decimalPlaces; invalidateNeedle(); } /** * Gets the precision of this Figure. * * @return The precision */ public int getDecimalPlaces() { return _decimalPlaces; } /** * Subfigure that draws the arched frame and background of the meter. * * @author jbercic * */ class MeterBackground extends Shape { /** * Fills the background with colors for the five levels. */ @Override protected void fillShape(final Graphics gfx) { double uppAngle = (_innerAngle / (_maxValue - _minValue)) * (_loloBound - _minValue); double loAngle = 90.0 + _innerAngle / 2.0; if (!_transparent) { gfx.setBackgroundColor(getBackgroundColor()); gfx.fillRectangle(getBounds()); } AntialiasingUtil.getInstance().enableAntialiasing(gfx); // lolo area gfx.setBackgroundColor(_loloColor); gfx.fillArc(calculateRadius(_outR), (int) Math.round(loAngle - uppAngle), (int) Math.round(uppAngle)); // lo area loAngle -= uppAngle; uppAngle = (_innerAngle / (_maxValue - _minValue)) * (_loBound - _loloBound); gfx.setBackgroundColor(_loColor); gfx.fillArc(calculateRadius(_outR), (int) Math.round(loAngle - uppAngle), (int) Math.round(uppAngle)); // m area loAngle -= uppAngle; uppAngle = (_innerAngle / (_maxValue - _minValue)) * (_mBound - _loBound); gfx.setBackgroundColor(_mColor); gfx.fillArc(calculateRadius(_outR), (int) Math.round(loAngle - uppAngle), (int) Math.round(uppAngle)); // hi area loAngle -= uppAngle; uppAngle = (_innerAngle / (_maxValue - _minValue)) * (_hiBound - _mBound); gfx.setBackgroundColor(_hiColor); gfx.fillArc(calculateRadius(_outR), (int) Math.round(loAngle - uppAngle), (int) Math.round(uppAngle)); // hihi area loAngle -= uppAngle; uppAngle = (_innerAngle / (_maxValue - _minValue)) * (_hihiBound - _hiBound); gfx.setBackgroundColor(_hihiColor); gfx.fillArc(calculateRadius(_outR), (int) Math.round(loAngle - uppAngle), (int) Math.round(uppAngle)); // background color gfx.setBackgroundColor(getBackgroundColor()); gfx.fillArc(calculateRadius(_innR), (int) Math.round(90.0 - _angle / 2), _angle); } /** * @param radius TODO * @return */ private Rectangle calculateRadius(final double radius) { return new Rectangle((int) (_imgWidth / 2 - radius), (int) (_r - radius) + _topDelta, (int) (radius * 2.0), (int) (radius * 2.0)); } /** * The main drawing routine. */ @Override public void paintFigure(final Graphics gfx) { if (_doCalc) { // helge 1 _doCalc = false; calculateRadii(0); } fillShape(gfx); outlineShape(gfx); _crossedOutAdapter.paint(gfx, false); _rhombusAdapter.paint(gfx, false); } /** * Draws the outline of the background: the frame, the scale and the channel name. */ @Override protected void outlineShape(final Graphics gfx) { AntialiasingUtil.getInstance().enableAntialiasing(gfx); /** * WORKAROUND: rotation does something weird to the Graphics object, so that subsequent * font setting does not seem to work. Pushing and popping the state seems to fix this. */ gfx.pushState(); drawScale(gfx); gfx.popState(); drawChannelName(gfx); drawFrame(gfx); } /** * Draws the channel name below the meter.</br> The channel used is the one connected to the * value property. * * @param gfx * The Graphics context. */ private void drawChannelName(final Graphics gfx) { gfx.setForegroundColor(getForegroundColor()); gfx.setFont(_channelFont); String toprint; try { toprint = ChannelReferenceValidationUtil .createCanonicalName(_channelName, _aliases); } catch (ChannelReferenceValidationException e) { toprint = _channelName; // TODO: Should this be logged properly? e.printStackTrace(); } TextPainter.drawText(gfx, toprint, _imgWidth / 2, _channelFont.getFontData()[0] .getHeight(), TextPainter.CENTER); } /** * Draws the scale and values at major scale lines. * * @param gfx * The Graphics context. */ private void drawScale(final Graphics gfx) { double curr; double currAngle; gfx.setForegroundColor(_scaleColor); gfx.setLineWidth(_scaleWidth); // minor scale lines for (curr = _minValue + _minorStep; curr <= _maxValue - _minorStep; curr += _minorStep) { currAngle = 90.0 + (double) _innerAngle / 2 - (_innerAngle / (_maxValue - _minValue)) * (curr - _minValue); ShadedDrawing.drawLineAtAngle(gfx,_outR,_scaleMinR,currAngle,_imgWidth / 2,(int) _r + _topDelta); } int wdth = gfx.getLineWidth(); if (wdth == 0) { wdth = 1; } gfx.setLineWidth(wdth * 2); // major scale lines String val; gfx.setFont(_valuesFont); for (curr = _minValue; curr <= _maxValue; curr += _majorStep) { // the tick mark currAngle = 90.0 + (double) _innerAngle / 2 - (_innerAngle / (_maxValue - _minValue)) * (curr - _minValue); gfx.setForegroundColor(_scaleColor); ShadedDrawing.drawLineAtAngle(gfx, _outR, _scaleMajR, currAngle, _imgWidth / 2, (int) _r + _topDelta); // the value of the tick mark try { NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(_decimalPlaces); val = format.format(curr); } catch (Exception e) { val = Double.toString(curr); } gfx.setForegroundColor(getForegroundColor()); TextPainter.drawRotatedText(gfx, val, 90.0 - currAngle, _imgWidth / 2 + (int) (cosine(currAngle) * _textR), (int) _r - (int) (sine(currAngle) * _textR) + _topDelta, TextPainter.CENTER); } } /** * Draws the arched frame of the meter. * <p> * <b>WARNING:</b> if the starting and ending point of an arc are close together, the arc * may not be drawn at all due to rounding errors (the angles given must be integer). For * example if start=-89 and angle=359, the endings are only a pixel apart, if the radius is * less than about 54 pixels. The ending is further to the left (drawing is * counter-clockwise), meaning that the arc is only 1 pixel long, instead of a full circle. * </p> * * @param gfx * The Graphics context. */ private void drawFrame(final Graphics gfx) { gfx.setForegroundColor(CustomMediaFactory.getInstance().getColor(0, 0, 0)); gfx.setLineWidth(1); // outer arc gfx.drawArc((int) (_imgWidth / 2 - _outR), (int) (_r - _outR) + _topDelta, (int) _outR * 2, (int) _outR * 2, (int) Math.round(90.0 - _angle / 2), _angle); // inner arc gfx.drawArc((int) (_imgWidth / 2 - _innR), (int) (_r - _innR) + _topDelta, (int) _innR * 2, (int) _innR * 2, (int) Math.round(90.0 - _angle / 2), _angle); // left and right lines gfx.drawArc(_imgWidth / 2 - (int) _r, _topDelta, (int) (2.0 * _r), (int) (2.0 * _r), (int) Math.round(90.0 - _angle / 2), _angle); ShadedDrawing.drawLineAtAngle(gfx, _innR,/* out_r */ _r, 90 + _angle / 2, _imgWidth / 2, (int) _r + _topDelta); ShadedDrawing.drawLineAtAngle(gfx, _innR, _r, 90 - _angle / 2, _imgWidth / 2, (int) _r + _topDelta); } } /** * Subfigure that draws the needle and current value. * * @author jbercic * */ class MeterNeedle extends Shape { /** * The main drawing routine. */ @Override public void paintFigure(final Graphics gfx) { outlineShape(gfx); // helge 2 } /** * Fills the subfigure. Nothing to do here. */ @Override protected void fillShape(final Graphics gfx) { // nothing to do. } /** * Draws the outline. Here this means the needle and current value. */ @Override protected void outlineShape(final Graphics gfx) { AntialiasingUtil.getInstance().enableAntialiasing(gfx); gfx.setForegroundColor(_needleColor); gfx.setLineWidth(4); gfx.setFont(_channelFont); double currAngle = 90.0 + (double) _innerAngle / 2 - (_innerAngle / (_maxValue - _minValue)) * (_value - _minValue); ShadedDrawing.drawLineAtAngle(gfx, _innR, _outR, currAngle, _imgWidth / 2, (int) _r + _topDelta); String val; try { NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(_decimalPlaces); val = format.format(_value); } catch (Exception e) { val = Double.toString(_value); } gfx.setFont(_channelFont); gfx.setForegroundColor(getForegroundColor()); TextPainter.drawText(gfx, val, _imgWidth / 2, _valY, TextPainter.CENTER); } } /** * {@inheritDoc} */ @Override public Object getAdapter(final Class adapter) { if (adapter == IBorderEquippedWidget.class) { if (_borderAdapter == null) { _borderAdapter = new BorderAdapter(this) { @Override protected AbstractBorder createShapeBorder(final int borderWidth, final Color borderColor) { MeterBorder meterBorder = new MeterBorder(borderWidth); meterBorder.setBorderColor(borderColor); return meterBorder; } }; } return _borderAdapter; } else if(adapter == ICrossedFigure.class) { if(_crossedOutAdapter==null) { _crossedOutAdapter = new CrossedOutAdapter(this); } return _crossedOutAdapter; } else if(adapter == IRhombusEquippedWidget.class) { if(_rhombusAdapter==null) { _rhombusAdapter = new RhombusAdapter(this); } return _rhombusAdapter; } return null; } /** * The Border for this {@link RefreshableMeterFigure}. * * @author Kai Meyer * */ private final class MeterBorder extends AbstractBorder { /** * The Color of the border. */ private Color _borderColor; /** * The width of the border. */ private int _borderWidth = 1; /** * Constructor. * * @param borderWidth * The width for the border */ public MeterBorder(final int borderWidth) { _borderWidth = borderWidth; } /** * Sets the Color of the border. * * @param borderColor * The Color for the border */ public void setBorderColor(final Color borderColor) { _borderColor = borderColor; } /** * Returns <code>true</code> since this border is opaque. Being opaque it is responsible to * fill in the area within its boundaries. * * @return <code>true</code> since this border is opaque */ @Override public boolean isOpaque() { return true; } /** * {@inheritDoc} */ @Override public Insets getInsets(final IFigure figure) { return new Insets(0); } /** * {@inheritDoc} */ @Override public void paint(final IFigure figure, final Graphics graphics, final Insets insets) { graphics.setForegroundColor(_borderColor); graphics.setLineWidth(_borderWidth); Rectangle borderBounds = figure.getBounds(); // outer arc graphics.drawArc((int) (_imgWidth / 2 - _innR) + borderBounds.x, (int) (_r - _innR) + _topDelta + borderBounds.y, (int) _innR * 2, (int) _innR * 2, (int) Math .round(90.0 - _angle / 2), _angle); // inner arc graphics.drawArc(_imgWidth / 2 - (int) _r + borderBounds.x, _topDelta + borderBounds.y, (int) (2.0 * _r), (int) (2.0 * _r), (int) Math.round(90.0 - _angle / 2), _angle); // left and right lines ShadedDrawing.drawLineAtAngle(graphics, _innR, _r, 90 + _angle / 2, _imgWidth / 2 + borderBounds.x, (int) _r + _topDelta + borderBounds.y); ShadedDrawing.drawLineAtAngle(graphics, _innR, _r, 90 - _angle / 2, _imgWidth / 2 + borderBounds.x, (int) _r + _topDelta + borderBounds.y); } } }