org.eclipse.jface.resource.ImageRegistry Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Steven Ketcham ([email protected]) - 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.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.pde.api.tools.annotations.NoExtend;
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.
*
* 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.
*
*
* Clients may instantiate this class (it was not designed to be subclassed).
*
*
* 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
*
*/
@NoExtend
public class ImageRegistry {
/**
* display used when getting images
*/
private Display display;
private ResourceManager manager;
private Map table;
private Runnable disposeRunnable = this::dispose;
/**
* Contains the data for an entry in the registry.
*/
private static class Entry {
/** the image */
protected Image image;
/** the descriptor */
protected ImageDescriptor descriptor;
}
private static class OriginalImageDescriptor extends ImageDescriptor {
private Image original;
private int refCount = 0;
private Device originalDisplay;
/**
* @param original the original image
* @param originalDisplay the device the image is part of
*/
public OriginalImageDescriptor(Image original, Device originalDisplay) {
this.original = original;
this.originalDisplay = originalDisplay;
}
@Override
public Object createResource(Device device) throws DeviceResourceException {
if (device == originalDisplay) {
refCount++;
return original;
}
return super.createResource(device);
}
@Override
public void destroyResource(Object toDispose) {
if (original == toDispose) {
refCount--;
if (refCount == 0) {
original.dispose();
original = null;
}
} else {
super.destroyResource(toDispose);
}
}
@Override
public ImageData getImageData(int zoom) {
return original.getImageData(zoom);
}
}
/**
* Creates an empty image registry.
*
* There must be an SWT Display created in the current
* thread before calling this method.
*
*/
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 Display
must not be
* null
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 null
if none.
*
* @param key the key
* @return the image, or null
if none
*/
@SuppressWarnings({ "removal", "deprecation" })
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(() -> 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 null
if none.
*
* @param key the key
* @return the descriptor, or null
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
* ImageDescriptor.createImage
) 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.
*
* 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.
*
*
* @param key the key
* @param image the image, should not be null
* @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$
}
// Check for a null image here, otherwise the problem won't appear
// until dispose.
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130315
Assert.isNotNull(image, "Cannot register a null image."); //$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 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 (Entry entry : table.values()) {
if (entry.image != null) {
manager.destroy(entry.descriptor);
}
}
table = null;
}
display = null;
}
}