Source code: org/eclipse/jface/viewers/ContentViewer.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jface.viewers;
12
13 import org.eclipse.jface.util.Assert;
14 import org.eclipse.swt.events.DisposeEvent;
15 import org.eclipse.swt.events.DisposeListener;
16 import org.eclipse.swt.widgets.Control;
17
18 /**
19 * A content viewer is a model-based adapter on a widget which accesses its
20 * model by means of a content provider and a label provider.
21 * <p>
22 * A viewer's model consists of elements, represented by objects.
23 * A viewer defines and implements generic infrastructure for handling model
24 * input, updates, and selections in terms of elements.
25 * Input is obtained by querying an <code>IContentProvider</code> which returns
26 * elements. The elements themselves are not displayed directly. They are
27 * mapped to labels, containing text and/or an image, using the viewer's
28 * <code>ILabelProvider</code>.
29 * </p>
30 * <p>
31 * Implementing a concrete content viewer typically involves the following steps:
32 * <ul>
33 * <li>
34 * create SWT controls for viewer (in constructor) (optional)
35 * </li>
36 * <li>
37 * initialize SWT controls from input (inputChanged)
38 * </li>
39 * <li>
40 * define viewer-specific update methods
41 * </li>
42 * <li>
43 * support selections (<code>setSelection</code>, <code>getSelection</code>)
44 * </ul>
45 * </p>
46 */
47 public abstract class ContentViewer extends Viewer {
48
49 /**
50 * This viewer's content provider, or <code>null</code> if none.
51 */
52 private IContentProvider contentProvider = null;
53
54 /**
55 * This viewer's input, or <code>null</code> if none.
56 * The viewer's input provides the "model" for the viewer's content.
57 */
58 private Object input = null;
59
60 /**
61 * This viewer's label provider. Initially <code>null</code>, but
62 * lazily initialized (to a <code>SimpleLabelProvider</code>).
63 */
64 private IBaseLabelProvider labelProvider = null;
65
66 /**
67 * This viewer's label provider listener.
68 * Note: Having a viewer register a label provider listener with
69 * a label provider avoids having to define public methods
70 * for internal events.
71 */
72 private final ILabelProviderListener labelProviderListener = new ILabelProviderListener() {
73 public void labelProviderChanged(LabelProviderChangedEvent event) {
74 ContentViewer.this.handleLabelProviderChanged(event);
75 }
76 };
77 /**
78 * Creates a content viewer with no input, no content provider, and a
79 * default label provider.
80 */
81 protected ContentViewer() {
82 }
83 /**
84 * Returns the content provider used by this viewer,
85 * or <code>null</code> if this view does not yet have a content
86 * provider.
87 * <p>
88 * The <code>ContentViewer</code> implementation of this method returns the content
89 * provider recorded is an internal state variable.
90 * Overriding this method is generally not required;
91 * however, if overriding in a subclass,
92 * <code>super.getContentProvider</code> must be invoked.
93 * </p>
94 *
95 * @return the content provider, or <code>null</code> if none
96 */
97 public IContentProvider getContentProvider() {
98 return contentProvider;
99 }
100 /**
101 * The <code>ContentViewer</code> implementation of this <code>IInputProvider</code>
102 * method returns the current input of this viewer, or <code>null</code>
103 * if none. The viewer's input provides the "model" for the viewer's
104 * content.
105 */
106 public Object getInput() {
107 return input;
108 }
109 /**
110 * Returns the label provider used by this viewer.
111 * <p>
112 * The <code>ContentViewer</code> implementation of this method returns the label
113 * provider recorded in an internal state variable; if none has been
114 * set (with <code>setLabelProvider</code>) a <code>SimpleLabelProvider</code>
115 * will be created, remembered, and returned.
116 * Overriding this method is generally not required;
117 * however, if overriding in a subclass,
118 * <code>super.getLabelProvider</code> must be invoked.
119 * </p>
120 *
121 * @return a label provider
122 */
123 public IBaseLabelProvider getLabelProvider() {
124 if (labelProvider == null)
125 labelProvider = new LabelProvider();
126 return labelProvider;
127 }
128 /**
129 * Handles a dispose event on this viewer's control.
130 * <p>
131 * The <code>ContentViewer</code> implementation of this method disposes of this
132 * viewer's label provider and content provider (if it has one).
133 * Subclasses should override this method to perform any additional
134 * cleanup of resources; however, overriding methods must invoke
135 * <code>super.handleDispose</code>.
136 * </p>
137 *
138 * @param event a dispose event
139 */
140 protected void handleDispose(DisposeEvent event) {
141 if (contentProvider != null) {
142 contentProvider.inputChanged(this, getInput(), null);
143 contentProvider.dispose();
144 contentProvider = null;
145 }
146 if (labelProvider != null) {
147 labelProvider.removeListener(labelProviderListener);
148 labelProvider.dispose();
149 labelProvider = null;
150 }
151 }
152 /**
153 * Handles a label provider changed event.
154 * <p>
155 * The <code>ContentViewer</code> implementation of this method calls <code>labelProviderChanged()</code>
156 * to cause a complete refresh of the viewer.
157 * Subclasses may reimplement or extend.
158 * </p>
159 */
160 protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
161 labelProviderChanged();
162 }
163 /**
164 * Adds event listener hooks to the given control.
165 * <p>
166 * All subclasses must call this method when their control is
167 * first established.
168 * </p>
169 * <p>
170 * The <code>ContentViewer</code> implementation of this method hooks
171 * dispose events for the given control.
172 * Subclasses may override if they need to add other control hooks;
173 * however, <code>super.hookControl</code> must be invoked.
174 * </p>
175 *
176 * @param control the control
177 */
178 protected void hookControl(Control control) {
179 control.addDisposeListener(new DisposeListener() {
180 public void widgetDisposed(DisposeEvent event) {
181 handleDispose(event);
182 }
183 });
184 }
185 /**
186 * Notifies that the label provider has changed.
187 * <p>
188 * The <code>ContentViewer</code> implementation of this method calls <code>refresh()</code>.
189 * Subclasses may reimplement or extend.
190 * </p>
191 */
192 protected void labelProviderChanged() {
193 refresh();
194 }
195 /**
196 * Sets the content provider used by this viewer.
197 * <p>
198 * The <code>ContentViewer</code> implementation of this method records the
199 * content provider in an internal state variable.
200 * Overriding this method is generally not required;
201 * however, if overriding in a subclass,
202 * <code>super.setContentProvider</code> must be invoked.
203 * </p>
204 *
205 * @param contentProvider the content provider
206 * @see #getContentProvider
207 */
208 public void setContentProvider(IContentProvider contentProvider) {
209 Assert.isNotNull(contentProvider);
210 IContentProvider oldContentProvider = this.contentProvider;
211 this.contentProvider = contentProvider;
212 if (oldContentProvider != null) {
213 Object currentInput = getInput();
214 oldContentProvider.inputChanged(this, currentInput, null);
215 oldContentProvider.dispose();
216 contentProvider.inputChanged(this, null, currentInput);
217 refresh();
218 }
219 }
220 /**
221 * The <code>ContentViewer</code> implementation of this <code>Viewer</code>
222 * method invokes <code>inputChanged</code> on the content provider and then the
223 * <code>inputChanged</code> hook method. This method fails if this viewer does
224 * not have a content provider. Subclassers are advised to override
225 * <code>inputChanged</code> rather than this method, but may extend this method
226 * if required.
227 */
228 public void setInput(Object input) {
229 Assert.isTrue(
230 getContentProvider() != null,
231 "ContentViewer must have a content provider when input is set."); //$NON-NLS-1$
232
233 Object oldInput = getInput();
234 contentProvider.inputChanged(this, oldInput, input);
235 this.input = input;
236
237 // call input hook
238 inputChanged(this.input, oldInput);
239 }
240 /**
241 * Sets the label provider for this viewer.
242 * <p>
243 * The <code>ContentViewer</code> implementation of this method ensures that the
244 * given label provider is connected to this viewer and the
245 * former label provider is disconnected from this viewer.
246 * Overriding this method is generally not required;
247 * however, if overriding in a subclass,
248 * <code>super.setLabelProvider</code> must be invoked.
249 * </p>
250 *
251 * @param labelProvider the label provider, or <code>null</code> if none
252 */
253 public void setLabelProvider(IBaseLabelProvider labelProvider) {
254 IBaseLabelProvider oldProvider = this.labelProvider;
255 // If it hasn't changed, do nothing.
256 // This also ensures that the provider is not disposed
257 // if set a second time.
258 if (labelProvider == oldProvider) {
259 return;
260 }
261 if (oldProvider != null) {
262 oldProvider.removeListener(this.labelProviderListener);
263 }
264 this.labelProvider = labelProvider;
265 if (labelProvider != null) {
266 labelProvider.addListener(this.labelProviderListener);
267 }
268 refresh();
269
270 // Dispose old provider after refresh, so that items never refer to stale images.
271 if (oldProvider != null) {
272 oldProvider.dispose();
273 }
274 }
275 }