/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-2014, 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.] * * ---------------- * ZoomHandler.java * ---------------- * (C) Copyright 2013, 2014,by Object Refinery Limited and Contributors. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): Michael Zinsmaier; * * Changes: * -------- * 11-Jun-2009 : Version 1 (DG); * */ package org.jfree.chart.panel; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.ChartPanel; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.ShapeUtils; /** * A mouse handler than performs a zooming operation on a ChartPanel. */ public class ZoomHandler extends AbstractMouseHandler { /** a generated serial id. */ private static final long serialVersionUID = -3796063183854513802L; private Point2D zoomPoint; private Rectangle2D zoomRectangle; public ZoomHandler() { super(); zoomPoint = null; } public ZoomHandler(int modifier) { super(modifier); zoomPoint = null; } @Override public void mousePressed(MouseEvent e) { ChartPanel chartPanel = (ChartPanel) e.getSource(); Rectangle2D screenDataArea = chartPanel.getScreenDataArea(e.getX(), e.getY()); if (screenDataArea != null) { this.zoomPoint = ShapeUtils.getPointInRectangle(e.getX(), e.getY(), screenDataArea); } else { this.zoomPoint = null; chartPanel.clearLiveMouseHandler(); } } @Override public void mouseDragged(MouseEvent e) { ChartPanel panel = (ChartPanel) e.getSource(); if (this.zoomPoint == null) { //no initial zoom rectangle exists but the handler is set //as life handler unregister panel.clearLiveMouseHandler(); return; } Graphics2D g2 = (Graphics2D) panel.getGraphics(); // erase the previous zoom rectangle (if any). We only need to do // this is we are using XOR mode, which we do when we're not using // the buffer (if there is a buffer, then at the end of this method we // just trigger a repaint) if (!panel.getUseBuffer()) { drawZoomRectangle(panel, g2, true); } boolean hZoom, vZoom; if (panel.getOrientation() == PlotOrientation.HORIZONTAL) { hZoom = panel.isRangeZoomable(); vZoom = panel.isDomainZoomable(); } else { hZoom = panel.isDomainZoomable(); vZoom = panel.isRangeZoomable(); } Rectangle2D scaledDataArea = panel.getScreenDataArea( (int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); if (hZoom && vZoom) { // selected rectangle shouldn't extend outside the data area... double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double( this.zoomPoint.getX(), this.zoomPoint.getY(), xmax - this.zoomPoint.getX(), ymax - this.zoomPoint.getY()); } else if (hZoom) { double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); this.zoomRectangle = new Rectangle2D.Double( this.zoomPoint.getX(), scaledDataArea.getMinY(), xmax - this.zoomPoint.getX(), scaledDataArea.getHeight()); } else if (vZoom) { double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double( scaledDataArea.getMinX(), this.zoomPoint.getY(), scaledDataArea.getWidth(), ymax - this.zoomPoint.getY()); } panel.setZoomRectangle(this.zoomRectangle); // Draw the new zoom rectangle... if (panel.getUseBuffer()) { panel.repaint(); } else { // with no buffer, we use XOR to draw the rectangle "over" the // chart... drawZoomRectangle(panel, g2, true); } g2.dispose(); } @Override public void mouseReleased(MouseEvent e) { ChartPanel panel = (ChartPanel) e.getSource(); if (this.zoomRectangle == null) { //no initial zoom rectangle exists but the handler is set //as life handler unregister panel.clearLiveMouseHandler(); return; } boolean hZoom, vZoom; if (panel.getOrientation() == PlotOrientation.HORIZONTAL) { hZoom = panel.isRangeZoomable(); vZoom = panel.isDomainZoomable(); } else { hZoom = panel.isDomainZoomable(); vZoom = panel.isRangeZoomable(); } boolean zoomTrigger1 = hZoom && Math.abs(e.getX() - this.zoomPoint.getX()) >= panel.getZoomTriggerDistance(); boolean zoomTrigger2 = vZoom && Math.abs(e.getY() - this.zoomPoint.getY()) >= panel.getZoomTriggerDistance(); if (zoomTrigger1 || zoomTrigger2) { if ((hZoom && (e.getX() < this.zoomPoint.getX())) || (vZoom && (e.getY() < this.zoomPoint.getY()))) { panel.restoreAutoBounds(); } else { double x, y, w, h; Rectangle2D screenDataArea = panel.getScreenDataArea( (int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); double maxX = screenDataArea.getMaxX(); double maxY = screenDataArea.getMaxY(); // for mouseReleased event, (horizontalZoom || verticalZoom) // will be true, so we can just test for either being false; // otherwise both are true if (!vZoom) { x = this.zoomPoint.getX(); y = screenDataArea.getMinY(); w = Math.min(this.zoomRectangle.getWidth(), maxX - this.zoomPoint.getX()); h = screenDataArea.getHeight(); } else if (!hZoom) { x = screenDataArea.getMinX(); y = this.zoomPoint.getY(); w = screenDataArea.getWidth(); h = Math.min(this.zoomRectangle.getHeight(), maxY - this.zoomPoint.getY()); } else { x = this.zoomPoint.getX(); y = this.zoomPoint.getY(); w = Math.min(this.zoomRectangle.getWidth(), maxX - this.zoomPoint.getX()); h = Math.min(this.zoomRectangle.getHeight(), maxY - this.zoomPoint.getY()); } Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h); panel.zoom(zoomArea); } this.zoomPoint = null; this.zoomRectangle = null; panel.setZoomRectangle(null); panel.clearLiveMouseHandler(); } else { // erase the zoom rectangle Graphics2D g2 = (Graphics2D) panel.getGraphics(); if (panel.getUseBuffer()) { panel.repaint(); } else { drawZoomRectangle(panel, g2, true); } g2.dispose(); this.zoomPoint = null; this.zoomRectangle = null; panel.setZoomRectangle(null); panel.clearLiveMouseHandler(); } } /** * Draws zoom rectangle (if present). * The drawing is performed in XOR mode, therefore * when this method is called twice in a row, * the second call will completely restore the state * of the canvas. * * @param g2 the graphics device. * @param xor use XOR for drawing? */ private void drawZoomRectangle(ChartPanel panel, Graphics2D g2, boolean xor) { if (this.zoomRectangle != null) { if (xor) { // Set XOR mode to draw the zoom rectangle g2.setXORMode(Color.gray); } if (panel.getFillZoomRectangle()) { g2.setPaint(panel.getZoomFillPaint()); g2.fill(this.zoomRectangle); } else { g2.setPaint(panel.getZoomOutlinePaint()); g2.draw(this.zoomRectangle); } if (xor) { // Reset to the default 'overwrite' mode g2.setPaintMode(); } } } /** * @see AbstractMouseHandler#isLiveHandler() */ @Override public boolean isLiveHandler() { return true; } }