001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------------------------- 028 * DefaultSelectionZoomStrategy.java 029 * --------------------------------- 030 * (C) Copyright 2021-2022 by David Gilbert and Contributors. 031 * 032 * Original Author: -; 033 * Contributor(s): David Gilbert; 034 * 035 * 036 */ 037 038package org.jfree.chart.swing; 039 040import java.awt.Color; 041import java.awt.Graphics2D; 042import java.awt.Paint; 043import java.awt.event.MouseEvent; 044import java.awt.geom.Point2D; 045import java.awt.geom.Rectangle2D; 046import java.io.IOException; 047import java.io.ObjectInputStream; 048import java.io.ObjectOutputStream; 049import org.jfree.chart.internal.SerialUtils; 050 051/** 052 * {@inheritDoc} 053 * 054 * This implementation can be extended to override default behavior. 055 */ 056public class DefaultSelectionZoomStrategy implements SelectionZoomStrategy { 057 058 private static final long serialVersionUID = -8042265475645652131L; 059 060 /** The minimum size required to perform a zoom on a rectangle */ 061 public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10; 062 063 /** 064 * The zoom rectangle starting point (selected by the user with a mouse 065 * click). This is a point on the screen, not the chart (which may have 066 * been scaled up or down to fit the panel). 067 */ 068 protected Point2D zoomPoint = null; 069 070 /** 071 * The zoom rectangle (selected by the user with the mouse). 072 */ 073 protected transient Rectangle2D zoomRectangle = null; 074 075 /** 076 * Controls if the zoom rectangle is drawn as an outline or filled. 077 */ 078 private boolean fillZoomRectangle = true; 079 080 /** 081 * The minimum distance required to drag the mouse to trigger a zoom. 082 */ 083 private int zoomTriggerDistance; 084 085 /** 086 * The paint used to draw the zoom rectangle outline. 087 */ 088 private transient Paint zoomOutlinePaint; 089 090 /** 091 * The zoom fill paint (should use transparency). 092 */ 093 private transient Paint zoomFillPaint; 094 095 public DefaultSelectionZoomStrategy() { 096 zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE; 097 this.zoomOutlinePaint = Color.BLUE; 098 this.zoomFillPaint = new Color(0, 0, 255, 63); 099 } 100 101 @Override 102 public boolean isActivated() { 103 return zoomRectangle != null; 104 } 105 106 @Override 107 public Point2D getZoomPoint() { 108 return zoomPoint; 109 } 110 111 @Override 112 public void setZoomPoint(Point2D zoomPoint) { 113 this.zoomPoint = zoomPoint; 114 } 115 116 @Override 117 public void setZoomTriggerDistance(int distance) { 118 this.zoomTriggerDistance = distance; 119 } 120 121 @Override 122 public int getZoomTriggerDistance() { 123 return zoomTriggerDistance; 124 } 125 126 @Override 127 public Paint getZoomOutlinePaint() { 128 return zoomOutlinePaint; 129 } 130 131 @Override 132 public void setZoomOutlinePaint(Paint paint) { 133 this.zoomOutlinePaint = paint; 134 } 135 136 @Override 137 public Paint getZoomFillPaint() { 138 return zoomFillPaint; 139 } 140 141 @Override 142 public void setZoomFillPaint(Paint paint) { 143 this.zoomFillPaint = paint; 144 } 145 146 @Override 147 public boolean getFillZoomRectangle() { 148 return this.fillZoomRectangle; 149 } 150 151 @Override 152 public void setFillZoomRectangle(boolean flag) { 153 this.fillZoomRectangle = flag; 154 } 155 156 @Override 157 public void updateZoomRectangleSelection(MouseEvent e, boolean hZoom, boolean vZoom, Rectangle2D scaledDataArea) { 158 if (hZoom && vZoom) { 159 // selected rectangle shouldn't extend outside the data area... 160 double xMax = Math.min(e.getX(), scaledDataArea.getMaxX()); 161 double yMax = Math.min(e.getY(), scaledDataArea.getMaxY()); 162 zoomRectangle = new Rectangle2D.Double( 163 zoomPoint.getX(), zoomPoint.getY(), 164 xMax - zoomPoint.getX(), yMax - zoomPoint.getY()); 165 } 166 else if (hZoom) { 167 double xMax = Math.min(e.getX(), scaledDataArea.getMaxX()); 168 zoomRectangle = new Rectangle2D.Double( 169 zoomPoint.getX(), scaledDataArea.getMinY(), 170 xMax - zoomPoint.getX(), scaledDataArea.getHeight()); 171 } 172 else if (vZoom) { 173 double yMax = Math.min(e.getY(), scaledDataArea.getMaxY()); 174 zoomRectangle = new Rectangle2D.Double( 175 scaledDataArea.getMinX(), zoomPoint.getY(), 176 scaledDataArea.getWidth(), yMax - zoomPoint.getY()); 177 } 178 } 179 180 @Override 181 public Rectangle2D getZoomRectangle(boolean hZoom, boolean vZoom, Rectangle2D screenDataArea) { 182 double x, y, w, h; 183 double maxX = screenDataArea.getMaxX(); 184 double maxY = screenDataArea.getMaxY(); 185 // for mouseReleased event, (horizontalZoom || verticalZoom) 186 // will be true, so we can just test for either being false; 187 // otherwise both are true 188 if (!vZoom) { 189 x = zoomPoint.getX(); 190 y = screenDataArea.getMinY(); 191 w = Math.min(zoomRectangle.getWidth(), 192 maxX - zoomPoint.getX()); 193 h = screenDataArea.getHeight(); 194 } 195 else if (!hZoom) { 196 x = screenDataArea.getMinX(); 197 y = zoomPoint.getY(); 198 w = screenDataArea.getWidth(); 199 h = Math.min(zoomRectangle.getHeight(), 200 maxY - zoomPoint.getY()); 201 } 202 else { 203 x = zoomPoint.getX(); 204 y = zoomPoint.getY(); 205 w = Math.min(zoomRectangle.getWidth(), 206 maxX - zoomPoint.getX()); 207 h = Math.min(zoomRectangle.getHeight(), 208 maxY - zoomPoint.getY()); 209 } 210 return new Rectangle2D.Double(x, y, w, h); 211 } 212 213 @Override 214 public void reset() { 215 zoomPoint = null; 216 zoomRectangle = null; 217 } 218 219 @Override 220 public void drawZoomRectangle(Graphics2D g2, boolean xor) { 221 if (zoomRectangle != null) { 222 if (xor) { 223 // Set XOR mode to draw the zoom rectangle 224 g2.setXORMode(Color.GRAY); 225 } 226 if (fillZoomRectangle) { 227 g2.setPaint(zoomFillPaint); 228 g2.fill(zoomRectangle); 229 } 230 else { 231 g2.setPaint(zoomOutlinePaint); 232 g2.draw(zoomRectangle); 233 } 234 if (xor) { 235 // Reset to the default 'overwrite' mode 236 g2.setPaintMode(); 237 } 238 } 239 } 240 241 /** 242 * Provides serialization support. 243 * 244 * @param stream the output stream. 245 * 246 * @throws IOException if there is an I/O error. 247 */ 248 private void writeObject(ObjectOutputStream stream) throws IOException { 249 stream.defaultWriteObject(); 250 SerialUtils.writePaint(this.zoomFillPaint, stream); 251 SerialUtils.writePaint(this.zoomOutlinePaint, stream); 252 } 253 254 /** 255 * Provides serialization support. 256 * 257 * @param stream the input stream. 258 * 259 * @throws IOException if there is an I/O error. 260 * @throws ClassNotFoundException if there is a classpath problem. 261 */ 262 private void readObject(ObjectInputStream stream) 263 throws IOException, ClassNotFoundException { 264 stream.defaultReadObject(); 265 this.zoomFillPaint = SerialUtils.readPaint(stream); 266 this.zoomOutlinePaint = SerialUtils.readPaint(stream); 267 } 268}