1 /*******************************************************************************
2 * Copyright (c) 2011 Google, Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Google, Inc. - initial API and implementation
10 *******************************************************************************/
11 package org
.eclipse
.wb
.swt
;
13 import java
.io
.FileInputStream
;
14 import java
.io
.IOException
;
15 import java
.io
.InputStream
;
16 import java
.util
.HashMap
;
19 import org
.eclipse
.swt
.SWT
;
20 import org
.eclipse
.swt
.graphics
.Color
;
21 import org
.eclipse
.swt
.graphics
.Cursor
;
22 import org
.eclipse
.swt
.graphics
.Font
;
23 import org
.eclipse
.swt
.graphics
.FontData
;
24 import org
.eclipse
.swt
.graphics
.GC
;
25 import org
.eclipse
.swt
.graphics
.Image
;
26 import org
.eclipse
.swt
.graphics
.ImageData
;
27 import org
.eclipse
.swt
.graphics
.RGB
;
28 import org
.eclipse
.swt
.graphics
.Rectangle
;
29 import org
.eclipse
.swt
.widgets
.Display
;
32 * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
34 * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
35 * operating system resources managed by cached objects when those objects and OS resources are no longer
36 * needed (e.g. on application shutdown)
38 * This class may be freely distributed as part of any application or plugin.
43 public class SWTResourceManager
{
44 ////////////////////////////////////////////////////////////////////////////
48 ////////////////////////////////////////////////////////////////////////////
49 private static Map
<RGB
, Color
> m_colorMap
= new HashMap
<RGB
, Color
>();
51 * Returns the system {@link Color} matching the specific ID.
53 * @param systemColorID
54 * the ID value for the color
55 * @return the system {@link Color} matching the specific ID
57 public static Color
getColor(int systemColorID
) {
58 Display display
= Display
.getCurrent();
59 return display
.getSystemColor(systemColorID
);
62 * Returns a {@link Color} given its red, green and blue component values.
65 * the red component of the color
67 * the green component of the color
69 * the blue component of the color
70 * @return the {@link Color} matching the given red, green and blue component values
72 public static Color
getColor(int r
, int g
, int b
) {
73 return getColor(new RGB(r
, g
, b
));
76 * Returns a {@link Color} given its RGB value.
79 * the {@link RGB} value of the color
80 * @return the {@link Color} matching the RGB value
82 public static Color
getColor(RGB rgb
) {
83 Color color
= m_colorMap
.get(rgb
);
85 Display display
= Display
.getCurrent();
86 color
= new Color(display
, rgb
);
87 m_colorMap
.put(rgb
, color
);
92 * Dispose of all the cached {@link Color}'s.
94 public static void disposeColors() {
95 for (Color color
: m_colorMap
.values()) {
100 ////////////////////////////////////////////////////////////////////////////
104 ////////////////////////////////////////////////////////////////////////////
106 * Maps image paths to images.
108 private static Map
<String
, Image
> m_imageMap
= new HashMap
<String
, Image
>();
110 * Returns an {@link Image} encoded by the specified {@link InputStream}.
113 * the {@link InputStream} encoding the image data
114 * @return the {@link Image} encoded by the specified input stream
116 protected static Image
getImage(InputStream stream
) throws IOException
{
118 Display display
= Display
.getCurrent();
119 ImageData data
= new ImageData(stream
);
120 if (data
.transparentPixel
> 0) {
121 return new Image(display
, data
, data
.getTransparencyMask());
123 return new Image(display
, data
);
129 * Returns an {@link Image} stored in the file at the specified path.
132 * the path to the image file
133 * @return the {@link Image} stored in the file at the specified path
135 public static Image
getImage(String path
) {
136 Image image
= m_imageMap
.get(path
);
139 image
= getImage(new FileInputStream(path
));
140 m_imageMap
.put(path
, image
);
141 } catch (Exception e
) {
142 image
= getMissingImage();
143 m_imageMap
.put(path
, image
);
149 * Returns an {@link Image} stored in the file at the specified path relative to the specified class.
152 * the {@link Class} relative to which to find the image
154 * the path to the image file, if starts with <code>'/'</code>
155 * @return the {@link Image} stored in the file at the specified path
157 public static Image
getImage(Class
<?
> clazz
, String path
) {
158 String key
= clazz
.getName() + '|' + path
;
159 Image image
= m_imageMap
.get(key
);
162 image
= getImage(clazz
.getResourceAsStream(path
));
163 m_imageMap
.put(key
, image
);
164 } catch (Exception e
) {
165 image
= getMissingImage();
166 m_imageMap
.put(key
, image
);
171 private static final int MISSING_IMAGE_SIZE
= 10;
173 * @return the small {@link Image} that can be used as placeholder for missing image.
175 private static Image
getMissingImage() {
176 Image image
= new Image(Display
.getCurrent(), MISSING_IMAGE_SIZE
, MISSING_IMAGE_SIZE
);
178 GC gc
= new GC(image
);
179 gc
.setBackground(getColor(SWT
.COLOR_RED
));
180 gc
.fillRectangle(0, 0, MISSING_IMAGE_SIZE
, MISSING_IMAGE_SIZE
);
186 * Style constant for placing decorator image in top left corner of base image.
188 public static final int TOP_LEFT
= 1;
190 * Style constant for placing decorator image in top right corner of base image.
192 public static final int TOP_RIGHT
= 2;
194 * Style constant for placing decorator image in bottom left corner of base image.
196 public static final int BOTTOM_LEFT
= 3;
198 * Style constant for placing decorator image in bottom right corner of base image.
200 public static final int BOTTOM_RIGHT
= 4;
204 protected static final int LAST_CORNER_KEY
= 5;
206 * Maps images to decorated images.
208 @SuppressWarnings("unchecked")
209 private static Map
<Image
, Map
<Image
, Image
>>[] m_decoratedImageMap
= new Map
[LAST_CORNER_KEY
];
211 * Returns an {@link Image} composed of a base image decorated by another image.
214 * the base {@link Image} that should be decorated
216 * the {@link Image} to decorate the base image
217 * @return {@link Image} The resulting decorated image
219 public static Image
decorateImage(Image baseImage
, Image decorator
) {
220 return decorateImage(baseImage
, decorator
, BOTTOM_RIGHT
);
223 * Returns an {@link Image} composed of a base image decorated by another image.
226 * the base {@link Image} that should be decorated
228 * the {@link Image} to decorate the base image
230 * the corner to place decorator image
231 * @return the resulting decorated {@link Image}
233 public static Image
decorateImage(final Image baseImage
, final Image decorator
, final int corner
) {
234 if (corner
<= 0 || corner
>= LAST_CORNER_KEY
) {
235 throw new IllegalArgumentException("Wrong decorate corner");
237 Map
<Image
, Map
<Image
, Image
>> cornerDecoratedImageMap
= m_decoratedImageMap
[corner
];
238 if (cornerDecoratedImageMap
== null) {
239 cornerDecoratedImageMap
= new HashMap
<Image
, Map
<Image
, Image
>>();
240 m_decoratedImageMap
[corner
] = cornerDecoratedImageMap
;
242 Map
<Image
, Image
> decoratedMap
= cornerDecoratedImageMap
.get(baseImage
);
243 if (decoratedMap
== null) {
244 decoratedMap
= new HashMap
<Image
, Image
>();
245 cornerDecoratedImageMap
.put(baseImage
, decoratedMap
);
248 Image result
= decoratedMap
.get(decorator
);
249 if (result
== null) {
250 Rectangle bib
= baseImage
.getBounds();
251 Rectangle dib
= decorator
.getBounds();
253 result
= new Image(Display
.getCurrent(), bib
.width
, bib
.height
);
255 GC gc
= new GC(result
);
256 gc
.drawImage(baseImage
, 0, 0);
257 if (corner
== TOP_LEFT
) {
258 gc
.drawImage(decorator
, 0, 0);
259 } else if (corner
== TOP_RIGHT
) {
260 gc
.drawImage(decorator
, bib
.width
- dib
.width
, 0);
261 } else if (corner
== BOTTOM_LEFT
) {
262 gc
.drawImage(decorator
, 0, bib
.height
- dib
.height
);
263 } else if (corner
== BOTTOM_RIGHT
) {
264 gc
.drawImage(decorator
, bib
.width
- dib
.width
, bib
.height
- dib
.height
);
268 decoratedMap
.put(decorator
, result
);
273 * Dispose all of the cached {@link Image}'s.
275 public static void disposeImages() {
276 // dispose loaded images
278 for (Image image
: m_imageMap
.values()) {
283 // dispose decorated images
284 for (int i
= 0; i
< m_decoratedImageMap
.length
; i
++) {
285 Map
<Image
, Map
<Image
, Image
>> cornerDecoratedImageMap
= m_decoratedImageMap
[i
];
286 if (cornerDecoratedImageMap
!= null) {
287 for (Map
<Image
, Image
> decoratedMap
: cornerDecoratedImageMap
.values()) {
288 for (Image image
: decoratedMap
.values()) {
291 decoratedMap
.clear();
293 cornerDecoratedImageMap
.clear();
297 ////////////////////////////////////////////////////////////////////////////
301 ////////////////////////////////////////////////////////////////////////////
303 * Maps font names to fonts.
305 private static Map
<String
, Font
> m_fontMap
= new HashMap
<String
, Font
>();
307 * Maps fonts to their bold versions.
309 private static Map
<Font
, Font
> m_fontToBoldFontMap
= new HashMap
<Font
, Font
>();
311 * Returns a {@link Font} based on its name, height and style.
314 * the name of the font
316 * the height of the font
318 * the style of the font
319 * @return {@link Font} The font matching the name, height and style
321 public static Font
getFont(String name
, int height
, int style
) {
322 return getFont(name
, height
, style
, false, false);
325 * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
326 * flags are also supported.
329 * the name of the font
331 * the size of the font
333 * the style of the font
335 * the strikeout flag (warning: Windows only)
337 * the underline flag (warning: Windows only)
338 * @return {@link Font} The font matching the name, height, style, strikeout and underline
340 public static Font
getFont(String name
, int size
, int style
, boolean strikeout
, boolean underline
) {
341 String fontName
= name
+ '|' + size
+ '|' + style
+ '|' + strikeout
+ '|' + underline
;
342 Font font
= m_fontMap
.get(fontName
);
344 FontData fontData
= new FontData(name
, size
, style
);
345 if (strikeout
|| underline
) {
347 Class
<?
> logFontClass
= Class
.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
348 Object logFont
= FontData
.class.getField("data").get(fontData
); //$NON-NLS-1$
349 if (logFont
!= null && logFontClass
!= null) {
351 logFontClass
.getField("lfStrikeOut").set(logFont
, Byte
.valueOf((byte) 1)); //$NON-NLS-1$
354 logFontClass
.getField("lfUnderline").set(logFont
, Byte
.valueOf((byte) 1)); //$NON-NLS-1$
357 } catch (Throwable e
) {
358 System
.err
.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e
); //$NON-NLS-1$ //$NON-NLS-2$
361 font
= new Font(Display
.getCurrent(), fontData
);
362 m_fontMap
.put(fontName
, font
);
367 * Returns a bold version of the given {@link Font}.
370 * the {@link Font} for which a bold version is desired
371 * @return the bold version of the given {@link Font}
373 public static Font
getBoldFont(Font baseFont
) {
374 Font font
= m_fontToBoldFontMap
.get(baseFont
);
376 FontData fontDatas
[] = baseFont
.getFontData();
377 FontData data
= fontDatas
[0];
378 font
= new Font(Display
.getCurrent(), data
.getName(), data
.getHeight(), SWT
.BOLD
);
379 m_fontToBoldFontMap
.put(baseFont
, font
);
384 * Dispose all of the cached {@link Font}'s.
386 public static void disposeFonts() {
388 for (Font font
: m_fontMap
.values()) {
393 for (Font font
: m_fontToBoldFontMap
.values()) {
396 m_fontToBoldFontMap
.clear();
398 ////////////////////////////////////////////////////////////////////////////
402 ////////////////////////////////////////////////////////////////////////////
404 * Maps IDs to cursors.
406 private static Map
<Integer
, Cursor
> m_idToCursorMap
= new HashMap
<Integer
, Cursor
>();
408 * Returns the system cursor matching the specific ID.
411 * int The ID value for the cursor
412 * @return Cursor The system cursor matching the specific ID
414 public static Cursor
getCursor(int id
) {
415 Integer key
= Integer
.valueOf(id
);
416 Cursor cursor
= m_idToCursorMap
.get(key
);
417 if (cursor
== null) {
418 cursor
= new Cursor(Display
.getDefault(), id
);
419 m_idToCursorMap
.put(key
, cursor
);
424 * Dispose all of the cached cursors.
426 public static void disposeCursors() {
427 for (Cursor cursor
: m_idToCursorMap
.values()) {
430 m_idToCursorMap
.clear();
432 ////////////////////////////////////////////////////////////////////////////
436 ////////////////////////////////////////////////////////////////////////////
438 * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
439 * objects are no longer needed (e.g. on application shutdown).
441 public static void dispose() {