/* * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton, * 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.LinkedList; import java.util.List; import java.util.Map; 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.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.FigureListener; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.Panel; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.XYLayout; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.swt.graphics.Color; /** * A bargraph figure. * * @author Kai Meyer * */ public final class RefreshableBargraphFigure extends RectangleFigure implements IAdaptable { /** * Height of the text. */ private static final int TEXTHEIGHT = 14; /** * Width of the text. */ private static final int TEXTWIDTH = 46; /** * Width of the text. */ private int _textWidth = 10; /** * The Strings, which are displayed in this figure. */ private static final String[] LABELS = new String[] { "LOLO", "LO", "HI", "HIHI" }; /** * Show markers at bottom or right. */ private static final int BOTTOM_RIGHT = 1; /** * Show markers at top or left. */ private static final int TOP_LEFT = 2; /** * Minimum value for this figure. */ private double _minimum = 0.0; /** * Maximum value for this figure. */ private double _maximum = 1.0; /** * The current value. */ private double _value; /** * The boolean, which indicates, if the values should be shown or not. */ private boolean _showValues = false; /** * The integer, which indicates, how the marks should be shown. */ private int _showMarks = BOTTOM_RIGHT; /** * The integer, which indicates, how the scale should be shown. */ private int _showScale = BOTTOM_RIGHT; /** * The count of sections in the scale. */ private int _scaleSectionCount = 1; /** * The orientation (horizontal==true | vertical==false). */ private boolean _orientationHorizontal = true; /** * The Color for the border. */ private Color _borderColor; /** * The Map for the levels. */ private final Map<String, Double> _levelMap = new HashMap<String, Double>(); /** * A border adapter, which covers all border handlings. */ private IBorderEquippedWidget _borderAdapter; /** * The Scale of this figure. */ private final Scale _scale; /** * The MarkerPanel of this figure. */ private final MarkerPanel _markerPanel; /** * The FillRectangle of this figure. */ private final FillRectangleFigure _fillRectangleFigure; /** * The Rectangle for the FillRectangleFigure. */ private Rectangle _barRectangle; /** * The wideness of the Scale. */ private final int _scaleWideness = 10; /** * The wideness of the Tickmarks. */ private final int _tickMarkWideness = 10; /** * The boolean, which indicates, if the figure has a transparent background. */ private boolean _transparent = true; /** * The boolean, which indicates, if only the fill value should be shown. */ private boolean _showOnlyValue = false; private CrossedOutAdapter _crossedOutAdapter; private RhombusAdapter _rhombusAdapter; /** * Constructor. */ public RefreshableBargraphFigure() { super(); this.setSize(200, 30); this.initLevelMap(); this.setLayoutManager(new XYLayout()); _fillRectangleFigure = new FillRectangleFigure(); _markerPanel = new MarkerPanel(_showMarks == TOP_LEFT, _orientationHorizontal); _scale = new Scale(); _scale.setShowNegativeSections(true); this.add(_fillRectangleFigure); this.add(_markerPanel); this.add(_scale); this.refreshConstraints(); // listen to figure movement events addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { refreshConstraints(); } }); } /** * Initializes the Map of levels. */ private void initLevelMap() { _levelMap.put(LABELS[0], Double.valueOf(0.1)); _levelMap.put(LABELS[1], Double.valueOf(0.3)); _levelMap.put(LABELS[2], Double.valueOf(0.7)); _levelMap.put(LABELS[3], Double.valueOf(0.9)); } @Override public void paint(final Graphics graphics) { super.paint(graphics); _crossedOutAdapter.paint(graphics); _rhombusAdapter.paint(graphics); } /** * {@inheritDoc} */ @Override public synchronized void paintFigure(final Graphics graphics) { if (!_transparent) { graphics.setBackgroundColor(this.getBackgroundColor()); Rectangle bounds = this.getBounds().getCopy(); bounds.crop(this.getInsets()); graphics.fillRectangle(bounds); graphics.setBackgroundColor(this.getBackgroundColor()); graphics.setForegroundColor(_borderColor); } } // /** // * Gets the IFigure for the tooltip. // * @return IFigure The IFigure for the tooltip // */ // private IFigure getToolTipFigure() { // Panel panel = new Panel(); // panel.setLayoutManager(new ToolbarLayout(false)); // panel.add(new Label("Value: " + _value)); // panel.add(new Label("Minimum value: " + _minimum)); // panel.add(new Label("Maximum value: " + _maximum)); // for (int i = 0; i < LABELS.length; i++) { // panel.add(new Label(LABELS[i] + " level: " // + _levelMap.get(LABELS[i]))); // } // panel.setBackgroundColor(ColorConstants.tooltipBackground); // return panel; // } /** * Refreshes the Constraints. */ public void refreshConstraints() { this.approximateTextWidth(); _barRectangle = this.getBarRectangle(); this.setConstraint(_fillRectangleFigure, _barRectangle); Rectangle bounds = this.getBounds().getCopy().crop(this.getInsets()); this.setConstraint(_markerPanel, this.getMarkerPanelConstraint(bounds)); this.setConstraint(_scale, this.getScaleConstraint(bounds)); //this.setToolTip(this.getToolTipFigure()); } /** * Gets the Constraints for the MarkerPanel. * @param bounds * The bounds for the MarkerPanel * @return The constraints for the MarkerPanel */ private Rectangle getMarkerPanelConstraint(final Rectangle bounds) { if (_orientationHorizontal) { _markerPanel.setReferencePositions(_barRectangle.x, _barRectangle.x + _barRectangle.width); if (_showMarks == BOTTOM_RIGHT) { return new Rectangle(1, bounds.height - (TEXTHEIGHT+_tickMarkWideness), bounds.width - 2, (TEXTHEIGHT+_tickMarkWideness) - 2); } if (_showMarks == TOP_LEFT) { return new Rectangle(1, 1, bounds.width - 2, (TEXTHEIGHT+_tickMarkWideness) - 1); } } else { _markerPanel.setReferencePositions(_barRectangle.y, _barRectangle.y + _barRectangle.height); if (_showMarks == BOTTOM_RIGHT) { return new Rectangle(_barRectangle.width+(_barRectangle.x), 1, TEXTWIDTH - 1, bounds.height - 2); } if (_showMarks == TOP_LEFT) { return new Rectangle(1, 1, TEXTWIDTH - 1, bounds.height - 2); } } return new Rectangle(0, 0, 0, 0); } /** * Gets the constraints for the Scale. * @param bounds * The bounds for the Scale * @return Rectangle The Constraints for the Scale */ private Rectangle getScaleConstraint(final Rectangle bounds) { _scale.setHorizontalOrientation(_orientationHorizontal); _scale.setIncrement((_maximum-_minimum)/Math.max(1, _scaleSectionCount)); _scale.setStartValue(_minimum); if (_orientationHorizontal) { return getHorizontalScaleConstraint(bounds); } else { return getVerticalScaleConstraint(bounds); } } /** * Gets the constraints for the Vertical Scale. * @param bounds * The bounds for the Vertical Scale * @return Rectangle The Constraints for the Vertical Scale */ private Rectangle getVerticalScaleConstraint(final Rectangle bounds) { int length = _barRectangle.height/Math.max(1, _scaleSectionCount); if (_scaleSectionCount==1) { length--; } _scale.setLength(length); _scale.setReferencePositions(_barRectangle.y+_barRectangle.height); _scale.setRegion(_barRectangle.y-5, _barRectangle.y+_barRectangle.height); int width = _scaleWideness; if (_showValues) { width = width + _textWidth; } if (_showScale == BOTTOM_RIGHT) { return new Rectangle(_barRectangle.x + _barRectangle.width - _scaleWideness/2, 0, width, bounds.height); } if (_showScale == TOP_LEFT) { int x = _barRectangle.x-_scaleWideness/2; if (_showValues) { x = x- _textWidth; } return new Rectangle(x, 0, width, bounds.height); } return new Rectangle(0, 0, 0, 0); } /** * Gets the constraints for the Horizontal Scale. * @param bounds * The bounds for the Horizontal Scale * @return Rectangle The Constraints for the Horizontal Scale */ private Rectangle getHorizontalScaleConstraint(final Rectangle bounds) { _scale.setReferencePositions(_barRectangle.x); int length = _barRectangle.width/Math.max(1, _scaleSectionCount); if (_scaleSectionCount==1) { length--; } _scale.setLength(length); _scale.setRegion(_barRectangle.x, _barRectangle.x+_barRectangle.width+5); int height = _scaleWideness; if (_showValues) { height = height + TEXTHEIGHT; } if (_showScale == BOTTOM_RIGHT) { return new Rectangle(0, _barRectangle.y + _barRectangle.height - _scaleWideness/2 - 1, bounds.width, height); } if (_showScale == TOP_LEFT) { int y = _barRectangle.y-_scaleWideness/2; if (_showValues) { y = y - TEXTHEIGHT; } return new Rectangle(0, y, bounds.width, height); } return new Rectangle(0, 0, 0, 0); } /** * Calculate the real length of this bargraph. The value is calculated, to * fit the scale completely into the bargraph * @param length * The given length * @return int The new length */ private int calculateRealLength(final int length) { if ((_showScale == BOTTOM_RIGHT) || (_showScale == TOP_LEFT)) { int neededScaleLines = _scaleSectionCount + 1; return length - ((length - neededScaleLines) % _scaleSectionCount); } return length; } /** * Gets the rectangle for the bargraph. * @return Rectangle The rectangle for the bargraph */ private Rectangle getBarRectangle() { Rectangle bounds = this.getBounds().getCopy(); bounds.crop(this.getInsets()); if (_orientationHorizontal) { return getHorizontalBarRectangle(bounds); } else { return getVerticalBarRectangle(bounds); } } /** * Gets the rectangle for the vertical bargraph. * @return Rectangle The rectangle for the vertical bargraph */ private Rectangle getVerticalBarRectangle(final Rectangle bounds) { int yCorrection = 0; int xCorrection = 0; int widthCorrection = 0; if (_showMarks == TOP_LEFT) { xCorrection = TEXTWIDTH; yCorrection = (TEXTHEIGHT+_tickMarkWideness) / 2; } if (_showMarks == BOTTOM_RIGHT) { widthCorrection = TEXTWIDTH; yCorrection = (TEXTHEIGHT+_tickMarkWideness) / 2; } if (_showScale == TOP_LEFT) { if (xCorrection==0) { xCorrection = _scaleWideness/2; if (_showValues) { xCorrection = xCorrection + _textWidth; yCorrection = (TEXTHEIGHT+_scaleWideness) / 2; } } } if (_showScale == BOTTOM_RIGHT) { if (widthCorrection==0) { widthCorrection = _scaleWideness/2; if (_showValues) { widthCorrection = widthCorrection + _textWidth; yCorrection = (TEXTHEIGHT+_scaleWideness) / 2; } } } return new Rectangle(xCorrection, yCorrection, bounds.width - (xCorrection + widthCorrection), this .calculateRealLength(bounds.height - 2 * yCorrection)); } /** * Gets the rectangle for the horizontal bargraph. * @return Rectangle The rectangle for the horizontal bargraph */ private Rectangle getHorizontalBarRectangle(final Rectangle bounds) { int yCorrection = 0; int heightCorrection = 0; int xCorrection = 0; if (_showMarks == TOP_LEFT) { yCorrection = (TEXTHEIGHT+_tickMarkWideness); xCorrection = (TEXTWIDTH / 2); } else if (_showMarks == BOTTOM_RIGHT) { heightCorrection = (TEXTHEIGHT+_tickMarkWideness); xCorrection = (TEXTWIDTH / 2); } if (_showScale == TOP_LEFT) { if (yCorrection==0) { yCorrection = _scaleWideness/2; if (_showValues) { yCorrection = yCorrection + TEXTHEIGHT; xCorrection = (_textWidth / 2); } } } if (_showScale == BOTTOM_RIGHT) { if (heightCorrection==0) { heightCorrection = _scaleWideness/2; if (_showValues) { heightCorrection = heightCorrection + TEXTHEIGHT; xCorrection = (_textWidth / 2); } } } return new Rectangle(xCorrection, yCorrection, this .calculateRealLength(bounds.width - 2 * xCorrection), bounds.height - (yCorrection + heightCorrection)); } /** * Gets the weight (0.0 - 1.0) for the value. * @param value * The value, which weight should be calculated. * @return double The weight for the value */ private double getWeight(final double value) { double max = _maximum - _minimum; if (max == 0) { max = 0.001; } return (value - _minimum) / max; } /** * Calculates and also sets the fill grade for the bar based on the current value and * min/max borders. */ private void updateFillRectangle() { double fillgrade = (_value - _minimum) / (_maximum - _minimum); _fillRectangleFigure.setFill(fillgrade); } /** * This method is a tribute to unit tests, which need a way to test the * performance of the figure implementation. Implementors should produce * some random changes and refresh the figure, when this method is called. * */ public void randomNoiseRefresh() { this.setFill(Math.random() * 100); repaint(); } /** * Sets if only the value should be shown instead of the area from minimum to fill level. * @param showOnlyValue True if only the value should be shown, false otherwise */ public void setShowOnlyValue(final boolean showOnlyValue) { _showOnlyValue = showOnlyValue; } /** * {@inheritDoc} */ @Override public void setForegroundColor(final Color fg) { _scale.setForegroundColor(fg); super.setForegroundColor(fg); } /** * {@inheritDoc} */ @Override public void setBackgroundColor(final Color bg) { _markerPanel.setBackgroundColor(bg); super.setBackgroundColor(bg); } /** * Sets the color for the border. * @param borderRGB * The RGB-value of the Color for the border */ public void setBorderColor(final Color borderRGB) { _borderColor = borderRGB; _fillRectangleFigure.setBorderColor(_borderColor); } /** * Sets the default fill Color. * @param color * the color */ public void setDefaultFillColor(final Color color) { _fillRectangleFigure.setDefaultFillColor(color); } /** * Gets the default fill Color. * @return Color The color default fill Color */ public Color getDefaultFillColor() { return _fillRectangleFigure.getDefaultFillColor(); } /** * Sets the color for the background of the fill-area. * @param color * the background color */ public void setFillBackgroundColor(final Color color) { _fillRectangleFigure.setBackgroundColor(color); } /** * Sets the fill grade. * @param value * The fill grade. */ public void setFill(final double value) { _value = value; updateFillRectangle(); } /** * Gets the fill grade. * @return double The fill grade */ public double getFill() { return _fillRectangleFigure.getFill(); } /** * Sets the orientation (horizontal==true | vertical==false). * @param horizontal * The orientation. */ public void setOrientation(final boolean horizontal) { _orientationHorizontal = horizontal; _scale.setHorizontalOrientation(_orientationHorizontal); _markerPanel.setHorizontalOrientation(horizontal); this.refreshConstraints(); } /** * Gets the orientation (horizontal==true | vertical==false). * @return boolean The orientation */ public boolean getOrientation() { return _orientationHorizontal; } /** * Sets the minimum value. * @param min * The minimum value */ public void setMinimum(final double min) { _minimum = min; this.refreshConstraints(); updateFillRectangle(); } /** * Gets the minimum value. * @return double The minimum value */ public double getMinimum() { return _minimum; } /** * Sets the lolo level. * @param loloLevel * The lolo level */ public void setLoloLevel(final double loloLevel) { _levelMap.put(LABELS[0], loloLevel); this.refreshConstraints(); } /** * Gets the lolo level. * @return double The lolo level */ public double getLoloLevel() { return _levelMap.get(LABELS[0]); } /** * Sets the lo level. * @param loLevel * The lo level */ public void setLoLevel(final double loLevel) { _levelMap.put(LABELS[1], loLevel); this.refreshConstraints(); } /** * Gets the lo level. * @return double The lo level */ public double getLoLevel() { return _levelMap.get(LABELS[1]); } /** * Sets the hi level. * @param hiLevel * The hi level */ public void setHiLevel(final double hiLevel) { _levelMap.put(LABELS[2], hiLevel); this.refreshConstraints(); } /** * Gets the hi level. * @return double The hi level */ public double getHiLevel() { return _levelMap.get(LABELS[2]); } /** * Sets the hihi level. * @param hihiLevel * The hihi level */ public void setHihiLevel(final double hihiLevel) { _levelMap.put(LABELS[3], hihiLevel); this.refreshConstraints(); } /** * Gets the hihi level. * @return double The hihi level */ public double getHihiLevel() { return _levelMap.get(LABELS[3]); } /** * Sets the maximum value. * @param max * The maximum value */ public void setMaximum(final double max) { _maximum = max; this.refreshConstraints(); updateFillRectangle(); } public void approximateTextWidth() { float width = 14; if ((this.getFont()!=null) && (this.getFont().getFontData().length>0) && (this.getFont().getFontData()[0]!=null)) { width = this.getFont().getFontData()[0].height; } NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(2); int minLength = (int) (format.format(_minimum).length()*width); _textWidth = minLength; if ((_showScale==BOTTOM_RIGHT) || (_showScale==TOP_LEFT)) { double increment = (_maximum-_minimum)/Math.max(1, _scaleSectionCount); for (int i=0;i<_scaleSectionCount;i++) { int value = (int) (format.format(minLength + (i*increment)).length()*width); _textWidth = Math.max(minLength, value); } } int maxLength = (int) (format.format(_maximum).length()*width); _textWidth = Math.max(_textWidth, maxLength); } /** * Gets the maximum value. * @return double The maximum value */ public double getMaximum() { return _maximum; } /** * Sets, if the values should be shown. * @param showValues * True, if the values should be shown, false otherwise */ public void setShowValues(final boolean showValues) { _showValues = showValues; _scale.setShowValues(showValues); this.refreshConstraints(); } /** * Gets, if the values should be shown. * @return boolean True, if the values should be shown, false otherwise */ public boolean getShowValues() { return _showValues; } /** * Sets, how the marks should be shown. * @param showMarks * 0 = don't show, 1 = show Bottom/Right, 2 = show Top/Left */ public void setShowMarks(final int showMarks) { _showMarks = showMarks; _markerPanel.setTopLeftAlignment(showMarks == TOP_LEFT); this.refreshConstraints(); } /** * Gets, how the marks should be shown. * @return boolean 0 = don't show, 1 = show Bottom/Right, 2 = show Top/Left */ public int getShowMarks() { return _showMarks; } /** * Sets, how the scale should be shown. * * @param showScale * 0 = don't show, 1 = show Bottom/Right, 2 = show Top/Left */ public void setShowScale(final int showScale) { _showScale = showScale; _scale.setAlignment(showScale == TOP_LEFT); this.refreshConstraints(); } /** * Gets, how the scale should be shown. * * @return int 0 = don't show, 1 = show Bottom/Right, 2 = show Top/Left */ public int getShowScale() { return _showScale; } /** * Sets the count of sections in the scale. * * @param scaleSectionCount * The count of sections in the scale */ public void setScaleSectionCount(final int scaleSectionCount) { _scaleSectionCount = scaleSectionCount; this.refreshConstraints(); } /** * Gets the count of sections in the scale. * * @return int The count of sections in the scale. */ public int getScaleSectionCount() { return _scaleSectionCount; } /** * Sets, if this widget should have a transparent background. * @param transparent * The new value for the transparent property */ public void setTransparent(final boolean transparent) { _transparent = transparent; } /** * {@inheritDoc} */ @Override @SuppressWarnings("rawtypes") public Object getAdapter(final Class adapter) { if (adapter == IBorderEquippedWidget.class) { if (_borderAdapter == null) { _borderAdapter = new BorderAdapter(this); } 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; } /** * This class represents the bargraph. * * @author Kai Meyer */ private final class FillRectangleFigure extends RectangleFigure { /** * The fill grade (0 - 1). */ private double _fillGrade = 0.5; /** * The default fill Color. */ private Color _defaultFillColor; /** * The Color for the border. */ private Color _borderColor; /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { Rectangle bounds = this.getBounds(); graphics.setForegroundColor(this.getForegroundColor()); graphics.setBackgroundColor(this.getBackgroundColor()); graphics.fillRectangle(bounds); graphics.setBackgroundColor(this.getDefaultFillColor()); if (_showOnlyValue) { PointList list = new PointList(); if (_orientationHorizontal) { int valueX = (int) Math.round(bounds.width * (this.getFill())) + bounds.x; list.addPoint(valueX, bounds.y); list.addPoint(valueX-4, bounds.y+(bounds.height/2)); list.addPoint(valueX, bounds.y+bounds.height); list.addPoint(valueX+4, bounds.y+(bounds.height/2)); } else { int valueY = (int) Math.round(bounds.y + bounds.height - (bounds.height * (this.getFill()))); list.addPoint(bounds.x, valueY); list.addPoint(bounds.x+(bounds.width/2), valueY-4); list.addPoint(bounds.x+bounds.width, valueY); list.addPoint(bounds.x+(bounds.width/2), valueY+4); } graphics.fillPolygon(list); graphics.drawPolygon(list); } else { Rectangle fillRectangle = this.getFillLevelRectangle(bounds); graphics.fillRectangle(fillRectangle); graphics.setForegroundColor(this.getBorderColor()); graphics.drawRectangle(new Rectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1)); graphics.drawRectangle(new Rectangle(fillRectangle.x, fillRectangle.y, fillRectangle.width - 1, fillRectangle.height - 1)); } } /** * Gets the rectangle for the fill level. * * @param area * The rectangle of the bargraph * @return Rectangle The rectangle for the fill level */ private Rectangle getFillLevelRectangle(final Rectangle area) { if (_orientationHorizontal) { int newW = (int) Math.round(area.width * (this.getFill())); return new Rectangle(area.getLocation(), new Dimension(newW+1, area.height)); } int newH = (int) Math.round(area.height * (this.getFill())) +1; return new Rectangle(area.x, area.y + area.height - newH, area.width, newH); } /** * Sets the fill grade. * @param fill * The fill grade. */ public void setFill(final double fill) { _fillGrade = fill; } /** * Gets the fill grade. * @return double The fill grade */ public double getFill() { return _fillGrade; } /** * Sets the default fill Color. * @param defaultFillColor * The default fill Color */ public void setDefaultFillColor(final Color defaultFillColor) { _defaultFillColor = defaultFillColor; } /** * Gets the default fill Color. * @return Color The color default fill Color */ public Color getDefaultFillColor() { return _defaultFillColor; } /** * Sets the color for the border. * @param borderColor * The Color for the border */ public void setBorderColor(final Color borderColor) { _borderColor = borderColor; } /** * Gets the color for the border. * @return Color The color for the border */ public Color getBorderColor() { return _borderColor; } } /** * This Figure contains the Markers. * @author Kai Meyer */ private final class MarkerPanel extends Panel { /** * The List of Markers. */ private final List<Marker> _markerList = new LinkedList<Marker>(); /** * The alignment for the Markers. */ private boolean _topLeft; /** * The orientation of this figure. */ private boolean _isHorizontal; /** * The start position. */ private int _start = 10; /** * The end position. */ private int _end = 100; /** * Constructor. * * @param topLeft * true, if the Marker is on top/left of the bargraph, false * otherwise * @param isHorizontal * true, if the Marker has a horizontal orientation */ public MarkerPanel(final boolean topLeft, final boolean isHorizontal) { this.setLayoutManager(new XYLayout()); _topLeft = topLeft; _isHorizontal = isHorizontal; Marker marker = null; for (String element : LABELS) { marker = new Marker(element, _topLeft, _isHorizontal); marker.setForegroundColor(this.getForegroundColor()); this.add(marker); _markerList.add(marker); } // listen to figure movement events addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { refreshConstraints(); } }); this.refreshConstraints(); } /** * Refreshes the constraints. */ private void refreshConstraints() { if (_isHorizontal) { for (int i = 0; i < _markerList.size(); i++) { double weight = getWeight(_levelMap.get(LABELS[i])); if (Double.isNaN(weight) || (weight < 0) || (weight > 1)) { _markerList.get(i).setVisible(false); } else { _markerList.get(i).setVisible(true); int x = _start + (int) ((_end - _start) * weight) - 1 - TEXTWIDTH / 2; this.setConstraint(_markerList.get(i), new Rectangle(x, 0, TEXTWIDTH, bounds.height)); } } } else { for (int i = 0; i < _markerList.size(); i++) { double weight = getWeight(_levelMap.get(LABELS[i])); if (Double.isNaN(weight) || (weight < 0) || (weight > 1)) { _markerList.get(i).setVisible(false); } else { _markerList.get(i).setVisible(true); int y = _start -1 + (int) ((_end - _start) * (1 - weight)) - (TEXTHEIGHT+_tickMarkWideness)/2; this.setConstraint(_markerList.get(i), new Rectangle(1, y, bounds.width, (TEXTHEIGHT+_tickMarkWideness))); } } } } /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { if (!_transparent) { Rectangle bounds = this.getBounds(); graphics.setBackgroundColor(this.getBackgroundColor()); graphics.setForegroundColor(ColorConstants.black); graphics.fillRectangle(bounds); } } /** * Sets the reference values for this figure. * * @param start * The start value * @param end * The end value */ public void setReferencePositions(final int start, final int end) { _start = start; _end = end; this.refreshConstraints(); } /** * Sets the orientation of this figure. * * @param isHorizontal * The orientation of this figure * (true=horizontal;false=vertical) */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = isHorizontal; for (Marker marker : _markerList) { marker.setHorizontalOrientation(isHorizontal); } this.refreshConstraints(); } /** * Sets the alignment of this figure. * * @param topLeft * The alignment of this figure * (true=top/left;false=bottom/right) */ public void setTopLeftAlignment(final boolean topLeft) { _topLeft = topLeft; for (Marker marker : _markerList) { marker.setTopLeftAlignment(topLeft); } this.refreshConstraints(); } } /** * This class represents a Marker. * * @author Kai Meyer * */ private final class Marker extends RectangleFigure { /** * The orientation of this Marker. */ private boolean _isHorizontal; /** * The alignment of this Marker. */ private boolean _topLeft; /** * The needed space of a {@link TickMark}. */ private final int _tickMarkSpace = 9; /** * The Label for the text. */ private final Label _textLabel; /** * The Tickmark. */ private final TickMark _tickMark; /** * Constructor. * * @param key * The text to display * @param topLeft * True, if the marker should be above the y value, false * otherwise * @param isHorizontal * True, if the marker should have a horizontal orientation, * false otherwise */ public Marker(final String key, final boolean topLeft, final boolean isHorizontal) { this.setLayoutManager(new XYLayout()); _textLabel = new Label(key.toString()); _textLabel.setForegroundColor(this.getForegroundColor()); _tickMark = new TickMark(); _tickMark.setForegroundColor(this.getForegroundColor()); _tickMark.setBackgroundColor(getDefaultFillColor()); this.add(_tickMark); this.add(_textLabel); this.refreshConstraints(); addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { refreshConstraints(); } }); } /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { // nothing to do; } /** * {@inheritDoc} */ @Override public void setVisible(final boolean visible) { _textLabel.setVisible(visible); _tickMark.setVisible(visible); } /** * Sets the orientation of this figure. * * @param isHorizontal * The orientation of this figure * (true=horizontal;false=vertical) */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = isHorizontal; _tickMark.setHorizontalOrientation(isHorizontal); this.refreshLabel(); } /** * Sets the alignment of this figure. * * @param topLeft * The alignment of this figure * (true=top/left;false=bottom/right) */ public void setTopLeftAlignment(final boolean topLeft) { _topLeft = topLeft; _tickMark.setTopLeftAlignment(topLeft); this.refreshLabel(); } private void refreshConstraints() { Rectangle bounds = this.getBounds(); if (_isHorizontal) { if (_topLeft) { this.setConstraint(_tickMark, new Rectangle(0, bounds.height-_tickMarkSpace, bounds.width, _tickMarkSpace)); this.setConstraint(_textLabel, new Rectangle(0, 0, bounds.width, bounds.height-_tickMarkSpace)); } else { this.setConstraint(_tickMark, new Rectangle(0, 0, bounds.width, _tickMarkSpace)); this.setConstraint(_textLabel, new Rectangle(0, _tickMarkSpace, bounds.width, bounds.height-_tickMarkSpace)); } } else { if (_topLeft) { this.setConstraint(_tickMark, new Rectangle(bounds.width-_tickMarkSpace, 0, _tickMarkSpace, bounds.height)); this.setConstraint(_textLabel, new Rectangle(0, 0, bounds.width-_tickMarkSpace, bounds.height)); } else { this.setConstraint(_tickMark, new Rectangle(0, 0, _tickMarkSpace, bounds.height)); this.setConstraint(_textLabel, new Rectangle(_tickMarkSpace, 0, bounds.width-_tickMarkSpace, bounds.height)); } } refreshLabel(); } /** * Refreshes the Label. */ private void refreshLabel() { if (_isHorizontal) { _textLabel.setTextPlacement(PositionConstants.WEST); if (_topLeft) { _textLabel.setTextAlignment(PositionConstants.BOTTOM); } else { _textLabel.setTextAlignment(PositionConstants.TOP); } } else { _textLabel.setTextPlacement(PositionConstants.NORTH); if (_topLeft) { _textLabel.setTextAlignment(PositionConstants.RIGHT); } else { _textLabel.setTextAlignment(PositionConstants.LEFT); } } } /** * This class represents a tigmark. * * @author Kai Meyer */ private final class TickMark extends RectangleFigure { /** * The width of this marker. */ private int _width; /** * The height of this marker. */ private int _height; /** * The direction of this Marker. */ private int _direction = 1; /** * The orientation of this Marker. */ private boolean _isHorizontal; /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { Rectangle bounds = this.getBounds(); graphics.setForegroundColor(this.getForegroundColor()); graphics.setBackgroundColor(getDefaultFillColor()); PointList pointList = new PointList(); int x; int y; if (_isHorizontal) { x = bounds.x + bounds.width / 2; y = bounds.y; if (_direction < 0) { y = y + bounds.height; } pointList.addPoint(x, y); pointList.addPoint(x - _width, y + _height * _direction); pointList.addPoint(x + _width, y + _height * _direction); pointList.addPoint(x, y); } else { x = bounds.x; y = bounds.y + bounds.height / 2; if (_direction < 0) { x = x + bounds.width - 2; } pointList.addPoint(x, y); pointList.addPoint(x + _width * _direction, y - _height); pointList.addPoint(x + _width * _direction, y + _height); pointList.addPoint(x, y); } graphics.fillPolygon(pointList); graphics.drawPolyline(pointList); } /** * Sets the orientation of this figure. * * @param isHorizontal * The orientation of this figure * (true=horizontal;false=vertical) */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = isHorizontal; if (isHorizontal) { _width = 3; _height = 5; } else { _width = 5; _height = 3; } } /** * Sets the alignment of this figure. * * @param topLeft * The alignment of this figure * (true=top/left;false=bottom/right) */ public void setTopLeftAlignment(final boolean topLeft) { if (topLeft) { _direction = -1; } else { _direction = 1; } } } } /** * This class represents a scale. * * @author Kai Meyer */ private final class Scale extends RectangleFigure { /** * The length of this Scale. */ private int _length; /** * The direction of this Scale. */ private boolean _isHorizontal; /** * The Alignment for the {@link ScaleMarker}s. */ private boolean _isTopLeft; /** * The start position. */ private int _refPos = 10; /** * The begin of the region, which surrounds the Markers. */ private int _begin; /** * The end of the region, which surrounds the Markers. */ private int _end; /** * True, if the negativ sections should be draan, false otherwise. */ private boolean _showNegativSections = false; /** * The lenght of the lines. */ private final int _wideness = 10; /** * True, if the first Marker should be shown, false otherwise. */ private final boolean _showFirst = true; /** * True, if the values of the Markers should be shown, false otherwise. */ private boolean _showValues = false; /** * The size of one step in a Scale. */ private double _increment = 1; /** * The start-value for the markers. */ private double _startValue = 0; /** * The List of positive ScaleMarkers. */ private final List<ScaleMarker> _posScaleMarkers = new LinkedList<ScaleMarker>(); /** * The List of negative ScaleMarkers. */ private final List<ScaleMarker> _negScaleMarkers = new LinkedList<ScaleMarker>(); /** * Constructor. */ public Scale() { this.setLayoutManager(new XYLayout()); this.refreshConstraints(); // listen to figure movement events addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { refreshConstraints(); } }); } /** * Refreshes the Constraints. */ private void refreshConstraints() { if ((_length==0) || (this.getBounds().height==0) || (this.getBounds().width==0)) { _posScaleMarkers.clear(); _negScaleMarkers.clear(); this.removeAll(); return; } if (_isHorizontal) { refreshHorizontalConstraints(); } else { refreshVerticalConstraints(); } } /** * Refreshes the Constraints at Vertical orientation. */ private void refreshVerticalConstraints() { int index = 0; int pos = _refPos; pos = pos - 1; int width = _wideness; if (_showValues) { width = _textWidth + _wideness; } double value = _startValue; while ((pos >= 0) && (pos >= _begin)) { if (pos<=_end) { if (index>=_posScaleMarkers.size()) { this.addScaleMarker(index, _posScaleMarkers); } this.setConstraint(_posScaleMarkers.get(index), new Rectangle(0,pos-TEXTHEIGHT/2,width,TEXTHEIGHT)); this.refreshScaleMarker(_posScaleMarkers.get(index), value, (((index>0) || _showFirst) && _showValues)); index++; } value = value + _increment; pos = pos - _length; } this.removeScaleMarkers(index, _posScaleMarkers); if (_showNegativSections) { pos = _refPos + _length - 1; index = 0; value = _startValue - _increment; while ((pos < this.getBounds().height) && (pos <= _end)) { if (pos>=_begin) { if (index>=_negScaleMarkers.size()) { this.addScaleMarker(index, _negScaleMarkers); } this.setConstraint(_negScaleMarkers.get(index), new Rectangle(0,pos-TEXTHEIGHT/2,width,TEXTHEIGHT)); this.refreshScaleMarker(_negScaleMarkers.get(index), value, _showValues); index++; } value = value - _increment; pos = pos + _length; } this.removeScaleMarkers(index, _negScaleMarkers); } } /** * Refreshes the Constraints at horizontal orientation. */ private void refreshHorizontalConstraints() { int index = 0; int pos = _refPos; int height = _wideness; if (_showValues) { height = TEXTHEIGHT + _wideness; } double value = _startValue; while ((pos <= this.getBounds().width) && (pos <= _end)) { if (pos>=_begin) { if (index>=_posScaleMarkers.size()) { this.addScaleMarker(index, _posScaleMarkers); } this.setConstraint(_posScaleMarkers.get(index), new Rectangle(pos-_textWidth/2,0,_textWidth,height)); this.refreshScaleMarker(_posScaleMarkers.get(index), value, (((index>0) || _showFirst) && _showValues)); index++; } value = value + _increment; pos = pos + _length; } this.removeScaleMarkers(index, _posScaleMarkers); if (_showNegativSections) { pos = _refPos - _length; index = 0; value = _startValue - _increment; while ((pos > 0) && (pos >= _begin)) { if (pos<=_end) { if (index>=_negScaleMarkers.size()) { this.addScaleMarker(index, _negScaleMarkers); } this.setConstraint(_negScaleMarkers.get(index), new Rectangle(pos-_textWidth/2,0,_textWidth,height)); this.refreshScaleMarker(_negScaleMarkers.get(index), value, _showValues); index++; } value = value - _increment; pos = pos - _length; } this.removeScaleMarkers(index, _negScaleMarkers); } } /** * Refreshes the given ScaleMarker. * @param marker * The ScaleMarker, which should be refreshed * @param labelValue * The new value for the displayed text * @param showValue * True, if the value should be shown, false otherwise */ private void refreshScaleMarker(final ScaleMarker marker, final double labelValue, final boolean showValue) { marker.setTopLeftAlignment(_isTopLeft); marker.setHorizontalOrientation(!_isHorizontal); NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(2); String text = format.format(labelValue); marker.setText(text); marker.setShowValues(showValue); marker.setWideness(_wideness); } /** * Adds a new ScaleMarker into the given List at the given index. * @param index * The index * @param scaleMarkers * The List of ScaleMarkers */ private void addScaleMarker(final int index, final List<ScaleMarker> scaleMarkers) { ScaleMarker marker = new ScaleMarker(); scaleMarkers.add(index, marker); this.add(marker); } /** * Removes all ScaleMarkers in the given List, beginning by the given index. * @param index * The index * @param scaleMarkers * The List of ScaleMarkers */ private void removeScaleMarkers(final int index, final List<ScaleMarker> scaleMarkers) { if (!scaleMarkers.isEmpty() && (index<=scaleMarkers.size())) { while (index<scaleMarkers.size()) { this.remove(scaleMarkers.remove(index)); } } } /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { //Do nothing } /** * Sets the length of this Scale. * * @param length * The length of this Scale */ public void setLength(final int length) { _length = length; } /** * Sets the orientation of this Scale. * * @param isHorizontal * The orientation of this Scale * (true=horizontal;false=vertical) */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = isHorizontal; this.refreshConstraints(); } /** * Sets the alignment for the ScaleMarker. * @param isTopLeft * The alignment for the ScaleMarker * (true=top/left; false=bottom/right) * */ public void setAlignment(final boolean isTopLeft) { _isTopLeft = isTopLeft; this.refreshConstraints(); } /** * Sets the reference values for this figure. * * @param refPos * The start value */ public void setReferencePositions(final int refPos) { _refPos = refPos; if (_refPos<0) { if (_isHorizontal) { _refPos = _refPos + 1; } else { _refPos = _refPos - 1; } } this.refreshConstraints(); } /** * The begin and the end of the region, which surrounds the Markers. * @param begin * The begin * @param end * The end */ public void setRegion(final int begin, final int end) { _begin = begin; _end = end; this.refreshConstraints(); } /** * Sets if the negative sections should be drawn. * * @param showNegativ * True, if the negativ sections should be drawn, false * otherwise. */ public void setShowNegativeSections(final boolean showNegativ) { _showNegativSections = showNegativ; } /** * Sets, if the values of the Markers should be shown. * @param showValues * True if the values of the Markers should be shown, false otherwise */ public void setShowValues(final boolean showValues) { _showValues = showValues; this.refreshConstraints(); } /** * {@inheritDoc} */ @Override public void setForegroundColor(final Color fg) { super.setForegroundColor(fg); for (ScaleMarker marker : _posScaleMarkers) { marker.setForegroundColor(fg); } for (ScaleMarker marker : _negScaleMarkers) { marker.setForegroundColor(fg); } } /** * Sets the increment for the Scale. * @param value * The value for the increment */ public void setIncrement(final double value) { _increment = value; this.refreshConstraints(); } /** * Sets the start value for the Markers. * @param startValue * The start value */ public void setStartValue(final double startValue) { _startValue = startValue; } /** * This class represents a marker for the scale. * @author Kai Meyer */ private final class ScaleMarker extends RectangleFigure { /** * The Label of this ScaleMarker. */ private final Label _textLabel; /** * The hyphen of this ScaleMarker. */ private final ScaleHyphen _scaleHyphen; /** * The orientation of this Marker. */ private boolean _isHorizontal; /** * The alignment of this Marker. */ private boolean _topLeft; /** * True, if the values of the Markers should be shown, false otherwise. */ private boolean _showValues = false; /** * The needed space of the hyphen. */ private final int _hyphenSpace = 10; /** * Constructor. */ public ScaleMarker() { this.setLayoutManager(new XYLayout()); _textLabel = new Label(""); _textLabel.setForegroundColor(this.getForegroundColor()); _scaleHyphen = new ScaleHyphen(); _scaleHyphen.setForegroundColor(this.getForegroundColor()); this.add(_scaleHyphen); this.add(_textLabel); refreshConstraints(); addFigureListener(new FigureListener() { @Override public void figureMoved(final IFigure source) { refreshConstraints(); } }); } private void refreshConstraints() { Rectangle bounds = this.getBounds(); if (_isHorizontal) { if (_topLeft) { this.setConstraint(_scaleHyphen, new Rectangle(0, bounds.height-_hyphenSpace, bounds.width, _hyphenSpace)); this.setConstraint(_textLabel, new Rectangle(0, 0, bounds.width, bounds.height-_hyphenSpace)); } else { this.setConstraint(_scaleHyphen, new Rectangle(0, 0, bounds.width, _hyphenSpace)); this.setConstraint(_textLabel, new Rectangle(0, _hyphenSpace, bounds.width, bounds.height-_hyphenSpace)); } } else { if (_topLeft) { this.setConstraint(_scaleHyphen, new Rectangle(bounds.width-_hyphenSpace, 0, _hyphenSpace, bounds.height)); this.setConstraint(_textLabel, new Rectangle(0, 0, bounds.width-_hyphenSpace, bounds.height)); } else { this.setConstraint(_scaleHyphen, new Rectangle(0, 0, _hyphenSpace, bounds.height)); this.setConstraint(_textLabel, new Rectangle(_hyphenSpace, 0, bounds.width-_hyphenSpace, bounds.height)); } } refreshLabel(); } /** * Refreshes the Label. */ private void refreshLabel() { if (_showValues) { _textLabel.setVisible(true); if (_isHorizontal) { _textLabel.setTextPlacement(PositionConstants.WEST); if (_topLeft) { _textLabel.setTextAlignment(PositionConstants.BOTTOM); } else { _textLabel.setTextAlignment(PositionConstants.TOP); } } else { _textLabel.setTextPlacement(PositionConstants.NORTH); if (_topLeft) { _textLabel.setTextAlignment(PositionConstants.RIGHT); } else { _textLabel.setTextAlignment(PositionConstants.LEFT); } } } else { _textLabel.setVisible(false); } } /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { // nothing to do } /** * Sets the orientation of this figure. * @param isHorizontal * The orientation of this figure * (true=horizontal;false=vertical) */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = !isHorizontal; _scaleHyphen.setHorizontalOrientation(isHorizontal); this.refreshLabel(); } /** * Sets the alignment of this figure. * * @param topLeft * The alignment of this figure * (true=top/left;false=bottom/right) */ public void setTopLeftAlignment(final boolean topLeft) { _topLeft = topLeft; _scaleHyphen.setAlignment(_topLeft); this.refreshLabel(); } /** * Sets the displayed text. * @param text * The text to display */ public void setText(final String text) { _textLabel.setText(text); this.refreshLabel(); } /** * Sets, if the values of the Markers should be shown. * @param showValues * True if the values of the Markers should be shown, false otherwise */ public void setShowValues(final boolean showValues) { _showValues = showValues; this.refreshLabel(); } /** * Sets the wideness of the Hyphen. * @param wideness * The wideness */ public void setWideness(final int wideness) { _scaleHyphen.setWideness(wideness); } /** * {@inheritDoc} */ @Override public void setForegroundColor(final Color fg) { super.setForegroundColor(fg); _scaleHyphen.setForegroundColor(fg); _textLabel.setForegroundColor(fg); } /** * This class represents a hyphen for the scale. * * @author Kai Meyer */ private final class ScaleHyphen extends RectangleFigure { /** * The height of the line. */ private int _height = 0; /** * The width of the line. */ private int _width = 10; /** * The orientation of the line. */ private boolean _isHorizontal; /** * The wideness of this Hyphen. */ private int _wideness = 10; /** * The Alignment of this Hyphen. */ private boolean _isTopLeft; /** * {@inheritDoc} */ @Override public void paintFigure(final Graphics graphics) { graphics.setForegroundColor(this.getForegroundColor()); //vertical int x = this.getBounds().x+this.getBounds().width/2; int y = this.getBounds().y; if (_isHorizontal) { if (_isTopLeft) { x = this.getBounds().x + this.getBounds().width-_width; y = this.getBounds().y + this.getBounds().height/2; } else { x = this.getBounds().x; y = this.getBounds().y + this.getBounds().height/2; } } graphics.drawLine(x, y, x + _width, y + _height); } /** * Sets the wight and height of this Hyphen. */ private void setHeightAndWidth() { if (_isHorizontal) { _height = 0; _width = _wideness; } else { _height = _wideness; _width = 0; } } /** * Sets the orientation of this Hyphen. * @param isHorizontal * The Orientation of this Hyphen * true=horizontal; false = vertical */ public void setHorizontalOrientation(final boolean isHorizontal) { _isHorizontal = isHorizontal; this.setHeightAndWidth(); } /** * Sets the wideness of the Hyphen. * @param wideness * The wideness */ public void setWideness(final int wideness) { _wideness = wideness; this.setHeightAndWidth(); } /** * Sets the alignment of this Hyphen. * @param isTopLeft * The alignment (true=top/left; false = bottom/right) */ public void setAlignment(final boolean isTopLeft) { _isTopLeft = isTopLeft; } } } } }