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