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
|
7
|
*
|
8
|
* Contributors:
|
9
|
* Google, Inc. - initial API and implementation
|
10
|
*******************************************************************************/
|
11
|
package org.eclipse.wb.swt;
|
12
|
|
13
|
import java.io.FileInputStream;
|
14
|
import java.io.IOException;
|
15
|
import java.io.InputStream;
|
16
|
import java.util.HashMap;
|
17
|
import java.util.Map;
|
18
|
|
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;
|
30
|
|
31
|
/**
|
32
|
* Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
|
33
|
* <p>
|
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)
|
37
|
* <p>
|
38
|
* This class may be freely distributed as part of any application or plugin.
|
39
|
* <p>
|
40
|
* @author scheglov_ke
|
41
|
* @author Dan Rubel
|
42
|
*/
|
43
|
public class SWTResourceManager {
|
44
|
////////////////////////////////////////////////////////////////////////////
|
45
|
//
|
46
|
// Color
|
47
|
//
|
48
|
////////////////////////////////////////////////////////////////////////////
|
49
|
private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
|
50
|
/**
|
51
|
* Returns the system {@link Color} matching the specific ID.
|
52
|
*
|
53
|
* @param systemColorID
|
54
|
* the ID value for the color
|
55
|
* @return the system {@link Color} matching the specific ID
|
56
|
*/
|
57
|
public static Color getColor(int systemColorID) {
|
58
|
Display display = Display.getCurrent();
|
59
|
return display.getSystemColor(systemColorID);
|
60
|
}
|
61
|
/**
|
62
|
* Returns a {@link Color} given its red, green and blue component values.
|
63
|
*
|
64
|
* @param r
|
65
|
* the red component of the color
|
66
|
* @param g
|
67
|
* the green component of the color
|
68
|
* @param b
|
69
|
* the blue component of the color
|
70
|
* @return the {@link Color} matching the given red, green and blue component values
|
71
|
*/
|
72
|
public static Color getColor(int r, int g, int b) {
|
73
|
return getColor(new RGB(r, g, b));
|
74
|
}
|
75
|
/**
|
76
|
* Returns a {@link Color} given its RGB value.
|
77
|
*
|
78
|
* @param rgb
|
79
|
* the {@link RGB} value of the color
|
80
|
* @return the {@link Color} matching the RGB value
|
81
|
*/
|
82
|
public static Color getColor(RGB rgb) {
|
83
|
Color color = m_colorMap.get(rgb);
|
84
|
if (color == null) {
|
85
|
Display display = Display.getCurrent();
|
86
|
color = new Color(display, rgb);
|
87
|
m_colorMap.put(rgb, color);
|
88
|
}
|
89
|
return color;
|
90
|
}
|
91
|
/**
|
92
|
* Dispose of all the cached {@link Color}'s.
|
93
|
*/
|
94
|
public static void disposeColors() {
|
95
|
for (Color color : m_colorMap.values()) {
|
96
|
color.dispose();
|
97
|
}
|
98
|
m_colorMap.clear();
|
99
|
}
|
100
|
////////////////////////////////////////////////////////////////////////////
|
101
|
//
|
102
|
// Image
|
103
|
//
|
104
|
////////////////////////////////////////////////////////////////////////////
|
105
|
/**
|
106
|
* Maps image paths to images.
|
107
|
*/
|
108
|
private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
|
109
|
/**
|
110
|
* Returns an {@link Image} encoded by the specified {@link InputStream}.
|
111
|
*
|
112
|
* @param stream
|
113
|
* the {@link InputStream} encoding the image data
|
114
|
* @return the {@link Image} encoded by the specified input stream
|
115
|
*/
|
116
|
protected static Image getImage(InputStream stream) throws IOException {
|
117
|
try {
|
118
|
Display display = Display.getCurrent();
|
119
|
ImageData data = new ImageData(stream);
|
120
|
if (data.transparentPixel > 0) {
|
121
|
return new Image(display, data, data.getTransparencyMask());
|
122
|
}
|
123
|
return new Image(display, data);
|
124
|
} finally {
|
125
|
stream.close();
|
126
|
}
|
127
|
}
|
128
|
/**
|
129
|
* Returns an {@link Image} stored in the file at the specified path.
|
130
|
*
|
131
|
* @param path
|
132
|
* the path to the image file
|
133
|
* @return the {@link Image} stored in the file at the specified path
|
134
|
*/
|
135
|
public static Image getImage(String path) {
|
136
|
Image image = m_imageMap.get(path);
|
137
|
if (image == null) {
|
138
|
try {
|
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);
|
144
|
}
|
145
|
}
|
146
|
return image;
|
147
|
}
|
148
|
/**
|
149
|
* Returns an {@link Image} stored in the file at the specified path relative to the specified class.
|
150
|
*
|
151
|
* @param clazz
|
152
|
* the {@link Class} relative to which to find the image
|
153
|
* @param path
|
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
|
156
|
*/
|
157
|
public static Image getImage(Class<?> clazz, String path) {
|
158
|
String key = clazz.getName() + '|' + path;
|
159
|
Image image = m_imageMap.get(key);
|
160
|
if (image == null) {
|
161
|
try {
|
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);
|
167
|
}
|
168
|
}
|
169
|
return image;
|
170
|
}
|
171
|
private static final int MISSING_IMAGE_SIZE = 10;
|
172
|
/**
|
173
|
* @return the small {@link Image} that can be used as placeholder for missing image.
|
174
|
*/
|
175
|
private static Image getMissingImage() {
|
176
|
Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
|
177
|
//
|
178
|
GC gc = new GC(image);
|
179
|
gc.setBackground(getColor(SWT.COLOR_RED));
|
180
|
gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
|
181
|
gc.dispose();
|
182
|
//
|
183
|
return image;
|
184
|
}
|
185
|
/**
|
186
|
* Style constant for placing decorator image in top left corner of base image.
|
187
|
*/
|
188
|
public static final int TOP_LEFT = 1;
|
189
|
/**
|
190
|
* Style constant for placing decorator image in top right corner of base image.
|
191
|
*/
|
192
|
public static final int TOP_RIGHT = 2;
|
193
|
/**
|
194
|
* Style constant for placing decorator image in bottom left corner of base image.
|
195
|
*/
|
196
|
public static final int BOTTOM_LEFT = 3;
|
197
|
/**
|
198
|
* Style constant for placing decorator image in bottom right corner of base image.
|
199
|
*/
|
200
|
public static final int BOTTOM_RIGHT = 4;
|
201
|
/**
|
202
|
* Internal value.
|
203
|
*/
|
204
|
protected static final int LAST_CORNER_KEY = 5;
|
205
|
/**
|
206
|
* Maps images to decorated images.
|
207
|
*/
|
208
|
@SuppressWarnings("unchecked")
|
209
|
private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
|
210
|
/**
|
211
|
* Returns an {@link Image} composed of a base image decorated by another image.
|
212
|
*
|
213
|
* @param baseImage
|
214
|
* the base {@link Image} that should be decorated
|
215
|
* @param decorator
|
216
|
* the {@link Image} to decorate the base image
|
217
|
* @return {@link Image} The resulting decorated image
|
218
|
*/
|
219
|
public static Image decorateImage(Image baseImage, Image decorator) {
|
220
|
return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
|
221
|
}
|
222
|
/**
|
223
|
* Returns an {@link Image} composed of a base image decorated by another image.
|
224
|
*
|
225
|
* @param baseImage
|
226
|
* the base {@link Image} that should be decorated
|
227
|
* @param decorator
|
228
|
* the {@link Image} to decorate the base image
|
229
|
* @param corner
|
230
|
* the corner to place decorator image
|
231
|
* @return the resulting decorated {@link Image}
|
232
|
*/
|
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");
|
236
|
}
|
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;
|
241
|
}
|
242
|
Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
|
243
|
if (decoratedMap == null) {
|
244
|
decoratedMap = new HashMap<Image, Image>();
|
245
|
cornerDecoratedImageMap.put(baseImage, decoratedMap);
|
246
|
}
|
247
|
//
|
248
|
Image result = decoratedMap.get(decorator);
|
249
|
if (result == null) {
|
250
|
Rectangle bib = baseImage.getBounds();
|
251
|
Rectangle dib = decorator.getBounds();
|
252
|
//
|
253
|
result = new Image(Display.getCurrent(), bib.width, bib.height);
|
254
|
//
|
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);
|
265
|
}
|
266
|
gc.dispose();
|
267
|
//
|
268
|
decoratedMap.put(decorator, result);
|
269
|
}
|
270
|
return result;
|
271
|
}
|
272
|
/**
|
273
|
* Dispose all of the cached {@link Image}'s.
|
274
|
*/
|
275
|
public static void disposeImages() {
|
276
|
// dispose loaded images
|
277
|
{
|
278
|
for (Image image : m_imageMap.values()) {
|
279
|
image.dispose();
|
280
|
}
|
281
|
m_imageMap.clear();
|
282
|
}
|
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()) {
|
289
|
image.dispose();
|
290
|
}
|
291
|
decoratedMap.clear();
|
292
|
}
|
293
|
cornerDecoratedImageMap.clear();
|
294
|
}
|
295
|
}
|
296
|
}
|
297
|
////////////////////////////////////////////////////////////////////////////
|
298
|
//
|
299
|
// Font
|
300
|
//
|
301
|
////////////////////////////////////////////////////////////////////////////
|
302
|
/**
|
303
|
* Maps font names to fonts.
|
304
|
*/
|
305
|
private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
|
306
|
/**
|
307
|
* Maps fonts to their bold versions.
|
308
|
*/
|
309
|
private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
|
310
|
/**
|
311
|
* Returns a {@link Font} based on its name, height and style.
|
312
|
*
|
313
|
* @param name
|
314
|
* the name of the font
|
315
|
* @param height
|
316
|
* the height of the font
|
317
|
* @param style
|
318
|
* the style of the font
|
319
|
* @return {@link Font} The font matching the name, height and style
|
320
|
*/
|
321
|
public static Font getFont(String name, int height, int style) {
|
322
|
return getFont(name, height, style, false, false);
|
323
|
}
|
324
|
/**
|
325
|
* Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
|
326
|
* flags are also supported.
|
327
|
*
|
328
|
* @param name
|
329
|
* the name of the font
|
330
|
* @param size
|
331
|
* the size of the font
|
332
|
* @param style
|
333
|
* the style of the font
|
334
|
* @param strikeout
|
335
|
* the strikeout flag (warning: Windows only)
|
336
|
* @param underline
|
337
|
* the underline flag (warning: Windows only)
|
338
|
* @return {@link Font} The font matching the name, height, style, strikeout and underline
|
339
|
*/
|
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);
|
343
|
if (font == null) {
|
344
|
FontData fontData = new FontData(name, size, style);
|
345
|
if (strikeout || underline) {
|
346
|
try {
|
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) {
|
350
|
if (strikeout) {
|
351
|
logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
|
352
|
}
|
353
|
if (underline) {
|
354
|
logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
|
355
|
}
|
356
|
}
|
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$
|
359
|
}
|
360
|
}
|
361
|
font = new Font(Display.getCurrent(), fontData);
|
362
|
m_fontMap.put(fontName, font);
|
363
|
}
|
364
|
return font;
|
365
|
}
|
366
|
/**
|
367
|
* Returns a bold version of the given {@link Font}.
|
368
|
*
|
369
|
* @param baseFont
|
370
|
* the {@link Font} for which a bold version is desired
|
371
|
* @return the bold version of the given {@link Font}
|
372
|
*/
|
373
|
public static Font getBoldFont(Font baseFont) {
|
374
|
Font font = m_fontToBoldFontMap.get(baseFont);
|
375
|
if (font == null) {
|
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);
|
380
|
}
|
381
|
return font;
|
382
|
}
|
383
|
/**
|
384
|
* Dispose all of the cached {@link Font}'s.
|
385
|
*/
|
386
|
public static void disposeFonts() {
|
387
|
// clear fonts
|
388
|
for (Font font : m_fontMap.values()) {
|
389
|
font.dispose();
|
390
|
}
|
391
|
m_fontMap.clear();
|
392
|
// clear bold fonts
|
393
|
for (Font font : m_fontToBoldFontMap.values()) {
|
394
|
font.dispose();
|
395
|
}
|
396
|
m_fontToBoldFontMap.clear();
|
397
|
}
|
398
|
////////////////////////////////////////////////////////////////////////////
|
399
|
//
|
400
|
// Cursor
|
401
|
//
|
402
|
////////////////////////////////////////////////////////////////////////////
|
403
|
/**
|
404
|
* Maps IDs to cursors.
|
405
|
*/
|
406
|
private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
|
407
|
/**
|
408
|
* Returns the system cursor matching the specific ID.
|
409
|
*
|
410
|
* @param id
|
411
|
* int The ID value for the cursor
|
412
|
* @return Cursor The system cursor matching the specific ID
|
413
|
*/
|
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);
|
420
|
}
|
421
|
return cursor;
|
422
|
}
|
423
|
/**
|
424
|
* Dispose all of the cached cursors.
|
425
|
*/
|
426
|
public static void disposeCursors() {
|
427
|
for (Cursor cursor : m_idToCursorMap.values()) {
|
428
|
cursor.dispose();
|
429
|
}
|
430
|
m_idToCursorMap.clear();
|
431
|
}
|
432
|
////////////////////////////////////////////////////////////////////////////
|
433
|
//
|
434
|
// General
|
435
|
//
|
436
|
////////////////////////////////////////////////////////////////////////////
|
437
|
/**
|
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).
|
440
|
*/
|
441
|
public static void dispose() {
|
442
|
disposeColors();
|
443
|
disposeImages();
|
444
|
disposeFonts();
|
445
|
disposeCursors();
|
446
|
}
|
447
|
}
|