org.eclipse.jface.viewers.ContentViewer Maven / Gradle / Ivy
Show all versions of org.eclipse.jface Show documentation
/*******************************************************************************
* Copyright (c) 2000, 2015 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 Spungin - Bug 401439
* Lars Vogel - Bug 475844, 475689
*******************************************************************************/
package org.eclipse.jface.viewers;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.internal.InternalPolicy;
import org.eclipse.jface.util.Policy;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.widgets.Control;
/**
* A content viewer is a model-based adapter on a widget which accesses its
* model by means of a content provider and a label provider.
*
* A viewer's model consists of elements, represented by objects.
* A viewer defines and implements generic infrastructure for handling model
* input, updates, and selections in terms of elements.
* Input is obtained by querying an IContentProvider
which returns
* elements. The elements themselves are not displayed directly. They are
* mapped to labels, containing text and/or an image, using the viewer's
* ILabelProvider
.
*
*
* Implementing a concrete content viewer typically involves the following steps:
*
* -
* create SWT controls for viewer (in constructor) (optional)
*
* -
* initialize SWT controls from input (inputChanged)
*
* -
* define viewer-specific update methods
*
* -
* support selections (
setSelection
, getSelection
)
*
*
*/
public abstract class ContentViewer extends Viewer {
/**
* This viewer's content provider, or null
if none.
*/
private IContentProvider contentProvider = null;
/**
* This viewer's input, or null
if none.
* The viewer's input provides the "model" for the viewer's content.
*/
private Object input = null;
/**
* This viewer's label provider. Initially null
, but
* lazily initialized (to a SimpleLabelProvider
).
*/
private IBaseLabelProvider labelProvider = null;
/**
* This viewer's label provider listener.
* Note: Having a viewer register a label provider listener with
* a label provider avoids having to define public methods
* for internal events.
*/
private final ILabelProviderListener labelProviderListener = new ILabelProviderListener() {
private boolean logWhenDisposed = true; // initially true, set to false
@Override
public void labelProviderChanged(LabelProviderChangedEvent event) {
Control control = getControl();
if (control == null || control.isDisposed()) {
if (logWhenDisposed) {
String message = "Ignored labelProviderChanged notification because control is diposed." + //$NON-NLS-1$
" This indicates a potential memory leak."; //$NON-NLS-1$
if (!InternalPolicy.DEBUG_LOG_LABEL_PROVIDER_NOTIFICATIONS_WHEN_DISPOSED) {
// stop logging after the first
logWhenDisposed = false;
message += " This is only logged once per viewer instance," + //$NON-NLS-1$
" but similar calls will still be ignored."; //$NON-NLS-1$
}
Policy.getLog().log(
new Status(IStatus.WARNING, Policy.JFACE, message,
new RuntimeException()));
}
return;
}
ContentViewer.this.handleLabelProviderChanged(event);
}
};
/**
* Creates a content viewer with no input, no content provider, and a
* default label provider.
*/
protected ContentViewer() {
}
/**
* Returns the content provider used by this viewer,
* or null
if this view does not yet have a content
* provider.
*
* The ContentViewer
implementation of this method returns the content
* provider recorded is an internal state variable.
* Overriding this method is generally not required;
* however, if overriding in a subclass,
* super.getContentProvider
must be invoked.
*
*
* @return the content provider, or null
if none
*/
public IContentProvider getContentProvider() {
return contentProvider;
}
/**
* The ContentViewer
implementation of this IInputProvider
* method returns the current input of this viewer, or null
* if none. The viewer's input provides the "model" for the viewer's
* content.
*/
@Override
public Object getInput() {
return input;
}
/**
* Returns the label provider used by this viewer.
*
* The ContentViewer
implementation of this method returns the label
* provider recorded in an internal state variable; if none has been
* set (with setLabelProvider
) a default label provider
* will be created, remembered, and returned.
* Overriding this method is generally not required;
* however, if overriding in a subclass,
* super.getLabelProvider
must be invoked.
*
*
* @return a label provider
*/
public IBaseLabelProvider getLabelProvider() {
if (labelProvider == null) {
labelProvider = new LabelProvider();
}
return labelProvider;
}
/**
* Handles a dispose event on this viewer's control.
*
* The ContentViewer
implementation of this method disposes of this
* viewer's label provider and content provider (if it has one).
* Subclasses should override this method to perform any additional
* cleanup of resources; however, overriding methods must invoke
* super.handleDispose
.
*
*
* @param event a dispose event
*/
protected void handleDispose(DisposeEvent event) {
if (contentProvider != null) {
try {
contentProvider.inputChanged(this, getInput(), null);
} catch (Exception e) {
// ignore exception
String message = "Exception while calling ContentProvider.inputChanged from ContentViewer.handleDispose"; //$NON-NLS-1$
message += " (" + contentProvider.getClass().getName() + ")"; //$NON-NLS-1$//$NON-NLS-2$
Policy.getLog().log(new Status(IStatus.WARNING, Policy.JFACE, message, e));
}
contentProvider.dispose();
contentProvider = null;
}
if (labelProvider != null) {
labelProvider.removeListener(labelProviderListener);
labelProvider.dispose();
labelProvider = null;
}
input = null;
}
/**
* Handles a label provider changed event.
*
* The ContentViewer
implementation of this method calls labelProviderChanged()
* to cause a complete refresh of the viewer.
* Subclasses may reimplement or extend.
*
* @param event the change event
*/
protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
labelProviderChanged();
}
/**
* Adds event listener hooks to the given control.
*
* All subclasses must call this method when their control is
* first established.
*
*
* The ContentViewer
implementation of this method hooks
* dispose events for the given control.
* Subclasses may override if they need to add other control hooks;
* however, super.hookControl
must be invoked.
*
*
* @param control the control
*/
protected void hookControl(Control control) {
control.addDisposeListener(this::handleDispose);
}
/**
* Notifies that the label provider has changed.
*
* The ContentViewer
implementation of this method calls refresh()
.
* Subclasses may reimplement or extend.
*
*/
protected void labelProviderChanged() {
refresh();
}
/**
* Sets the content provider used by this viewer.
*
* The ContentViewer
implementation of this method records the
* content provider in an internal state variable.
* Overriding this method is generally not required;
* however, if overriding in a subclass,
* super.setContentProvider
must be invoked.
*
*
* @param contentProvider the content provider
* @see #getContentProvider
*/
public void setContentProvider(IContentProvider contentProvider) {
Assert.isNotNull(contentProvider);
IContentProvider oldContentProvider = this.contentProvider;
this.contentProvider = contentProvider;
if (oldContentProvider != null) {
Object currentInput = getInput();
oldContentProvider.inputChanged(this, currentInput, null);
oldContentProvider.dispose();
contentProvider.inputChanged(this, null, currentInput);
refresh();
}
}
/**
* The ContentViewer
implementation of this Viewer
* method invokes inputChanged
on the content provider and then the
* inputChanged
hook method. This method fails if this viewer does
* not have a content provider. Subclassers are advised to override
* inputChanged
rather than this method, but may extend this method
* if required.
*/
@Override
public void setInput(Object input) {
Control control = getControl();
if (control == null || control.isDisposed()) {
throw new IllegalStateException(
"Need an underlying widget to be able to set the input." + //$NON-NLS-1$
"(Has the widget been disposed?)"); //$NON-NLS-1$
}
Assert.isTrue(getContentProvider() != null,
"Instances of ContentViewer must have a content provider assigned before the setInput method is called."); //$NON-NLS-1$
Object oldInput = getInput();
contentProvider.inputChanged(this, oldInput, input);
this.input = input;
// call input hook
inputChanged(this.input, oldInput);
}
/**
* Sets the label provider for this viewer.
*
* The ContentViewer
implementation of this method ensures that the
* given label provider is connected to this viewer and the
* former label provider is disconnected from this viewer.
* Overriding this method is generally not required;
* however, if overriding in a subclass,
* super.setLabelProvider
must be invoked.
*
*
* @param labelProvider the label provider, or null
if none
*/
public void setLabelProvider(IBaseLabelProvider labelProvider) {
IBaseLabelProvider oldProvider = this.labelProvider;
// If it hasn't changed, do nothing.
// This also ensures that the provider is not disposed
// if set a second time.
if (labelProvider == oldProvider) {
return;
}
if (oldProvider != null) {
oldProvider.removeListener(this.labelProviderListener);
}
this.labelProvider = labelProvider;
if (labelProvider != null) {
labelProvider.addListener(this.labelProviderListener);
}
refresh();
// Dispose old provider after refresh, so that items never refer to stale images.
if (oldProvider != null) {
internalDisposeLabelProvider(oldProvider);
}
}
/**
* @param oldProvider
*
* @since 3.4
*/
void internalDisposeLabelProvider(IBaseLabelProvider oldProvider) {
oldProvider.dispose();
}
}