Java tutorial
/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Java is a trademark or registered trademark of Sun Microsystems, Inc. * in the United States and other countries.] * * ------------------- * ChartComposite.java * ------------------- * (C) Copyright 2006, by Henry Proudhon and Contributors. * * Original Author: Henry Proudhon (henry.proudhon AT insa-lyon.fr); * Contributor(s): David Gilbert (for Object Refinery Limited); * * Changes * ------- * 19-Jun-2006 : New class (HP); * 06-Nov-2006 : Added accessor methods for zoomInFactor and zoomOutFactor (DG); * 28-Nov-2006 : Added support for trace lines (HP); * 30-Nov-2006 : Improved zoom box handling (HP); * 06-Dec-2006 : Added (simplified) tool tip support (HP); * 11-Dec-2006 : Fixed popup menu location by fgiust, bug 1612770 (HP); * 31-Jan-2007 : Fixed some issues with the trace lines, fixed cross hair not being drawn, * added getter and setter methods for the trace lines (HP); */ /** * Based on ChartComposite.java, authors and license see above. * Removed stuff we don't need. Extended our own XComposite class. * Implemented setBackground() correctly. Used XCanvas instead of Canvas. * Fixed many memory leaks. */ package de.xirp.ui.widgets.custom; import java.awt.Point; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.swing.event.EventListenerList; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.plot.Zoomable; import de.xirp.chart.ChartUtil; import de.xirp.chart.fixedutils.SWTGraphics2D; import de.xirp.chart.fixedutils.SWTUtils; import de.xirp.ui.util.SWTUtil; import de.xirp.util.Util; /** * A SWT composite for displaying a {@link org.jfree.chart.JFreeChart} * object. <br> * <br> * The composite listens to the chart to receive notification of * changes to any component of the chart. The chart is redrawn * automatically whenever this notification is received. */ public class XChartComposite extends XComposite implements ChartChangeListener, SelectionListener, ControlListener { /** Default setting for buffer usage. */ public static final boolean DEFAULT_BUFFER_USED = false; /** The default panel width. */ public static final int DEFAULT_WIDTH = 680; /** The default panel height. */ public static final int DEFAULT_HEIGHT = 420; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 1000; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 1000; /** The minimum size required to perform a zoom on a rectangle */ public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10; /** Export pdf action command. */ public static final String EXPORT_PDF_COMMAND = "EXPORT_PDF"; //$NON-NLS-1$ /** Export png action command. */ public static final String EXPORT_PNG_COMMAND = "EXPORT_PNG"; //$NON-NLS-1$ /** Export jpeg action command. */ public static final String EXPORT_JPEG_COMMAND = "EXPORT_JPEG"; //$NON-NLS-1$ /** Zoom in (both axes) action command. */ public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH"; //$NON-NLS-1$ /** Zoom in (domain axis only) action command. */ public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN"; //$NON-NLS-1$ /** Zoom in (range axis only) action command. */ public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE"; //$NON-NLS-1$ /** Zoom out (both axes) action command. */ public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH"; //$NON-NLS-1$ /** Zoom out (domain axis only) action command. */ public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH"; //$NON-NLS-1$ /** Zoom out (range axis only) action command. */ public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH"; //$NON-NLS-1$ /** Zoom reset (both axes) action command. */ public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH"; //$NON-NLS-1$ /** Zoom reset (domain axis only) action command. */ public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN"; //$NON-NLS-1$ /** Zoom reset (range axis only) action command. */ public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE"; //$NON-NLS-1$ /** The chart that is displayed in the panel. */ public JFreeChart chart; /** The canvas to display the chart */ private XCanvas canvas; /** Storage for registered (chart) mouse listeners. */ private EventListenerList chartMouseListeners; /** * A flag that controls whether or not the off-screen buffer is * used. */ private boolean useBuffer; /** A flag that indicates that the buffer should be refreshed. */ private boolean refreshBuffer; /** A flag that indicates that the tooltips should be displayed. */ private boolean displayToolTips; /** A buffer for the rendered chart. */ private org.eclipse.swt.graphics.Image chartBuffer; /** The height of the chart buffer. */ private int chartBufferHeight; /** The width of the chart buffer. */ private int chartBufferWidth; /** * The minimum width for drawing a chart (uses scaling for smaller * widths). */ private int minimumDrawWidth; /** * The minimum height for drawing a chart (uses scaling for * smaller heights). */ private int minimumDrawHeight; /** * The maximum width for drawing a chart (uses scaling for bigger * widths). */ private int maximumDrawWidth; /** * The maximum height for drawing a chart (uses scaling for bigger * heights). */ private int maximumDrawHeight; /** The popup menu for the frame. */ private Menu popup; /** The drawing info collected the last time the chart was drawn. */ private ChartRenderingInfo info; /** The chart anchor point. */ private Point2D anchor; /** The scale factor used to draw the chart. */ private double scaleX; /** The scale factor used to draw the chart. */ private double scaleY; /** The plot orientation. */ private PlotOrientation orientation = PlotOrientation.VERTICAL; /** A flag that controls whether or not domain zooming is enabled. */ private boolean domainZoomable = false; /** A flag that controls whether or not range zooming is enabled. */ private boolean rangeZoomable = false; /** * The zoom rectangle starting point (selected by the user with a * mouse click). This is a point on the screen, not the chart * (which may have been scaled up or down to fit the panel). */ private org.eclipse.swt.graphics.Point zoomPoint = null; /** The zoom rectangle (selected by the user with the mouse). */ private transient Rectangle zoomRectangle = null; /** * The minimum distance required to drag the mouse to trigger a * zoom. */ private int zoomTriggerDistance; /** * A flag that controls whether or not horizontal tracing is * enabled. */ private boolean horizontalAxisTrace = false; /** * A flag that controls whether or not vertical tracing is * enabled. */ private boolean verticalAxisTrace = false; /** A vertical trace line. */ private transient int verticalTraceLineX; /** A horizontal trace line. */ private transient int horizontalTraceLineY; /** Menu item for zooming in on a chart (both axes). */ private XMenuItem zoomInBothMenuItem; /** Menu item for zooming in on a chart (domain axis). */ private XMenuItem zoomInDomainMenuItem; /** Menu item for zooming in on a chart (range axis). */ private XMenuItem zoomInRangeMenuItem; /** Menu item for zooming out on a chart. */ private XMenuItem zoomOutBothMenuItem; /** Menu item for zooming out on a chart (domain axis). */ private XMenuItem zoomOutDomainMenuItem; /** Menu item for zooming out on a chart (range axis). */ private XMenuItem zoomOutRangeMenuItem; /** Menu item for resetting the zoom (both axes). */ private XMenuItem zoomResetBothMenuItem; /** Menu item for resetting the zoom (domain axis only). */ private XMenuItem zoomResetDomainMenuItem; /** Menu item for resetting the zoom (range axis only). */ private XMenuItem zoomResetRangeMenuItem; /** * A flag that controls whether or not file extensions are * enforced. */ private boolean enforceFileExtensions; /** The factor used to zoom in on an axis range. */ private double zoomInFactor = 0.5; /** The factor used to zoom out on an axis range. */ private double zoomOutFactor = 2.0; /** The parent */ private Composite parent; /** The robot name */ private String robotName; /** * Constructs a {@link org.jfree.chart.JFreeChart chart} * composite. * * @param parent * The parent. * @param style * The style of the composite. * @param chart * The chart. * @param useBuffer * A flag controlling whether or not an off-screen * buffer is used. * @see org.jfree.chart.JFreeChart */ public XChartComposite(Composite parent, int style, JFreeChart chart, boolean useBuffer, String robotName) { this(parent, style, chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, useBuffer, true, true, true, robotName); } /** * Constructs a {@link org.jfree.chart.JFreeChart chart} * composite. * * @param parent * The parent. * @param style * The style of the composite. * @param jfreechart * the chart. * @param width * the preferred width of the panel. * @param height * the preferred height of the panel. * @param minimumDrawWidth * the minimum drawing width. * @param minimumDrawHeight * the minimum drawing height. * @param maximumDrawWidth * the maximum drawing width. * @param maximumDrawHeight * the maximum drawing height. * @param useBuffer * a flag that indicates whether to use the off-screen * buffer to improve performance (at the expense of * memory). * @param zoom * a flag indicating whether or not zoom options should * be added to the popup menu. * @param tooltips * a flag indicating whether or not tooltips should be * enabled for the chart. * @param export * a flag indicating whether or not the export options * should be added to the popup menu * @param robotName * name of the robot * @see org.jfree.chart.JFreeChart */ private XChartComposite(Composite parent, int style, JFreeChart jfreechart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean zoom, boolean tooltips, boolean export, String robotName) { super(parent, style | SWT.DOUBLE_BUFFERED | SWT.BACKGROUND); this.parent = parent; init(jfreechart, minimumDrawWidth, minimumDrawHeight, maximumDrawWidth, maximumDrawHeight, useBuffer, zoom, tooltips, export, robotName); } /** * @param jfreechart * @param minimumDrawWidth * @param minimumDrawHeight * @param maximumDrawWidth * @param maximumDrawHeight * @param useBuffer * @param zoom * @param tooltips * @param export * @param robotName */ private void init(JFreeChart jfreechart, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean zoom, boolean tooltips, boolean export, String robotName) { setChartAndRobotName(jfreechart, robotName); addControlListener(this); chartMouseListeners = new EventListenerList(); setLayout(new FillLayout()); info = new ChartRenderingInfo(); this.useBuffer = useBuffer; this.refreshBuffer = false; this.minimumDrawWidth = minimumDrawWidth; this.minimumDrawHeight = minimumDrawHeight; this.maximumDrawWidth = maximumDrawWidth; this.maximumDrawHeight = maximumDrawHeight; this.zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE; setDisplayToolTips(tooltips); canvas = new XCanvas(this, SWT.DOUBLE_BUFFERED | SWT.BACKGROUND); canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { if (chart != null) { paintChart(e); } } }); if (chart != null) { chart.addChangeListener(this); Plot plot = chart.getPlot(); this.domainZoomable = false; this.rangeZoomable = false; if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); this.rangeZoomable = z.isRangeZoomable(); this.orientation = z.getOrientation(); } } // set up popup menu... this.popup = null; if (zoom) { this.popup = createPopupMenu(zoom, export); } Listener listener = new Listener() { public void handleEvent(Event event) { if (XChartComposite.this.chart != null) { handleMouseEvents(event); } } }; canvas.addListener(SWT.MouseDown, listener); canvas.addListener(SWT.MouseMove, listener); canvas.addListener(SWT.MouseUp, listener); this.enforceFileExtensions = true; } /** * @param event */ private void handleMouseEvents(Event event) { switch (event.type) { case SWT.MouseDown: Rectangle scaledDataArea = getScreenDataArea(event.x, event.y); zoomPoint = getPointInRectangle(event.x, event.y, scaledDataArea); Rectangle insets = getClientArea(); int x = (int) ((event.x - insets.x) / scaleX); int y = (int) ((event.y - insets.y) / scaleY); anchor = new Point2D.Double(x, y); chart.setNotify(true); // force a redraw canvas.redraw(); // new entity code... Object[] listeners = chartMouseListeners.getListeners(ChartMouseListener.class); if (listeners.length == 0) { return; } ChartEntity entity = null; if (info != null) { EntityCollection entities = info.getEntityCollection(); if (entities != null) { entity = entities.getEntity(x, y); } } java.awt.event.MouseEvent mouseEvent = SWTUtils.toAwtMouseEvent(event); ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(), mouseEvent, entity); for (int i = listeners.length - 1; i >= 0; i -= 1) { ((ChartMouseListener) listeners[i]).chartMouseClicked(chartEvent); } break; case SWT.MouseMove: // handle axis trace if (horizontalAxisTrace || verticalAxisTrace) { horizontalTraceLineY = event.y; verticalTraceLineX = event.x; canvas.redraw(); } // handle tool tips in a simple way if (displayToolTips) { String s = getToolTipText(new MouseEvent(event)); if (s != null && !s.equals(canvas.getToolTipText())) { canvas.setToolTipText(s); } } // handle zoom box if (zoomPoint == null) { return; } scaledDataArea = getScreenDataArea(zoomPoint.x, zoomPoint.y); org.eclipse.swt.graphics.Point movingPoint = getPointInRectangle(event.x, event.y, scaledDataArea); // handle zoom boolean hZoom = false; boolean vZoom = false; if (orientation == PlotOrientation.HORIZONTAL) { hZoom = rangeZoomable; vZoom = domainZoomable; } else { hZoom = domainZoomable; vZoom = rangeZoomable; } if (hZoom && vZoom) { // selected rectangle shouldn't extend outside the // data area... zoomRectangle = new Rectangle(zoomPoint.x, zoomPoint.y, movingPoint.x - zoomPoint.x, movingPoint.y - zoomPoint.y); } else if (hZoom) { zoomRectangle = new Rectangle(zoomPoint.x, scaledDataArea.y, movingPoint.x - zoomPoint.x, scaledDataArea.height); } else if (vZoom) { zoomRectangle = new Rectangle(scaledDataArea.x, zoomPoint.y, scaledDataArea.width, event.y - zoomPoint.y); } canvas.redraw(); break; case SWT.MouseUp: if (zoomRectangle == null) { Rectangle screenDataArea = getScreenDataArea(event.x, event.y); if (screenDataArea != null) { zoomPoint = getPointInRectangle(event.x, event.y, screenDataArea); } if (popup != null && event.button == 3) { org.eclipse.swt.graphics.Point pt = canvas.toDisplay(event.x, event.y); displayPopupMenu(pt.x, pt.y); } } else { hZoom = false; vZoom = false; if (orientation == PlotOrientation.HORIZONTAL) { hZoom = rangeZoomable; vZoom = domainZoomable; } else { hZoom = domainZoomable; vZoom = rangeZoomable; } boolean zoomTrigger1 = hZoom && Math.abs(zoomRectangle.width) >= zoomTriggerDistance; boolean zoomTrigger2 = vZoom && Math.abs(zoomRectangle.height) >= zoomTriggerDistance; if (zoomTrigger1 || zoomTrigger2) { // if the box has been drawn backwards, // restore the auto bounds if ((hZoom && (zoomRectangle.x + zoomRectangle.width < zoomPoint.x)) || (vZoom && (zoomRectangle.y + zoomRectangle.height < zoomPoint.y))) { restoreAutoBounds(); } else { zoom(zoomRectangle); } canvas.redraw(); } } zoomPoint = null; zoomRectangle = null; break; default: zoomPoint = null; zoomRectangle = null; } } /** * @param e */ private void paintChart(PaintEvent e) { SWTGraphics2D sg2 = new SWTGraphics2D(e.gc, false); // first determine the size of the chart rendering area... // no insets for SWT, try getClientArea Rectangle available = getBounds(); // work out if scaling is required... boolean scale = false; int drawWidth = available.width; int drawHeight = available.height; if (drawWidth == 0.0 || drawHeight == 0.0) { return; } scaleX = 1.0; scaleY = 1.0; if (drawWidth < minimumDrawWidth) { scaleX = (double) drawWidth / minimumDrawWidth; drawWidth = minimumDrawWidth; scale = true; } else if (drawWidth > maximumDrawWidth) { scaleX = (double) drawWidth / maximumDrawWidth; drawWidth = maximumDrawWidth; scale = true; } if (drawHeight < minimumDrawHeight) { scaleY = (double) drawHeight / minimumDrawHeight; drawHeight = minimumDrawHeight; scale = true; } else if (drawHeight > maximumDrawHeight) { scaleY = (double) drawHeight / maximumDrawHeight; drawHeight = maximumDrawHeight; scale = true; } // are we using the chart buffer? // if (useBuffer) { // //SwtGraphics2D sg2 = new SwtGraphics2D( e.gc ); // chartBuffer = (org.eclipse.swt.graphics.Image) canvas // .getData("double-buffer-image"); // // do we need to fill the buffer? // if (chartBuffer == null || chartBufferWidth != available.width // || chartBufferHeight != available.height) { // chartBufferWidth = available.width; // chartBufferHeight = available.height; // if (chartBuffer != null) { // chartBuffer.dispose( ); // } // chartBuffer = new org.eclipse.swt.graphics.Image(getDisplay( ), // chartBufferWidth, chartBufferHeight); // refreshBuffer = true; // } // // // do we need to redraw the buffer? // if (refreshBuffer) { // if (scale) { // sg2.scale(scaleX, scaleY); // } // // Performs the actual drawing here ... // GC gci = new GC(chartBuffer); // SWTGraphics2D sg2d = new SWTGraphics2D(gci, true); // chart.draw(sg2d, new Rectangle2D.Double(0, 0, chartBufferWidth, // chartBufferHeight), getAnchor( ), info); // canvas.setData("double-buffer-image", chartBuffer); // sg2d.dispose( ); // this disposes the new GC() also // refreshBuffer = false; // } // // zap the buffer onto the canvas... // sg2.drawImage(chartBuffer, 0, 0); // } // // or redrawing the chart every time... // else { chart.draw(sg2, new Rectangle2D.Double(0, 0, getBounds().width, getBounds().height), getAnchor(), info); // } Rectangle area = getScreenDataArea(); if (verticalAxisTrace && area.x < verticalTraceLineX && area.x + area.width > verticalTraceLineX) { e.gc.drawLine(verticalTraceLineX, area.y, verticalTraceLineX, area.y + area.height); } if (horizontalAxisTrace && area.y < horizontalTraceLineY && area.y + area.height > horizontalTraceLineY) { e.gc.drawLine(area.x, horizontalTraceLineY, area.x + area.width, horizontalTraceLineY); } verticalTraceLineX = 0; horizontalTraceLineY = 0; if (zoomRectangle != null) { e.gc.drawRectangle(zoomRectangle); } // if (e.data != null && e.data.equals(Boolean.TRUE)) { // e.gc.dispose( ); // } sg2.dispose(); } /** * Returns the X scale factor for the chart. This will be 1.0 if * no scaling has been used. * * @return The scale factor. */ public double getScaleX() { return this.scaleX; } /** * Returns the Y scale factory for the chart. This will be 1.0 if * no scaling has been used. * * @return The scale factor. */ public double getScaleY() { return this.scaleY; } /** * Returns the anchor point. * * @return The anchor point (possibly <code>null</code>). */ public Point2D getAnchor() { return this.anchor; } /** * Sets the anchor point. This method is provided for the use of * subclasses, not end users. * * @param anchor * the anchor point (<code>null</code> permitted). */ protected void setAnchor(Point2D anchor) { this.anchor = anchor; } /** * Returns the chart contained in the panel. * * @return The chart (possibly <code>null</code>). */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that is displayed in the panel. * * @param chart * the chart (<code>null</code> permitted). */ public void setChartAndRobotName(JFreeChart chart, String robotName) { // stop listening for changes to the existing chart if (this.chart != null) { this.chart.removeChangeListener(this); } // add the new chart this.chart = chart; if (chart != null) { this.chart.addChangeListener(this); Plot plot = chart.getPlot(); this.domainZoomable = false; this.rangeZoomable = false; if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); this.rangeZoomable = z.isRangeZoomable(); this.orientation = z.getOrientation(); } this.chart.setBackgroundPaint(SWTUtils.toAwtColor((getBackground()))); } else { this.domainZoomable = false; this.rangeZoomable = false; } if (this.useBuffer) { this.refreshBuffer = true; } this.robotName = robotName; forceRedraw(); } /** * Returns the zoom in factor. * * @return The zoom in factor. * @see #setZoomInFactor(double) */ public double getZoomInFactor() { return this.zoomInFactor; } /** * Sets the zoom in factor. * * @param factor * the factor. * @see #getZoomInFactor() */ public void setZoomInFactor(double factor) { this.zoomInFactor = factor; } /** * Returns the zoom out factor. * * @return The zoom out factor. * @see #setZoomOutFactor(double) */ public double getZoomOutFactor() { return this.zoomOutFactor; } /** * Sets the zoom out factor. * * @param factor * the factor. * @see #getZoomOutFactor() */ public void setZoomOutFactor(double factor) { this.zoomOutFactor = factor; } /** * Returns <code>true</code> if file extensions should be * enforced, and <code>false</code> otherwise. * * @return The flag. */ public boolean isEnforceFileExtensions() { return this.enforceFileExtensions; } /** * Sets a flag that controls whether or not file extensions are * enforced. * * @param enforce * the new flag value. */ public void setEnforceFileExtensions(boolean enforce) { this.enforceFileExtensions = enforce; } /** * Returns a point based on (x, y) but constrained to be within * the bounds of the given rectangle. This method could be moved * to JCommon. * * @param x * the x-coordinate. * @param y * the y-coordinate. * @param area * the rectangle (<code>null</code> not permitted). * @return A point within the rectangle. */ @SuppressWarnings("cast") private org.eclipse.swt.graphics.Point getPointInRectangle(int x, int y, Rectangle area) { x = (int) Math.max(area.x, Math.min(x, area.x + area.width)); y = (int) Math.max(area.y, Math.min(y, area.y + area.height)); return new org.eclipse.swt.graphics.Point(x, y); } /** * Zooms in on an anchor point (specified in screen coordinate * space). * * @param x * the x value (in screen coordinates). * @param y * the y value (in screen coordinates). */ public void zoomInBoth(double x, double y) { zoomInDomain(x, y); zoomInRange(x, y); } /** * Decreases the length of the domain axis, centered about the * given coordinate on the screen. The length of the domain axis * is reduced by the value of {@link #getZoomInFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomInDomain(double x, double y) { Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable plot = (Zoomable) p; plot.zoomDomainAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Decreases the length of the range axis, centered about the * given coordinate on the screen. The length of the range axis is * reduced by the value of {@link #getZoomInFactor()}. * * @param x * the x-coordinate (in screen coordinates). * @param y * the y coordinate (in screen coordinates). */ public void zoomInRange(double x, double y) { Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Zooms out on an anchor point (specified in screen coordinate * space). * * @param x * the x value (in screen coordinates). * @param y * the y value (in screen coordinates). */ public void zoomOutBoth(double x, double y) { zoomOutDomain(x, y); zoomOutRange(x, y); } /** * Increases the length of the domain axis, centered about the * given coordinate on the screen. The length of the domain axis * is increased by the value of {@link #getZoomOutFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomOutDomain(double x, double y) { Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomDomainAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Increases the length the range axis, centered about the given * coordinate on the screen. The length of the range axis is * increased by the value of {@link #getZoomOutFactor()}. * * @param x * the x coordinate (in screen coordinates). * @param y * the y-coordinate (in screen coordinates). */ public void zoomOutRange(double x, double y) { Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y))); } } /** * Zooms in on a selected region. * * @param selection * the selected region. */ @SuppressWarnings("cast") public void zoom(Rectangle selection) { // get the origin of the zoom selection in the Java2D space // used for // drawing the chart (that is, before any scaling to fit the // panel) Point2D selectOrigin = translateScreenToJava2D(new Point(selection.x, selection.y)); PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle scaledDataArea = getScreenDataArea((int) (selection.x + selection.width) / 2, (int) (selection.y + selection.height) / 2); if ((selection.height > 0) && (selection.width > 0)) { double hLower = (selection.x - scaledDataArea.x) / (double) scaledDataArea.width; double hUpper = (selection.x + selection.width - scaledDataArea.x) / (double) scaledDataArea.width; double vLower = (scaledDataArea.y + scaledDataArea.height - selection.y - selection.height) / (double) scaledDataArea.height; double vUpper = (scaledDataArea.y + scaledDataArea.height - selection.y) / (double) scaledDataArea.height; Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable z = (Zoomable) p; if (z.getOrientation() == PlotOrientation.HORIZONTAL) { z.zoomDomainAxes(vLower, vUpper, plotInfo, selectOrigin); z.zoomRangeAxes(hLower, hUpper, plotInfo, selectOrigin); } else { z.zoomDomainAxes(hLower, hUpper, plotInfo, selectOrigin); z.zoomRangeAxes(vLower, vUpper, plotInfo, selectOrigin); } } } } /** * Receives notification of changes to the chart, and redraws the * chart. * * @param event * details of the chart change event. */ public void chartChanged(ChartChangeEvent event) { this.refreshBuffer = true; Plot plot = chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.orientation = z.getOrientation(); } forceRedraw(); } /** * Forces a redraw of the canvas by invoking a new PaintEvent. */ public void forceRedraw() { if (SWTUtil.swtAssert(canvas)) { // Event ev = new Event( ); // ev.gc = new GC(canvas); // ev.x = 0; // ev.y = 0; // ev.data = Boolean.TRUE; // ev.width = canvas.getBounds( ).width; // ev.height = canvas.getBounds( ).height; // ev.count = 0; // canvas.notifyListeners(SWT.Paint, ev); canvas.redraw(); } } /** * Restores the auto-range calculation on both axes. */ public void restoreAutoBounds() { restoreAutoDomainBounds(); restoreAutoRangeBounds(); } /** * Restores the auto-range calculation on the domain axis. */ public void restoreAutoDomainBounds() { Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { Zoomable z = (Zoomable) p; z.zoomDomainAxes(0.0, this.info.getPlotInfo(), SWTUtils.toAwtPoint(this.zoomPoint)); } } /** * Restores the auto-range calculation on the range axis. */ public void restoreAutoRangeBounds() { Plot p = this.chart.getPlot(); if (p instanceof ValueAxisPlot) { Zoomable z = (Zoomable) p; z.zoomRangeAxes(0.0, this.info.getPlotInfo(), SWTUtils.toAwtPoint(this.zoomPoint)); } } /** * Applies any scaling that is in effect for the chart drawing to * the given rectangle. * * @param rect * the rectangle. * @return A new scaled rectangle. */ public Rectangle scale(Rectangle2D rect) { Rectangle insets = this.getClientArea(); int x = (int) Math.round(rect.getX() * getScaleX()) + insets.x; int y = (int) Math.round(rect.getY() * this.getScaleY()) + insets.y; int w = (int) Math.round(rect.getWidth() * this.getScaleX()); int h = (int) Math.round(rect.getHeight() * this.getScaleY()); return new Rectangle(x, y, w, h); } /** * Returns the data area for the chart (the area inside the axes) * with the current scaling applied (that is, the area as it * appears on screen). * * @return The scaled data area. */ public Rectangle getScreenDataArea() { Rectangle2D dataArea = this.info.getPlotInfo().getDataArea(); Rectangle clientArea = this.getClientArea(); int x = (int) (dataArea.getX() * this.scaleX + clientArea.x); int y = (int) (dataArea.getY() * this.scaleY + clientArea.y); int w = (int) (dataArea.getWidth() * this.scaleX); int h = (int) (dataArea.getHeight() * this.scaleY); return new Rectangle(x, y, w, h); } /** * Returns the data area (the area inside the axes) for the plot * or subplot, with the current scaling applied. * * @param x * the x-coordinate (for subplot selection). * @param y * the y-coordinate (for subplot selection). * @return The scaled data area. */ public Rectangle getScreenDataArea(int x, int y) { PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle result; if (plotInfo.getSubplotCount() == 0) { result = getScreenDataArea(); } else { // get the origin of the zoom selection in the Java2D // space used for // drawing the chart (that is, before any scaling to fit // the panel) Point2D selectOrigin = translateScreenToJava2D(new Point(x, y)); int subplotIndex = plotInfo.getSubplotIndex(selectOrigin); if (subplotIndex == -1) { return null; } result = scale(plotInfo.getSubplotInfo(subplotIndex).getDataArea()); } return result; } /** * Translates a Java2D point on the chart to a screen location. * * @param java2DPoint * the Java2D point. * @return The screen location. */ public Point translateJava2DToScreen(Point2D java2DPoint) { Rectangle insets = this.getClientArea(); int x = (int) (java2DPoint.getX() * this.scaleX + insets.x); int y = (int) (java2DPoint.getY() * this.scaleY + insets.y); return new Point(x, y); } /** * Translates a screen location to a Java SWT point. * * @param screenPoint * the screen location. * @return The Java2D coordinates. */ public Point translateScreenToJavaSWT(Point screenPoint) { Rectangle insets = this.getClientArea(); int x = (int) ((screenPoint.x - insets.x) / this.scaleX); int y = (int) ((screenPoint.y - insets.y) / this.scaleY); return new Point(x, y); } /** * Translates a screen location to a Java2D point. * * @param screenPoint * the screen location. * @return The Java2D coordinates. */ public Point2D translateScreenToJava2D(Point screenPoint) { Rectangle insets = this.getClientArea(); int x = (int) ((screenPoint.x - insets.x) / this.scaleX); int y = (int) ((screenPoint.y - insets.y) / this.scaleY); return new Point2D.Double(x, y); } /** * Returns the flag that controls whether or not a horizontal axis * trace line is drawn over the plot area at the current mouse * location. * * @return A boolean. */ public boolean getHorizontalAxisTrace() { return this.horizontalAxisTrace; } /** * A flag that controls trace lines on the horizontal axis. * * @param flag * <code>true</code> enables trace lines for the * mouse pointer on the horizontal axis. */ public void setHorizontalAxisTrace(boolean flag) { this.horizontalAxisTrace = flag; } /** * Returns the flag that controls whether or not a vertical axis * trace line is drawn over the plot area at the current mouse * location. * * @return A boolean. */ public boolean getVerticalAxisTrace() { return this.verticalAxisTrace; } /** * A flag that controls trace lines on the vertical axis. * * @param flag * <code>true</code> enables trace lines for the * mouse pointer on the vertical axis. */ public void setVerticalAxisTrace(boolean flag) { this.verticalAxisTrace = flag; } /** * @param displayToolTips * the displayToolTips to set */ public void setDisplayToolTips(boolean displayToolTips) { this.displayToolTips = displayToolTips; } /** * Returns a string for the tooltip. * * @param e * the mouse event. * @return A tool tip or <code>null</code> if no tooltip is * available. */ public String getToolTipText(org.eclipse.swt.events.MouseEvent e) { String result = null; if (this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if (entities != null) { Rectangle insets = getBounds(); ChartEntity entity = entities.getEntity((int) ((e.x - insets.x) / this.scaleX), (int) ((e.y - insets.y) / this.scaleY)); if (entity != null) { result = entity.getToolTipText(); } } } return result; } /** * The idea is to modify the zooming options depending on the type * of chart being displayed by the panel. * * @param x * horizontal position of the popup. * @param y * vertical position of the popup. */ protected void displayPopupMenu(int x, int y) { if (popup != null && chart != null) { // go through each zoom menu item and decide whether or // not to // enable it... Plot plot = chart.getPlot(); boolean isDomainZoomable = false; boolean isRangeZoomable = false; if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; isDomainZoomable = z.isDomainZoomable(); isRangeZoomable = z.isRangeZoomable(); } if (zoomInDomainMenuItem != null) { zoomInDomainMenuItem.setEnabled(isDomainZoomable); } if (zoomOutDomainMenuItem != null) { zoomOutDomainMenuItem.setEnabled(isDomainZoomable); } if (zoomResetDomainMenuItem != null) { zoomResetDomainMenuItem.setEnabled(isDomainZoomable); } if (zoomInRangeMenuItem != null) { zoomInRangeMenuItem.setEnabled(isRangeZoomable); } if (zoomOutRangeMenuItem != null) { zoomOutRangeMenuItem.setEnabled(isRangeZoomable); } if (zoomResetRangeMenuItem != null) { zoomResetRangeMenuItem.setEnabled(isRangeZoomable); } if (zoomInBothMenuItem != null) { zoomInBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } if (zoomOutBothMenuItem != null) { zoomOutBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } if (zoomResetBothMenuItem != null) { zoomResetBothMenuItem.setEnabled(isDomainZoomable & isRangeZoomable); } popup.setLocation(x, y); popup.setVisible(true); } } /** * Creates a popup menu for the canvas. * * @param zoom * include menu items for zooming. * @param export * include menu item for exporting. * @return The popup menu. */ protected Menu createPopupMenu(boolean zoom, boolean export) { Menu result = new Menu(this); boolean separator = false; if (export) { // TODO: exports XMenuItem exportPDF = new XMenuItem(result, SWT.PUSH); exportPDF.setTextForLocaleKey("HChartComposite.menu.item.exportPDF"); //$NON-NLS-1$ exportPDF.setData(EXPORT_PDF_COMMAND); exportPDF.addSelectionListener(this); XMenuItem exportPNG = new XMenuItem(result, SWT.PUSH); exportPNG.setTextForLocaleKey("HChartComposite.menu.item.exportPNG"); //$NON-NLS-1$ exportPNG.setData(EXPORT_PNG_COMMAND); exportPNG.addSelectionListener(this); XMenuItem exportJPEG = new XMenuItem(result, SWT.PUSH); exportJPEG.setTextForLocaleKey("HChartComposite.menu.item.exportJPG"); //$NON-NLS-1$ exportJPEG.setData(EXPORT_JPEG_COMMAND); exportJPEG.addSelectionListener(this); separator = true; } if (zoom) { if (separator) { new MenuItem(result, SWT.SEPARATOR); separator = false; } Menu zoomInMenu = new Menu(result); XMenuItem zoomInMenuItem = new XMenuItem(result, SWT.CASCADE); zoomInMenuItem.setTextForLocaleKey("HChartComposite.menu.item.zoomIn"); //$NON-NLS-1$ zoomInMenuItem.setMenu(zoomInMenu); zoomInBothMenuItem = new XMenuItem(zoomInMenu, SWT.PUSH); zoomInBothMenuItem.setTextForLocaleKey("HChartComposite.menu.item.allAxes"); //$NON-NLS-1$ zoomInBothMenuItem.setData(ZOOM_IN_BOTH_COMMAND); zoomInBothMenuItem.addSelectionListener(this); new MenuItem(zoomInMenu, SWT.SEPARATOR); zoomInDomainMenuItem = new XMenuItem(zoomInMenu, SWT.PUSH); zoomInDomainMenuItem.setTextForLocaleKey("HChartComposite.menu.item.domainAxis"); //$NON-NLS-1$ zoomInDomainMenuItem.setData(ZOOM_IN_DOMAIN_COMMAND); zoomInDomainMenuItem.addSelectionListener(this); zoomInRangeMenuItem = new XMenuItem(zoomInMenu, SWT.PUSH); zoomInRangeMenuItem.setTextForLocaleKey("HChartComposite.menu.item.rangeAxis"); //$NON-NLS-1$ zoomInRangeMenuItem.setData(ZOOM_IN_RANGE_COMMAND); zoomInRangeMenuItem.addSelectionListener(this); Menu zoomOutMenu = new Menu(result); XMenuItem zoomOutMenuItem = new XMenuItem(result, SWT.CASCADE); zoomOutMenuItem.setTextForLocaleKey("HChartComposite.menu.item.zoomOut"); //$NON-NLS-1$ zoomOutMenuItem.setMenu(zoomOutMenu); zoomOutBothMenuItem = new XMenuItem(zoomOutMenu, SWT.PUSH); zoomOutBothMenuItem.setTextForLocaleKey("HChartComposite.menu.item.allAxes"); //$NON-NLS-1$ zoomOutBothMenuItem.setData(ZOOM_OUT_BOTH_COMMAND); zoomOutBothMenuItem.addSelectionListener(this); new MenuItem(zoomOutMenu, SWT.SEPARATOR); zoomOutDomainMenuItem = new XMenuItem(zoomOutMenu, SWT.PUSH); zoomOutDomainMenuItem.setTextForLocaleKey("HChartComposite.menu.item.domainAxis"); //$NON-NLS-1$ zoomOutDomainMenuItem.setData(ZOOM_OUT_DOMAIN_COMMAND); zoomOutDomainMenuItem.addSelectionListener(this); zoomOutRangeMenuItem = new XMenuItem(zoomOutMenu, SWT.PUSH); zoomOutRangeMenuItem.setTextForLocaleKey("HChartComposite.menu.item.rangeAxis"); //$NON-NLS-1$ zoomOutRangeMenuItem.setData(ZOOM_OUT_RANGE_COMMAND); zoomOutRangeMenuItem.addSelectionListener(this); // Menu autoRangeMenu = new Menu(result); // XMenuItem autoRangeMenuItem = new XMenuItem(result, SWT.CASCADE); // autoRangeMenuItem.setTextForLocaleKey("HChartComposite.menu.item.autoRange"); //$NON-NLS-1$ // autoRangeMenuItem.setMenu(autoRangeMenu); // // zoomResetBothMenuItem = new XMenuItem(autoRangeMenu, SWT.PUSH); // zoomResetBothMenuItem // .setTextForLocaleKey("HChartComposite.menu.item.allAxes"); //$NON-NLS-1$ // zoomResetBothMenuItem.setData(ZOOM_RESET_BOTH_COMMAND); // zoomResetBothMenuItem.addSelectionListener(this); // // new MenuItem(autoRangeMenu, SWT.SEPARATOR); // // zoomResetDomainMenuItem = new XMenuItem(autoRangeMenu, // SWT.PUSH); // zoomResetDomainMenuItem // .setTextForLocaleKey("HChartComposite.menu.item.domainAxis"); //$NON-NLS-1$ // zoomResetDomainMenuItem.setData(ZOOM_RESET_DOMAIN_COMMAND); // zoomResetDomainMenuItem.addSelectionListener(this); // // zoomResetRangeMenuItem = new XMenuItem(autoRangeMenu, SWT.PUSH); // zoomResetRangeMenuItem // .setTextForLocaleKey("HChartComposite.menu.item.rangeAxis"); //$NON-NLS-1$ // zoomResetRangeMenuItem.setData(ZOOM_RESET_RANGE_COMMAND); // zoomResetRangeMenuItem.addSelectionListener(this); } return result; } /** * Handles action events generated by the popup menu. * * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected( * org.eclipse.swt.events.SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { } /** * Handles action events generated by the popup menu. * * @see org.eclipse.swt.events.SelectionListener#widgetSelected( * org.eclipse.swt.events.SelectionEvent) */ public void widgetSelected(SelectionEvent e) { String command = (String) ((MenuItem) e.getSource()).getData(); if (command.equals(EXPORT_PDF_COMMAND)) { export(chart, EXPORT_PDF_COMMAND); } else if (command.equals(EXPORT_PNG_COMMAND)) { export(chart, EXPORT_PNG_COMMAND); } else if (command.equals(EXPORT_JPEG_COMMAND)) { export(chart, EXPORT_JPEG_COMMAND); } /* * in the next zoomPoint.x and y replace by e.x and y for now. * this helps to handle the mouse events and besides, those * values are unused AFAIK. */ else if (command.equals(ZOOM_IN_BOTH_COMMAND)) { zoomInBoth(e.x, e.y); } else if (command.equals(ZOOM_IN_DOMAIN_COMMAND)) { zoomInDomain(e.x, e.y); } else if (command.equals(ZOOM_IN_RANGE_COMMAND)) { zoomInRange(e.x, e.y); } else if (command.equals(ZOOM_OUT_BOTH_COMMAND)) { zoomOutBoth(e.x, e.y); } else if (command.equals(ZOOM_OUT_DOMAIN_COMMAND)) { zoomOutDomain(e.x, e.y); } else if (command.equals(ZOOM_OUT_RANGE_COMMAND)) { zoomOutRange(e.x, e.y); } else if (command.equals(ZOOM_RESET_BOTH_COMMAND)) { restoreAutoBounds(); } else if (command.equals(ZOOM_RESET_DOMAIN_COMMAND)) { restoreAutoDomainBounds(); } else if (command.equals(ZOOM_RESET_RANGE_COMMAND)) { restoreAutoRangeBounds(); } forceRedraw(); } /** * Exports the given chart with the given export command, f.e. * export as PDF. * * @param chart * The chart to export. * @param command * The export command. */ private void export(final JFreeChart chart, final String command) { if (Util.isEmpty(robotName)) { robotName = "unknown_robot"; //$NON-NLS-1$ } SWTUtil.showBusyWhile(parent.getShell(), new Runnable() { public void run() { if (command.equals(EXPORT_PDF_COMMAND)) { ChartUtil.exportPDF(chart, robotName); } else if (command.equals(EXPORT_PNG_COMMAND)) { ChartUtil.exportPNG(chart, robotName); } else if (command.equals(EXPORT_JPEG_COMMAND)) { ChartUtil.exportJPG(chart, robotName); } } }); } /* * (non-Javadoc) * * @see org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics.Color) */ @Override public void setBackground(Color color) { super.setBackground(color); if (canvas != null) { canvas.setBackground(color); } if (chart != null) { chart.setBackgroundPaint(SWTUtils.toAwtColor(color)); } } /* * (non-Javadoc) * * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) */ public void controlMoved(ControlEvent e) { // Do nothing } /* * (non-Javadoc) * * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) */ public void controlResized(ControlEvent e) { forceRedraw(); } }