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: *******************************************************************************/package org.eclipse.jface.viewers;
011:
012: import org.eclipse.core.runtime.Assert;
013: import org.eclipse.swt.events.DisposeEvent;
014: import org.eclipse.swt.events.DisposeListener;
015: import org.eclipse.swt.widgets.Control;
016:
017: /**
018: * A content viewer is a model-based adapter on a widget which accesses its
019: * model by means of a content provider and a label provider.
020: * <p>
021: * A viewer's model consists of elements, represented by objects.
022: * A viewer defines and implements generic infrastructure for handling model
023: * input, updates, and selections in terms of elements.
024: * Input is obtained by querying an <code>IContentProvider</code> which returns
025: * elements. The elements themselves are not displayed directly. They are
026: * mapped to labels, containing text and/or an image, using the viewer's
027: * <code>ILabelProvider</code>.
028: * </p>
029: * <p>
030: * Implementing a concrete content viewer typically involves the following steps:
031: * <ul>
032: * <li>
033: * create SWT controls for viewer (in constructor) (optional)
034: * </li>
035: * <li>
036: * initialize SWT controls from input (inputChanged)
037: * </li>
038: * <li>
039: * define viewer-specific update methods
040: * </li>
041: * <li>
042: * support selections (<code>setSelection</code>, <code>getSelection</code>)
043: * </ul>
044: * </p>
045: */
046: public abstract class ContentViewer extends Viewer {
047:
048: /**
049: * This viewer's content provider, or <code>null</code> if none.
050: */
051: private IContentProvider contentProvider = null;
052:
053: /**
054: * This viewer's input, or <code>null</code> if none.
055: * The viewer's input provides the "model" for the viewer's content.
056: */
057: private Object input = null;
058:
059: /**
060: * This viewer's label provider. Initially <code>null</code>, but
061: * lazily initialized (to a <code>SimpleLabelProvider</code>).
062: */
063: private IBaseLabelProvider labelProvider = null;
064:
065: /**
066: * This viewer's label provider listener.
067: * Note: Having a viewer register a label provider listener with
068: * a label provider avoids having to define public methods
069: * for internal events.
070: */
071: private final ILabelProviderListener labelProviderListener = new ILabelProviderListener() {
072: public void labelProviderChanged(LabelProviderChangedEvent event) {
073: ContentViewer.this .handleLabelProviderChanged(event);
074: }
075: };
076:
077: /**
078: * Creates a content viewer with no input, no content provider, and a
079: * default label provider.
080: */
081: protected ContentViewer() {
082: }
083:
084: /**
085: * Returns the content provider used by this viewer,
086: * or <code>null</code> if this view does not yet have a content
087: * provider.
088: * <p>
089: * The <code>ContentViewer</code> implementation of this method returns the content
090: * provider recorded is an internal state variable.
091: * Overriding this method is generally not required;
092: * however, if overriding in a subclass,
093: * <code>super.getContentProvider</code> must be invoked.
094: * </p>
095: *
096: * @return the content provider, or <code>null</code> if none
097: */
098: public IContentProvider getContentProvider() {
099: return contentProvider;
100: }
101:
102: /**
103: * The <code>ContentViewer</code> implementation of this <code>IInputProvider</code>
104: * method returns the current input of this viewer, or <code>null</code>
105: * if none. The viewer's input provides the "model" for the viewer's
106: * content.
107: */
108: public Object getInput() {
109: return input;
110: }
111:
112: /**
113: * Returns the label provider used by this viewer.
114: * <p>
115: * The <code>ContentViewer</code> implementation of this method returns the label
116: * provider recorded in an internal state variable; if none has been
117: * set (with <code>setLabelProvider</code>) a default label provider
118: * will be created, remembered, and returned.
119: * Overriding this method is generally not required;
120: * however, if overriding in a subclass,
121: * <code>super.getLabelProvider</code> must be invoked.
122: * </p>
123: *
124: * @return a label provider
125: */
126: public IBaseLabelProvider getLabelProvider() {
127: if (labelProvider == null) {
128: labelProvider = new LabelProvider();
129: }
130: return labelProvider;
131: }
132:
133: /**
134: * Handles a dispose event on this viewer's control.
135: * <p>
136: * The <code>ContentViewer</code> implementation of this method disposes of this
137: * viewer's label provider and content provider (if it has one).
138: * Subclasses should override this method to perform any additional
139: * cleanup of resources; however, overriding methods must invoke
140: * <code>super.handleDispose</code>.
141: * </p>
142: *
143: * @param event a dispose event
144: */
145: protected void handleDispose(DisposeEvent event) {
146: if (contentProvider != null) {
147: contentProvider.inputChanged(this , getInput(), null);
148: contentProvider.dispose();
149: contentProvider = null;
150: }
151: if (labelProvider != null) {
152: labelProvider.removeListener(labelProviderListener);
153: labelProvider.dispose();
154: labelProvider = null;
155: }
156: input = null;
157: }
158:
159: /**
160: * Handles a label provider changed event.
161: * <p>
162: * The <code>ContentViewer</code> implementation of this method calls <code>labelProviderChanged()</code>
163: * to cause a complete refresh of the viewer.
164: * Subclasses may reimplement or extend.
165: * </p>
166: * @param event the change event
167: */
168: protected void handleLabelProviderChanged(
169: LabelProviderChangedEvent event) {
170: labelProviderChanged();
171: }
172:
173: /**
174: * Adds event listener hooks to the given control.
175: * <p>
176: * All subclasses must call this method when their control is
177: * first established.
178: * </p>
179: * <p>
180: * The <code>ContentViewer</code> implementation of this method hooks
181: * dispose events for the given control.
182: * Subclasses may override if they need to add other control hooks;
183: * however, <code>super.hookControl</code> must be invoked.
184: * </p>
185: *
186: * @param control the control
187: */
188: protected void hookControl(Control control) {
189: control.addDisposeListener(new DisposeListener() {
190: public void widgetDisposed(DisposeEvent event) {
191: handleDispose(event);
192: }
193: });
194: }
195:
196: /**
197: * Notifies that the label provider has changed.
198: * <p>
199: * The <code>ContentViewer</code> implementation of this method calls <code>refresh()</code>.
200: * Subclasses may reimplement or extend.
201: * </p>
202: */
203: protected void labelProviderChanged() {
204: refresh();
205: }
206:
207: /**
208: * Sets the content provider used by this viewer.
209: * <p>
210: * The <code>ContentViewer</code> implementation of this method records the
211: * content provider in an internal state variable.
212: * Overriding this method is generally not required;
213: * however, if overriding in a subclass,
214: * <code>super.setContentProvider</code> must be invoked.
215: * </p>
216: *
217: * @param contentProvider the content provider
218: * @see #getContentProvider
219: */
220: public void setContentProvider(IContentProvider contentProvider) {
221: Assert.isNotNull(contentProvider);
222: IContentProvider oldContentProvider = this .contentProvider;
223: this .contentProvider = contentProvider;
224: if (oldContentProvider != null) {
225: Object currentInput = getInput();
226: oldContentProvider.inputChanged(this , currentInput, null);
227: oldContentProvider.dispose();
228: contentProvider.inputChanged(this , null, currentInput);
229: refresh();
230: }
231: }
232:
233: /**
234: * The <code>ContentViewer</code> implementation of this <code>Viewer</code>
235: * method invokes <code>inputChanged</code> on the content provider and then the
236: * <code>inputChanged</code> hook method. This method fails if this viewer does
237: * not have a content provider. Subclassers are advised to override
238: * <code>inputChanged</code> rather than this method, but may extend this method
239: * if required.
240: */
241: public void setInput(Object input) {
242: Assert
243: .isTrue(getContentProvider() != null,
244: "ContentViewer must have a content provider when input is set."); //$NON-NLS-1$
245:
246: Object oldInput = getInput();
247: contentProvider.inputChanged(this , oldInput, input);
248: this .input = input;
249:
250: // call input hook
251: inputChanged(this .input, oldInput);
252: }
253:
254: /**
255: * Sets the label provider for this viewer.
256: * <p>
257: * The <code>ContentViewer</code> implementation of this method ensures that the
258: * given label provider is connected to this viewer and the
259: * former label provider is disconnected from this viewer.
260: * Overriding this method is generally not required;
261: * however, if overriding in a subclass,
262: * <code>super.setLabelProvider</code> must be invoked.
263: * </p>
264: *
265: * @param labelProvider the label provider, or <code>null</code> if none
266: */
267: public void setLabelProvider(IBaseLabelProvider labelProvider) {
268: IBaseLabelProvider oldProvider = this .labelProvider;
269: // If it hasn't changed, do nothing.
270: // This also ensures that the provider is not disposed
271: // if set a second time.
272: if (labelProvider == oldProvider) {
273: return;
274: }
275: if (oldProvider != null) {
276: oldProvider.removeListener(this .labelProviderListener);
277: }
278: this .labelProvider = labelProvider;
279: if (labelProvider != null) {
280: labelProvider.addListener(this .labelProviderListener);
281: }
282: refresh();
283:
284: // Dispose old provider after refresh, so that items never refer to stale images.
285: if (oldProvider != null) {
286: oldProvider.dispose();
287: }
288: }
289: }
|