001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, 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     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * -------------
028     * SWTUtils.java
029     * -------------
030     * (C) Copyright 2006, 2007, by Henry Proudhon and Contributors.
031     *
032     * Original Author:  Henry Proudhon (henry.proudhon AT ensmp.fr);
033     * Contributor(s):   Rainer Blessing;
034     *                   David Gilbert (david.gilbert@object-refinery.com);
035     *                   Christoph Beck.
036     *
037     * Changes
038     * -------
039     * 01-Aug-2006 : New class (HP);
040     * 16-Jan-2007 : Use FontData.getHeight() instead of direct field access (RB); 
041     * 31-Jan-2007 : Moved the dummy JPanel from SWTGraphics2D.java, 
042     *               added a new convert method for mouse events (HP);
043     * 12-Jul-2007 : Improved the mouse event conversion with buttons 
044     *               and modifiers handling, patch sent by Christoph Beck (HP);
045     * 27-Aug-2007 : Modified toAwtMouseEvent signature (HP).
046     * 
047     */
048    
049    package org.jfree.experimental.swt;
050    
051    import java.awt.event.InputEvent;
052    import java.awt.event.MouseEvent;
053    import java.awt.geom.Point2D;
054    import java.awt.geom.Rectangle2D;
055    
056    import javax.swing.JPanel;
057    
058    import org.eclipse.swt.SWT;
059    import org.eclipse.swt.graphics.Color;
060    import org.eclipse.swt.graphics.Device;
061    import org.eclipse.swt.graphics.Font;
062    import org.eclipse.swt.graphics.FontData;
063    import org.eclipse.swt.graphics.GC;
064    import org.eclipse.swt.graphics.Point;
065    import org.eclipse.swt.graphics.Rectangle;
066    
067    /**
068     * Utility class gathering some useful and general method.
069     * Mainly convert forth and back graphical stuff between 
070     * awt and swt.
071     */
072    public class SWTUtils {
073        
074        private final static String Az = "ABCpqr";
075    
076        /** A dummy JPanel used to provide font metrics. */
077        protected static final JPanel DUMMY_PANEL = new JPanel();
078    
079        /**
080         * Create a <code>FontData</code> object which encapsulate 
081         * the essential data to create a swt font. The data is taken 
082         * from the provided awt Font.
083         * <p>Generally speaking, given a font size, the returned swt font 
084         * will display differently on the screen than the awt one.
085         * Because the SWT toolkit use native graphical resources whenever 
086         * it is possible, this fact is platform dependent. To address 
087         * this issue, it is possible to enforce the method to return 
088         * a font with the same size (or at least as close as possible) 
089         * as the awt one.
090         * <p>When the object is no more used, the user must explicitly 
091         * call the dispose method on the returned font to free the 
092         * operating system resources (the garbage collector won't do it).
093         * 
094         * @param device The swt device to draw on (display or gc device).
095         * @param font The awt font from which to get the data.
096         * @param ensureSameSize A boolean used to enforce the same size 
097         * (in pixels) between the awt font and the newly created swt font.
098         * @return a <code>FontData</code> object.
099         */
100        public static FontData toSwtFontData(Device device, java.awt.Font font, 
101                boolean ensureSameSize) {
102            FontData fontData = new FontData();
103            fontData.setName(font.getFamily());
104            int style = SWT.NORMAL;
105            switch (font.getStyle()) {
106                case java.awt.Font.PLAIN:
107                    style |= SWT.NORMAL;
108                    break;
109                case java.awt.Font.BOLD:
110                    style |= SWT.BOLD;
111                    break;
112                case java.awt.Font.ITALIC:
113                    style |= SWT.ITALIC;
114                    break;
115                case (java.awt.Font.ITALIC + java.awt.Font.BOLD):
116                    style |= SWT.ITALIC | SWT.BOLD;
117                    break;
118            }
119            fontData.setStyle(style);
120            // convert the font size (in pt for awt) to height in pixels for swt
121            int height = (int) Math.round(font.getSize() * 72.0 
122                    / device.getDPI().y);
123            fontData.setHeight(height);
124            // hack to ensure the newly created swt fonts will be rendered with the
125            // same height as the awt one
126            if (ensureSameSize) {            
127                GC tmpGC = new GC(device);
128                Font tmpFont = new Font(device, fontData);
129                tmpGC.setFont(tmpFont);
130                if (tmpGC.textExtent(Az).x 
131                        > DUMMY_PANEL.getFontMetrics(font).stringWidth(Az)) {
132                    while (tmpGC.textExtent(Az).x 
133                            > DUMMY_PANEL.getFontMetrics(font).stringWidth(Az)) {
134                        tmpFont.dispose();
135                        height--;
136                        fontData.setHeight(height);
137                        tmpFont = new Font(device, fontData);
138                        tmpGC.setFont(tmpFont);
139                    }
140                }
141                else if (tmpGC.textExtent(Az).x 
142                        < DUMMY_PANEL.getFontMetrics(font).stringWidth(Az)) {
143                    while (tmpGC.textExtent(Az).x 
144                            < DUMMY_PANEL.getFontMetrics(font).stringWidth(Az)) {
145                        tmpFont.dispose();
146                        height++;
147                        fontData.setHeight(height);
148                        tmpFont = new Font(device, fontData);
149                        tmpGC.setFont(tmpFont);
150                    }
151                }
152                tmpFont.dispose();
153                tmpGC.dispose();
154            }
155            return fontData;
156        }
157        
158        /**
159         * Create an awt font by converting as much information 
160         * as possible from the provided swt <code>FontData</code>.
161         * <p>Generally speaking, given a font size, an swt font will 
162         * display differently on the screen than the corresponding awt 
163         * one. Because the SWT toolkit use native graphical ressources whenever 
164         * it is possible, this fact is platform dependent. To address 
165         * this issue, it is possible to enforce the method to return 
166         * an awt font with the same height as the swt one.
167         * 
168         * @param device The swt device being drawn on (display or gc device).
169         * @param fontData The swt font to convert.
170         * @param ensureSameSize A boolean used to enforce the same size 
171         * (in pixels) between the swt font and the newly created awt font.
172         * @return An awt font converted from the provided swt font.
173         */
174        public static java.awt.Font toAwtFont(Device device, FontData fontData, 
175                boolean ensureSameSize) {
176            int style;
177            switch (fontData.getStyle()) {
178                case SWT.NORMAL:
179                    style = java.awt.Font.PLAIN;
180                    break;
181                case SWT.ITALIC:
182                    style = java.awt.Font.ITALIC;
183                    break;
184                case SWT.BOLD:
185                    style = java.awt.Font.BOLD;
186                    break;
187                default:
188                    style = java.awt.Font.PLAIN;
189                    break;
190            }
191            int height = (int) Math.round(fontData.getHeight() * device.getDPI().y 
192                    / 72.0);
193            // hack to ensure the newly created awt fonts will be rendered with the
194            // same height as the swt one
195            if (ensureSameSize) {
196                GC tmpGC = new GC(device);
197                Font tmpFont = new Font(device, fontData);
198                tmpGC.setFont(tmpFont);
199                JPanel DUMMY_PANEL = new JPanel();
200                java.awt.Font tmpAwtFont = new java.awt.Font(fontData.getName(), 
201                        style, height);
202                if (DUMMY_PANEL.getFontMetrics(tmpAwtFont).stringWidth(Az) 
203                        > tmpGC.textExtent(Az).x) {
204                    while (DUMMY_PANEL.getFontMetrics(tmpAwtFont).stringWidth(Az) 
205                            > tmpGC.textExtent(Az).x) {
206                        height--;                
207                        tmpAwtFont = new java.awt.Font(fontData.getName(), style, 
208                                height);
209                    }
210                }
211                else if (DUMMY_PANEL.getFontMetrics(tmpAwtFont).stringWidth(Az) 
212                        < tmpGC.textExtent(Az).x) {
213                    while (DUMMY_PANEL.getFontMetrics(tmpAwtFont).stringWidth(Az) 
214                            < tmpGC.textExtent(Az).x) {
215                        height++;
216                        tmpAwtFont = new java.awt.Font(fontData.getName(), style, 
217                                height);
218                    }
219                }
220                tmpFont.dispose();
221                tmpGC.dispose();
222            }
223            return new java.awt.Font(fontData.getName(), style, height);
224        }
225    
226        /**
227         * Create an awt font by converting as much information 
228         * as possible from the provided swt <code>Font</code>.
229         * 
230         * @param device The swt device to draw on (display or gc device).
231         * @param font The swt font to convert.
232         * @return An awt font converted from the provided swt font.
233         */
234        public static java.awt.Font toAwtFont(Device device, Font font) {
235            FontData fontData = font.getFontData()[0]; 
236            return toAwtFont(device, fontData, true);
237        }
238    
239        /**
240         * Creates an awt color instance to match the rgb values 
241         * of the specified swt color.
242         * 
243         * @param color The swt color to match.
244         * @return an awt color abject.
245         */
246        public static java.awt.Color toAwtColor(Color color) {
247            return new java.awt.Color(color.getRed(), color.getGreen(), 
248                    color.getBlue());
249        }
250        
251        /**
252         * Creates a swt color instance to match the rgb values 
253         * of the specified awt paint. For now, this method test 
254         * if the paint is a color and then return the adequate 
255         * swt color. Otherwise plain black is assumed.
256         * 
257         * @param device The swt device to draw on (display or gc device).
258         * @param paint The awt color to match.
259         * @return a swt color object.
260         */
261        public static Color toSwtColor(Device device, java.awt.Paint paint) {
262            java.awt.Color color;
263            if (paint instanceof java.awt.Color) {
264                color = (java.awt.Color) paint;
265            }
266            else {
267                try {
268                    throw new Exception("only color is supported at present... " 
269                            + "setting paint to uniform black color" );
270                } 
271                catch (Exception e) {
272                    e.printStackTrace();
273                    color = new java.awt.Color(0, 0, 0);
274                }
275            }
276            return new org.eclipse.swt.graphics.Color(device,
277                    color.getRed(), color.getGreen(), color.getBlue());
278        }
279    
280        /**
281         * Creates a swt color instance to match the rgb values 
282         * of the specified awt color. alpha channel is not supported.
283         * Note that the dispose method will need to be called on the 
284         * returned object.
285         * 
286         * @param device The swt device to draw on (display or gc device).
287         * @param color The awt color to match.
288         * @return a swt color object.
289         */
290        public static Color toSwtColor(Device device, java.awt.Color color) {
291            return new org.eclipse.swt.graphics.Color(device,
292                    color.getRed(), color.getGreen(), color.getBlue());
293        }
294        
295        /**
296         * Transform an awt Rectangle2d instance into a swt one.
297         * The coordinates are rounded to integer for the swt object.
298         * @param rect2d The awt rectangle to map.
299         * @return an swt <code>Rectangle</code> object.
300         */
301        public static Rectangle toSwtRectangle(Rectangle2D rect2d) {
302            return new Rectangle(
303                    (int) Math.round(rect2d.getMinX()),
304                    (int) Math.round(rect2d.getMinY()),
305                    (int) Math.round(rect2d.getWidth()),
306                    (int) Math.round(rect2d.getHeight())
307                    );
308        }
309    
310        /**
311         * Transform a swt Rectangle instance into an awt one.
312         * @param rect the swt <code>Rectangle</code>
313         * @return a Rectangle2D.Double instance with 
314         * the eappropriate location and size.
315         */
316        public static Rectangle2D toAwtRectangle(Rectangle rect) {
317            Rectangle2D rect2d = new Rectangle2D.Double();
318            rect2d.setRect(rect.x, rect.y, rect.width, rect.height);
319            return rect2d;
320        }
321        
322        /**
323         * Returns an AWT point with the same coordinates as the specified 
324         * SWT point.
325         * 
326         * @param p  the SWT point (<code>null</code> not permitted).
327         * 
328         * @return An AWT point with the same coordinates as <code>p</code>.
329         * 
330         * @see #toSwtPoint(java.awt.Point)
331         */
332        public static Point2D toAwtPoint(Point p) {
333            return new java.awt.Point(p.x, p.y);
334        }
335    
336        /**
337         * Returns an SWT point with the same coordinates as the specified
338         * AWT point.
339         * 
340         * @param p  the AWT point (<code>null</code> not permitted).
341         * 
342         * @return An SWT point with the same coordinates as <code>p</code>.
343         * 
344         * @see #toAwtPoint(Point)
345         */
346        public static Point toSwtPoint(java.awt.Point p) {
347            return new Point(p.x, p.y);
348        }
349        
350        /**
351         * Returns an SWT point with the same coordinates as the specified AWT 
352         * point (rounded to integer values).
353         * 
354         * @param p  the AWT point (<code>null</code> not permitted).
355         * 
356         * @return An SWT point with the same coordinates as <code>p</code>.
357         * 
358         * @see #toAwtPoint(Point)
359         */
360        public static Point toSwtPoint(java.awt.geom.Point2D p) {
361            return new Point((int) Math.round(p.getX()), 
362                    (int) Math.round(p.getY()));
363        }
364        
365        /**
366         * Creates an AWT <code>MouseEvent</code> from a swt event.
367         * This method helps passing SWT mouse event to awt components.
368         * @param event The swt event.
369         * @return A AWT mouse event based on the given SWT event.
370         */
371        public static MouseEvent toAwtMouseEvent(org.eclipse.swt.events.MouseEvent event) {
372            int button = MouseEvent.NOBUTTON;
373            switch (event.button) {
374            case 1: button = MouseEvent.BUTTON1; break;
375            case 2: button = MouseEvent.BUTTON2; break;
376            case 3: button = MouseEvent.BUTTON3; break;
377            }
378            int modifiers = 0;
379            if ((event.stateMask & SWT.CTRL) != 0) {
380                modifiers |= InputEvent.CTRL_DOWN_MASK;
381            }
382            if ((event.stateMask & SWT.SHIFT) != 0) {
383                modifiers |= InputEvent.SHIFT_DOWN_MASK;
384            }
385            if ((event.stateMask & SWT.ALT) != 0) {
386                modifiers |= InputEvent.ALT_DOWN_MASK;
387            }
388            MouseEvent awtMouseEvent = new MouseEvent(DUMMY_PANEL, event.hashCode(), 
389                    event.time, modifiers, event.x, event.y, 1, false, button);
390            return awtMouseEvent;
391        }
392    
393    }