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