/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/package org.eclipse.wb.swt;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.HashMap;import java.util.Map;import org.eclipse.swt.SWT;import org.eclipse.swt.graphics.Color;import org.eclipse.swt.graphics.Cursor;import org.eclipse.swt.graphics.Font;import org.eclipse.swt.graphics.FontData;import org.eclipse.swt.graphics.GC;import org.eclipse.swt.graphics.Image;import org.eclipse.swt.graphics.ImageData;import org.eclipse.swt.graphics.RGB;import org.eclipse.swt.graphics.Rectangle;import org.eclipse.swt.widgets.Display;/**
* Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
* <p>
* !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
* operating system resources managed by cached objects when those objects and OS resources are no longer
* needed (e.g. on application shutdown)
* <p>
* This class may be freely distributed as part of any application or plugin.
* <p>
* @author scheglov_ke
* @author Dan Rubel
*/publicclassSWTResourceManager{//// Color//privatestatic Map<RGB, Color> m_colorMap =newHashMap<RGB, Color>();/**
* Returns the system {@link Color} matching the specific ID.
*
* @param systemColorID
* the ID value for the color
* @return the system {@link Color} matching the specific ID
*/publicstatic Color getColor(int systemColorID){
Display display = Display.getCurrent();return display.getSystemColor(systemColorID);}/**
* Returns a {@link Color} given its red, green and blue component values.
*
* @param r
* the red component of the color
* @param g
* the green component of the color
* @param b
* the blue component of the color
* @return the {@link Color} matching the given red, green and blue component values
*/publicstatic Color getColor(int r,int g,int b){returngetColor(newRGB(r, g, b));}/**
* Returns a {@link Color} given its RGB value.
*
* @param rgb
* the {@link RGB} value of the color
* @return the {@link Color} matching the RGB value
*/publicstatic Color getColor(RGB rgb){
Color color = m_colorMap.get(rgb);if(color == null){
Display display = Display.getCurrent();
color =newColor(display, rgb);
m_colorMap.put(rgb, color);}return color;}/**
* Dispose of all the cached {@link Color}'s.
*/publicstaticvoiddisposeColors(){for(Color color : m_colorMap.values()){
color.dispose();}
m_colorMap.clear();}//// Image///**
* Maps image paths to images.
*/privatestatic Map<String, Image> m_imageMap =newHashMap<String, Image>();/**
* Returns an {@link Image} encoded by the specified {@link InputStream}.
*
* @param stream
* the {@link InputStream} encoding the image data
* @return the {@link Image} encoded by the specified input stream
*/protectedstatic Image getImage(InputStream stream)throws IOException {try{
Display display = Display.getCurrent();
ImageData data =newImageData(stream);if(data.transparentPixel >0){returnnewImage(display, data, data.getTransparencyMask());}returnnewImage(display, data);}finally{
stream.close();}}/**
* Returns an {@link Image} stored in the file at the specified path.
*
* @param path
* the path to the image file
* @return the {@link Image} stored in the file at the specified path
*/publicstatic Image getImage(String path){
Image image = m_imageMap.get(path);if(image == null){try{
image =getImage(newFileInputStream(path));
m_imageMap.put(path, image);}catch(Exception e){
image =getMissingImage();
m_imageMap.put(path, image);}}return image;}/**
* Returns an {@link Image} stored in the file at the specified path relative to the specified class.
*
* @param clazz
* the {@link Class} relative to which to find the image
* @param path
* the path to the image file, if starts with <code>'/'</code>
* @return the {@link Image} stored in the file at the specified path
*/publicstatic Image getImage(Class<?> clazz, String path){
String key = clazz.getName()+'|'+ path;
Image image = m_imageMap.get(key);if(image == null){try{
image =getImage(clazz.getResourceAsStream(path));
m_imageMap.put(key, image);}catch(Exception e){
image =getMissingImage();
m_imageMap.put(key, image);}}return image;}privatestaticfinalint MISSING_IMAGE_SIZE =10;/**
* @return the small {@link Image} that can be used as placeholder for missing image.
*/privatestatic Image getMissingImage(){
Image image =newImage(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);//
GC gc =newGC(image);
gc.setBackground(getColor(SWT.COLOR_RED));
gc.fillRectangle(0,0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
gc.dispose();//return image;}/**
* Style constant for placing decorator image in top left corner of base image.
*/publicstaticfinalint TOP_LEFT =1;/**
* Style constant for placing decorator image in top right corner of base image.
*/publicstaticfinalint TOP_RIGHT =2;/**
* Style constant for placing decorator image in bottom left corner of base image.
*/publicstaticfinalint BOTTOM_LEFT =3;/**
* Style constant for placing decorator image in bottom right corner of base image.
*/publicstaticfinalint BOTTOM_RIGHT =4;/**
* Internal value.
*/protectedstaticfinalint LAST_CORNER_KEY =5;/**
* Maps images to decorated images.
*/@SuppressWarnings("unchecked")privatestatic Map<Image, Map<Image, Image>>[] m_decoratedImageMap =newMap[LAST_CORNER_KEY];/**
* Returns an {@link Image} composed of a base image decorated by another image.
*
* @param baseImage
* the base {@link Image} that should be decorated
* @param decorator
* the {@link Image} to decorate the base image
* @return {@link Image} The resulting decorated image
*/publicstatic Image decorateImage(Image baseImage, Image decorator){returndecorateImage(baseImage, decorator, BOTTOM_RIGHT);}/**
* Returns an {@link Image} composed of a base image decorated by another image.
*
* @param baseImage
* the base {@link Image} that should be decorated
* @param decorator
* the {@link Image} to decorate the base image
* @param corner
* the corner to place decorator image
* @return the resulting decorated {@link Image}
*/publicstatic Image decorateImage(final Image baseImage,final Image decorator,finalint corner){if(corner <=0|| corner >= LAST_CORNER_KEY){thrownewIllegalArgumentException("Wrong decorate corner");}
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];if(cornerDecoratedImageMap == null){
cornerDecoratedImageMap =newHashMap<Image, Map<Image, Image>>();
m_decoratedImageMap[corner]= cornerDecoratedImageMap;}
Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);if(decoratedMap == null){
decoratedMap =newHashMap<Image, Image>();
cornerDecoratedImageMap.put(baseImage, decoratedMap);}//
Image result = decoratedMap.get(decorator);if(result == null){
Rectangle bib = baseImage.getBounds();
Rectangle dib = decorator.getBounds();//
result =newImage(Display.getCurrent(), bib.width, bib.height);//
GC gc =newGC(result);
gc.drawImage(baseImage,0,0);if(corner == TOP_LEFT){
gc.drawImage(decorator,0,0);}elseif(corner == TOP_RIGHT){
gc.drawImage(decorator, bib.width - dib.width,0);}elseif(corner == BOTTOM_LEFT){
gc.drawImage(decorator,0, bib.height - dib.height);}elseif(corner == BOTTOM_RIGHT){
gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);}
gc.dispose();//
decoratedMap.put(decorator, result);}return result;}/**
* Dispose all of the cached {@link Image}'s.
*/publicstaticvoiddisposeImages(){// dispose loaded images{for(Image image : m_imageMap.values()){
image.dispose();}
m_imageMap.clear();}// dispose decorated imagesfor(int i =0; i < m_decoratedImageMap.length; i++){
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];if(cornerDecoratedImageMap != null){for(Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()){for(Image image : decoratedMap.values()){
image.dispose();}
decoratedMap.clear();}
cornerDecoratedImageMap.clear();}}}//// Font///**
* Maps font names to fonts.
*/privatestatic Map<String, Font> m_fontMap =newHashMap<String, Font>();/**
* Maps fonts to their bold versions.
*/privatestatic Map<Font, Font> m_fontToBoldFontMap =newHashMap<Font, Font>();/**
* Returns a {@link Font} based on its name, height and style.
*
* @param name
* the name of the font
* @param height
* the height of the font
* @param style
* the style of the font
* @return {@link Font} The font matching the name, height and style
*/publicstatic Font getFont(String name,int height,int style){returngetFont(name, height, style,false,false);}/**
* Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
* flags are also supported.
*
* @param name
* the name of the font
* @param size
* the size of the font
* @param style
* the style of the font
* @param strikeout
* the strikeout flag (warning: Windows only)
* @param underline
* the underline flag (warning: Windows only)
* @return {@link Font} The font matching the name, height, style, strikeout and underline
*/publicstatic Font getFont(String name,int size,int style,boolean strikeout,boolean underline){
String fontName = name +'|'+ size +'|'+ style +'|'+ strikeout +'|'+ underline;
Font font = m_fontMap.get(fontName);if(font == null){
FontData fontData =newFontData(name, size, style);if(strikeout || underline){try{
Class<?> logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT");//$NON-NLS-1$
Object logFont = FontData.class.getField("data").get(fontData);//$NON-NLS-1$if(logFont != null && logFontClass != null){if(strikeout){
logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte)1));//$NON-NLS-1$}if(underline){
logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte)1));//$NON-NLS-1$}}}catch(Throwable e){
System.err.println("Unable to set underline or strikeout"+" (probably on a non-Windows platform). "+ e);//$NON-NLS-1$ //$NON-NLS-2$}}
font =newFont(Display.getCurrent(), fontData);
m_fontMap.put(fontName, font);}return font;}/**
* Returns a bold version of the given {@link Font}.
*
* @param baseFont
* the {@link Font} for which a bold version is desired
* @return the bold version of the given {@link Font}
*/publicstatic Font getBoldFont(Font baseFont){
Font font = m_fontToBoldFontMap.get(baseFont);if(font == null){
FontData fontDatas[]= baseFont.getFontData();
FontData data = fontDatas[0];
font =newFont(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
m_fontToBoldFontMap.put(baseFont, font);}return font;}/**
* Dispose all of the cached {@link Font}'s.
*/publicstaticvoiddisposeFonts(){// clear fontsfor(Font font : m_fontMap.values()){
font.dispose();}
m_fontMap.clear();// clear bold fontsfor(Font font : m_fontToBoldFontMap.values()){
font.dispose();}
m_fontToBoldFontMap.clear();}//// Cursor///**
* Maps IDs to cursors.
*/privatestatic Map<Integer, Cursor> m_idToCursorMap =newHashMap<Integer, Cursor>();/**
* Returns the system cursor matching the specific ID.
*
* @param id
* int The ID value for the cursor
* @return Cursor The system cursor matching the specific ID
*/publicstatic Cursor getCursor(int id){
Integer key = Integer.valueOf(id);
Cursor cursor = m_idToCursorMap.get(key);if(cursor == null){
cursor =newCursor(Display.getDefault(), id);
m_idToCursorMap.put(key, cursor);}return cursor;}/**
* Dispose all of the cached cursors.
*/publicstaticvoiddisposeCursors(){for(Cursor cursor : m_idToCursorMap.values()){
cursor.dispose();}
m_idToCursorMap.clear();}//// General///**
* Dispose of cached objects and their underlying OS resources. This should only be called when the cached
* objects are no longer needed (e.g. on application shutdown).
*/publicstaticvoiddispose(){disposeColors();disposeImages();disposeFonts();disposeCursors();}}