001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2020, by Object Refinery Limited 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 * ChartTransferable.java
029 * ----------------------
030 * (C) Copyright 2009-2020, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.chart;
038
039import java.awt.Graphics2D;
040import java.awt.datatransfer.DataFlavor;
041import java.awt.datatransfer.Transferable;
042import java.awt.datatransfer.UnsupportedFlavorException;
043import java.awt.geom.AffineTransform;
044import java.awt.geom.Rectangle2D;
045import java.awt.image.BufferedImage;
046import java.io.IOException;
047
048/**
049 * A class used to represent a chart on the clipboard.
050 *
051 * @since 1.0.13
052 */
053public class ChartTransferable implements Transferable {
054
055    /** The data flavor. */
056    final DataFlavor imageFlavor = new DataFlavor(
057            "image/x-java-image; class=java.awt.Image", "Image");    
058    
059    /** The chart. */
060    private JFreeChart chart;
061
062    /** The width of the chart on the clipboard. */
063    private int width;
064
065    /** The height of the chart on the clipboard. */
066    private int height;
067
068    /**
069     * The smallest width at which the chart will be drawn (if necessary, the
070     * chart will then be scaled down to fit the requested width).
071     *
072     * @since 1.0.14
073     */
074    private int minDrawWidth;
075
076    /**
077     * The smallest height at which the chart will be drawn (if necessary, the
078     * chart will then be scaled down to fit the requested height).
079     *
080     * @since 1.0.14
081     */
082    private int minDrawHeight;
083
084    /**
085     * The largest width at which the chart will be drawn (if necessary, the 
086     * chart will then be scaled up to fit the requested width). 
087     * 
088     * @since 1.0.14
089     */
090    private int maxDrawWidth;
091
092    /**
093     * The largest height at which the chart will be drawn (if necessary, the
094     * chart will then be scaled up to fit the requested height).
095     *
096     * @since 1.0.14
097     */
098    private int maxDrawHeight;
099
100    /**
101     * Creates a new chart selection.
102     *
103     * @param chart  the chart.
104     * @param width  the chart width.
105     * @param height  the chart height.
106     */
107    public ChartTransferable(JFreeChart chart, int width, int height) {
108        this(chart, width, height, true);
109    }
110
111    /**
112     * Creates a new chart selection.
113     *
114     * @param chart  the chart.
115     * @param width  the chart width.
116     * @param height  the chart height.
117     * @param cloneData  clone the dataset(s)?
118     */
119    public ChartTransferable(JFreeChart chart, int width, int height,
120            boolean cloneData) {
121        this(chart, width, height, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE,
122                true);
123    }
124
125    /**
126     * Creates a new chart selection.  The minimum and maximum drawing
127     * dimensions are used to match the scaling behaviour in the
128     * {@link ChartPanel} class.
129     *
130     * @param chart  the chart.
131     * @param width  the chart width.
132     * @param height  the chart height.
133     * @param minDrawW  the minimum drawing width.
134     * @param minDrawH  the minimum drawing height.
135     * @param maxDrawW  the maximum drawing width.
136     * @param maxDrawH  the maximum drawing height.
137     * @param cloneData  clone the dataset(s)?
138     *
139     * @since 1.0.14
140     */
141    public ChartTransferable(JFreeChart chart, int width, int height,
142            int minDrawW, int minDrawH, int maxDrawW, int maxDrawH,
143            boolean cloneData) {
144
145        // we clone the chart because presumably there can be some delay
146        // between putting this instance on the system clipboard and
147        // actually having the getTransferData() method called...
148        try {
149            this.chart = (JFreeChart) chart.clone();
150        }
151        catch (CloneNotSupportedException e) {
152            this.chart = chart;
153        }
154        // FIXME: we've cloned the chart, but the dataset(s) aren't cloned
155        // and we should do that
156        this.width = width;
157        this.height = height;
158        this.minDrawWidth = minDrawW;
159        this.minDrawHeight = minDrawH;
160        this.maxDrawWidth = maxDrawW;
161        this.maxDrawHeight = maxDrawH;
162    }
163
164    /**
165     * Returns the data flavors supported.
166     * 
167     * @return The data flavors supported.
168     */
169    @Override
170    public DataFlavor[] getTransferDataFlavors() {
171        return new DataFlavor[] {this.imageFlavor};
172    }
173
174    /**
175     * Returns {@code true} if the specified flavor is supported.
176     *
177     * @param flavor  the flavor.
178     *
179     * @return A boolean.
180     */
181    @Override
182    public boolean isDataFlavorSupported(DataFlavor flavor) {
183        return this.imageFlavor.equals(flavor);
184    }
185
186    /**
187     * Returns the content for the requested flavor, if it is supported.
188     *
189     * @param flavor  the requested flavor.
190     *
191     * @return The content.
192     *
193     * @throws java.awt.datatransfer.UnsupportedFlavorException if the flavor 
194     *         is not supported.
195     * @throws java.io.IOException if there is an IO problem.
196     */
197    @Override
198    public Object getTransferData(DataFlavor flavor)
199            throws UnsupportedFlavorException, IOException {
200        if (this.imageFlavor.equals(flavor)) {
201            return createBufferedImage(this.chart, this.width, this.height,
202                    this.minDrawWidth, this.minDrawHeight, this.maxDrawWidth,
203                    this.maxDrawHeight);
204        } else {
205            throw new UnsupportedFlavorException(flavor);
206        }
207    }
208
209    /**
210     * A utility method that creates an image of a chart, with scaling.
211     *
212     * @param chart  the chart.
213     * @param w  the image width.
214     * @param h  the image height.
215     * @param minDrawW  the minimum width for chart drawing.
216     * @param minDrawH  the minimum height for chart drawing.
217     * @param maxDrawW  the maximum width for chart drawing.
218     * @param maxDrawH  the maximum height for chart drawing.
219     *
220     * @return  A chart image.
221     *
222     * @since 1.0.14
223     */
224    private BufferedImage createBufferedImage(JFreeChart chart, int w, int h,
225            int minDrawW, int minDrawH, int maxDrawW, int maxDrawH) {
226
227        BufferedImage image = new BufferedImage(w, h,
228                BufferedImage.TYPE_INT_RGB); // bug #182
229        Graphics2D g2 = image.createGraphics();
230
231        // work out if scaling is required...
232        boolean scale = false;
233        double drawWidth = w;
234        double drawHeight = h;
235        double scaleX = 1.0;
236        double scaleY = 1.0;
237        if (drawWidth < minDrawW) {
238            scaleX = drawWidth / minDrawW;
239            drawWidth = minDrawW;
240            scale = true;
241        } else if (drawWidth > maxDrawW) {
242            scaleX = drawWidth / maxDrawW;
243            drawWidth = maxDrawW;
244            scale = true;
245        }
246        if (drawHeight < minDrawH) {
247            scaleY = drawHeight / minDrawH;
248            drawHeight = minDrawH;
249            scale = true;
250        } else if (drawHeight > maxDrawH) {
251            scaleY = drawHeight / maxDrawH;
252            drawHeight = maxDrawH;
253            scale = true;
254        }
255
256        Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0, drawWidth,
257                drawHeight);
258        if (scale) {
259            AffineTransform st = AffineTransform.getScaleInstance(scaleX,
260                    scaleY);
261            g2.transform(st);
262        }
263        chart.draw(g2, chartArea, null, null);
264        g2.dispose();
265        return image;
266
267    }
268
269}