blob: e279e2635892eb2227eb00cb4e68cf42a0c4e5b6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Steven Ketcham (sketcham@dsicdi.com) - Bug 42451
* [Dialogs] ImageRegistry throws null pointer exception in
* application with multiple Display's
*******************************************************************************/
package org.eclipse.jface.resource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.util.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Display;
/**
* An image registry maintains a mapping between symbolic image names
* and SWT image objects or special image descriptor objects which
* defer the creation of SWT image objects until they are needed.
* <p>
* An image registry owns all of the image objects registered
* with it, and automatically disposes of them when the SWT Display
* that creates the images is disposed. Because of this, clients do not
* need to (indeed, must not attempt to) dispose of these images themselves.
* </p>
* <p>
* Clients may instantiate this class (it was not designed to be subclassed).
* </p>
* <p>
* Unlike the FontRegistry, it is an error to replace images. As a result
* there are no events that fire when values are changed in the registry
* </p>
*/
public class ImageRegistry {
/**
* display used when getting images
*/
private Display display;
private ResourceManager manager;
private Map table;
private Runnable disposeRunnable = new Runnable() {
public void run() {
dispose();
}
};
/**
* Contains the data for an entry in the registry.
*/
private static class Entry {
protected Image image;
protected ImageDescriptor descriptor;
}
private static class OriginalImageDescriptor extends ImageDescriptor {
private Image original;
private int refCount = 0;
private Device originalDisplay;
public OriginalImageDescriptor(Image original, Device originalDisplay) {
this.original = original;
this.originalDisplay = originalDisplay;
}
public Object createResource(Device device) throws DeviceResourceException {
if (device == originalDisplay) {
refCount++;
return original;
}
return super.createResource(device);
}
public void destroyResource(Object toDispose) {
if (original == toDispose) {
refCount--;
if (refCount == 0) {
original.dispose();
original = null;
}
} else {
super.destroyResource(toDispose);
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.resource.ImageDescriptor#getImageData()
*/
public ImageData getImageData() {
return original.getImageData();
}
}
/**
* Creates an empty image registry.
* <p>
* There must be an SWT Display created in the current
* thread before calling this method.
* </p>
*/
public ImageRegistry() {
this(Display.getCurrent());
}
/**
* Creates an empty image registry using the given resource manager to allocate images.
*
* @param manager the resource manager used to allocate images
*
* @since 3.1
*/
public ImageRegistry(ResourceManager manager) {
Assert.isNotNull(manager);
Device dev = manager.getDevice();
if (dev instanceof Display) {
this.display = (Display)dev;
}
this.manager = manager;
manager.disposeExec(disposeRunnable);
}
/**
* Creates an empty image registry.
*
* @param display this <code>Display</code> must not be
* <code>null</code> and must not be disposed in order
* to use this registry
*/
public ImageRegistry(Display display) {
this(JFaceResources.getResources(display));
}
/**
* Returns the image associated with the given key in this registry,
* or <code>null</code> if none.
*
* @param key the key
* @return the image, or <code>null</code> if none
*/
public Image get(String key) {
// can be null
if (key == null) {
return null;
}
if (display != null) {
/**
* NOTE, for backwards compatibility the following images are supported
* here, they should never be disposed, hence we explicitly return them
* rather then registering images that SWT will dispose.
*
* Applications should go direclty to SWT for these icons.
*
* @see Display.getSystemIcon(int ID)
*/
int swtKey = -1;
if (key.equals(Dialog.DLG_IMG_INFO)) {
swtKey = SWT.ICON_INFORMATION;
}
if (key.equals(Dialog.DLG_IMG_QUESTION)) {
swtKey = SWT.ICON_QUESTION;
}
if (key.equals(Dialog.DLG_IMG_WARNING)) {
swtKey = SWT.ICON_WARNING;
}
if (key.equals(Dialog.DLG_IMG_ERROR)) {
swtKey = SWT.ICON_ERROR;
}
// if we actually just want to return an SWT image do so without
// looking in the registry
if (swtKey != -1) {
final Image[] image = new Image[1];
final int id = swtKey;
display.syncExec(new Runnable() {
public void run() {
image[0] = display.getSystemImage(id);
}
});
return image[0];
}
}
Entry entry = getEntry(key);
if (entry == null) {
return null;
}
if (entry.image == null) {
entry.image = manager.createImageWithDefault(entry.descriptor);
}
return entry.image;
}
/**
* Returns the descriptor associated with the given key in this registry,
* or <code>null</code> if none.
*
* @param key the key
* @return the descriptor, or <code>null</code> if none
* @since 2.1
*/
public ImageDescriptor getDescriptor(String key) {
Entry entry = getEntry(key);
if (entry == null) {
return null;
}
return entry.descriptor;
}
/**
* Adds (or replaces) an image descriptor to this registry. The first time
* this new entry is retrieved, the image descriptor's image will be computed
* (via </code>ImageDescriptor.createImage</code>) and remembered.
* This method replaces an existing image descriptor associated with the
* given key, but fails if there is a real image associated with it.
*
* @param key the key
* @param descriptor the ImageDescriptor
* @exception IllegalArgumentException if the key already exists
*/
public void put(String key, ImageDescriptor descriptor) {
Entry entry = getEntry(key);
if (entry == null) {
entry = new Entry();
getTable().put(key, entry);
}
if (entry.image != null) {
throw new IllegalArgumentException(
"ImageRegistry key already in use: " + key); //$NON-NLS-1$
}
entry.descriptor = descriptor;
}
/**
* Adds an image to this registry. This method fails if there
* is already an image or descriptor for the given key.
* <p>
* Note that an image registry owns all of the image objects registered
* with it, and automatically disposes of them when the SWT Display is disposed.
* Because of this, clients must not register an image object
* that is managed by another object.
* </p>
*
* @param key the key
* @param image the image
* @exception IllegalArgumentException if the key already exists
*/
public void put(String key, Image image) {
Entry entry = getEntry(key);
if (entry == null) {
entry = new Entry();
putEntry(key, entry);
}
if (entry.image != null || entry.descriptor != null) {
throw new IllegalArgumentException(
"ImageRegistry key already in use: " + key); //$NON-NLS-1$
}
entry.image = image;
entry.descriptor = new OriginalImageDescriptor(image, manager.getDevice());
try {
manager.create(entry.descriptor);
} catch (DeviceResourceException e) {
}
}
/**
* Removes an image from this registry.
* If an SWT image was allocated, it is disposed.
* This method has no effect if there is no image or descriptor for the given key.
* @param key the key
*/
public void remove(String key) {
ImageDescriptor descriptor = getDescriptor(key);
if (descriptor != null) {
manager.destroy(descriptor);
getTable().remove(key);
}
}
private Entry getEntry(String key) {
return (Entry) getTable().get(key);
}
private void putEntry(String key, Entry entry) {
getTable().put(key, entry);
}
private Map getTable() {
if (table == null) {
table = new HashMap(10);
}
return table;
}
/**
* Disposes this image registry, disposing any images
* that were allocated for it, and clearing its entries.
*
* @since 3.1
*/
public void dispose() {
manager.cancelDisposeExec(disposeRunnable);
if (table != null) {
for (Iterator i = table.values().iterator(); i.hasNext();) {
Entry entry = (Entry) i.next();
if (entry.image != null) {
manager.destroyImage(entry.descriptor);
}
}
table = null;
}
display = null;
}
}