001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Steven Ketcham (sketcham@dsicdi.com) - Bug 42451
011: * [Dialogs] ImageRegistry throws null pointer exception in
012: * application with multiple Display's
013: *******************************************************************************/package org.eclipse.jface.resource;
014:
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.Map;
018:
019: import org.eclipse.jface.dialogs.Dialog;
020: import org.eclipse.core.runtime.Assert;
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.graphics.Device;
023: import org.eclipse.swt.graphics.Image;
024: import org.eclipse.swt.graphics.ImageData;
025: import org.eclipse.swt.widgets.Display;
026:
027: /**
028: * An image registry maintains a mapping between symbolic image names
029: * and SWT image objects or special image descriptor objects which
030: * defer the creation of SWT image objects until they are needed.
031: * <p>
032: * An image registry owns all of the image objects registered
033: * with it, and automatically disposes of them when the SWT Display
034: * that creates the images is disposed. Because of this, clients do not
035: * need to (indeed, must not attempt to) dispose of these images themselves.
036: * </p>
037: * <p>
038: * Clients may instantiate this class (it was not designed to be subclassed).
039: * </p>
040: * <p>
041: * Unlike the FontRegistry, it is an error to replace images. As a result
042: * there are no events that fire when values are changed in the registry
043: * </p>
044: */
045: public class ImageRegistry {
046: /**
047: * display used when getting images
048: */
049: private Display display;
050:
051: private ResourceManager manager;
052:
053: private Map table;
054:
055: private Runnable disposeRunnable = new Runnable() {
056: public void run() {
057: dispose();
058: }
059: };
060:
061: /**
062: * Contains the data for an entry in the registry.
063: */
064: private static class Entry {
065: /** the image */
066: protected Image image;
067:
068: /** the descriptor */
069: protected ImageDescriptor descriptor;
070: }
071:
072: private static class OriginalImageDescriptor extends
073: ImageDescriptor {
074: private Image original;
075: private int refCount = 0;
076: private Device originalDisplay;
077:
078: /**
079: * @param original the original image
080: * @param originalDisplay the device the image is part of
081: */
082: public OriginalImageDescriptor(Image original,
083: Device originalDisplay) {
084: this .original = original;
085: this .originalDisplay = originalDisplay;
086: }
087:
088: public Object createResource(Device device)
089: throws DeviceResourceException {
090: if (device == originalDisplay) {
091: refCount++;
092: return original;
093: }
094: return super .createResource(device);
095: }
096:
097: public void destroyResource(Object toDispose) {
098: if (original == toDispose) {
099: refCount--;
100: if (refCount == 0) {
101: original.dispose();
102: original = null;
103: }
104: } else {
105: super .destroyResource(toDispose);
106: }
107: }
108:
109: /* (non-Javadoc)
110: * @see org.eclipse.jface.resource.ImageDescriptor#getImageData()
111: */
112: public ImageData getImageData() {
113: return original.getImageData();
114: }
115: }
116:
117: /**
118: * Creates an empty image registry.
119: * <p>
120: * There must be an SWT Display created in the current
121: * thread before calling this method.
122: * </p>
123: */
124: public ImageRegistry() {
125: this (Display.getCurrent());
126: }
127:
128: /**
129: * Creates an empty image registry using the given resource manager to allocate images.
130: *
131: * @param manager the resource manager used to allocate images
132: *
133: * @since 3.1
134: */
135: public ImageRegistry(ResourceManager manager) {
136: Assert.isNotNull(manager);
137: Device dev = manager.getDevice();
138: if (dev instanceof Display) {
139: this .display = (Display) dev;
140: }
141: this .manager = manager;
142: manager.disposeExec(disposeRunnable);
143: }
144:
145: /**
146: * Creates an empty image registry.
147: *
148: * @param display this <code>Display</code> must not be
149: * <code>null</code> and must not be disposed in order
150: * to use this registry
151: */
152: public ImageRegistry(Display display) {
153: this (JFaceResources.getResources(display));
154: }
155:
156: /**
157: * Returns the image associated with the given key in this registry,
158: * or <code>null</code> if none.
159: *
160: * @param key the key
161: * @return the image, or <code>null</code> if none
162: */
163: public Image get(String key) {
164:
165: // can be null
166: if (key == null) {
167: return null;
168: }
169:
170: if (display != null) {
171: /**
172: * NOTE, for backwards compatibility the following images are supported
173: * here, they should never be disposed, hence we explicitly return them
174: * rather then registering images that SWT will dispose.
175: *
176: * Applications should go direclty to SWT for these icons.
177: *
178: * @see Display.getSystemIcon(int ID)
179: */
180: int swtKey = -1;
181: if (key.equals(Dialog.DLG_IMG_INFO)) {
182: swtKey = SWT.ICON_INFORMATION;
183: }
184: if (key.equals(Dialog.DLG_IMG_QUESTION)) {
185: swtKey = SWT.ICON_QUESTION;
186: }
187: if (key.equals(Dialog.DLG_IMG_WARNING)) {
188: swtKey = SWT.ICON_WARNING;
189: }
190: if (key.equals(Dialog.DLG_IMG_ERROR)) {
191: swtKey = SWT.ICON_ERROR;
192: }
193: // if we actually just want to return an SWT image do so without
194: // looking in the registry
195: if (swtKey != -1) {
196: final Image[] image = new Image[1];
197: final int id = swtKey;
198: display.syncExec(new Runnable() {
199: public void run() {
200: image[0] = display.getSystemImage(id);
201: }
202: });
203: return image[0];
204: }
205: }
206:
207: Entry entry = getEntry(key);
208: if (entry == null) {
209: return null;
210: }
211:
212: if (entry.image == null) {
213: entry.image = manager
214: .createImageWithDefault(entry.descriptor);
215: }
216:
217: return entry.image;
218: }
219:
220: /**
221: * Returns the descriptor associated with the given key in this registry,
222: * or <code>null</code> if none.
223: *
224: * @param key the key
225: * @return the descriptor, or <code>null</code> if none
226: * @since 2.1
227: */
228: public ImageDescriptor getDescriptor(String key) {
229: Entry entry = getEntry(key);
230: if (entry == null) {
231: return null;
232: }
233:
234: return entry.descriptor;
235: }
236:
237: /**
238: * Adds (or replaces) an image descriptor to this registry. The first time
239: * this new entry is retrieved, the image descriptor's image will be computed
240: * (via </code>ImageDescriptor.createImage</code>) and remembered.
241: * This method replaces an existing image descriptor associated with the
242: * given key, but fails if there is a real image associated with it.
243: *
244: * @param key the key
245: * @param descriptor the ImageDescriptor
246: * @exception IllegalArgumentException if the key already exists
247: */
248: public void put(String key, ImageDescriptor descriptor) {
249: Entry entry = getEntry(key);
250: if (entry == null) {
251: entry = new Entry();
252: getTable().put(key, entry);
253: }
254:
255: if (entry.image != null) {
256: throw new IllegalArgumentException(
257: "ImageRegistry key already in use: " + key); //$NON-NLS-1$
258: }
259:
260: entry.descriptor = descriptor;
261: }
262:
263: /**
264: * Adds an image to this registry. This method fails if there
265: * is already an image or descriptor for the given key.
266: * <p>
267: * Note that an image registry owns all of the image objects registered
268: * with it, and automatically disposes of them when the SWT Display is disposed.
269: * Because of this, clients must not register an image object
270: * that is managed by another object.
271: * </p>
272: *
273: * @param key the key
274: * @param image the image, should not be <code>null</code>
275: * @exception IllegalArgumentException if the key already exists
276: */
277: public void put(String key, Image image) {
278: Entry entry = getEntry(key);
279:
280: if (entry == null) {
281: entry = new Entry();
282: putEntry(key, entry);
283: }
284:
285: if (entry.image != null || entry.descriptor != null) {
286: throw new IllegalArgumentException(
287: "ImageRegistry key already in use: " + key); //$NON-NLS-1$
288: }
289:
290: // Check for a null image here, otherwise the problem won't appear
291: // until dispose.
292: // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130315
293: Assert.isNotNull(image, "Cannot register a null image."); //$NON-NLS-1$
294: entry.image = image;
295: entry.descriptor = new OriginalImageDescriptor(image, manager
296: .getDevice());
297:
298: try {
299: manager.create(entry.descriptor);
300: } catch (DeviceResourceException e) {
301: }
302: }
303:
304: /**
305: * Removes an image from this registry.
306: * If an SWT image was allocated, it is disposed.
307: * This method has no effect if there is no image or descriptor for the given key.
308: * @param key the key
309: */
310: public void remove(String key) {
311: ImageDescriptor descriptor = getDescriptor(key);
312: if (descriptor != null) {
313: manager.destroy(descriptor);
314: getTable().remove(key);
315: }
316: }
317:
318: private Entry getEntry(String key) {
319: return (Entry) getTable().get(key);
320: }
321:
322: private void putEntry(String key, Entry entry) {
323: getTable().put(key, entry);
324: }
325:
326: private Map getTable() {
327: if (table == null) {
328: table = new HashMap(10);
329: }
330: return table;
331: }
332:
333: /**
334: * Disposes this image registry, disposing any images
335: * that were allocated for it, and clearing its entries.
336: *
337: * @since 3.1
338: */
339: public void dispose() {
340: manager.cancelDisposeExec(disposeRunnable);
341:
342: if (table != null) {
343: for (Iterator i = table.values().iterator(); i.hasNext();) {
344: Entry entry = (Entry) i.next();
345: if (entry.image != null) {
346: manager.destroyImage(entry.descriptor);
347: }
348: }
349: table = null;
350: }
351: display = null;
352: }
353: }
|