001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.ListenerList;
013: import org.eclipse.core.runtime.Assert;
014: import org.eclipse.jface.util.SafeRunnable;
015: import org.eclipse.swt.events.HelpEvent;
016: import org.eclipse.swt.events.HelpListener;
017: import org.eclipse.swt.widgets.Control;
018: import org.eclipse.swt.widgets.Item;
019:
020: /**
021: * A viewer is a model-based adapter on a widget.
022: * <p>
023: * A viewer can be created as an adapter on a pre-existing control (e.g.,
024: * creating a <code>ListViewer</code> on an existing <code>List</code> control).
025: * All viewers also provide a convenience constructor for creating the control.
026: * </p>
027: * <p>
028: * Implementing a concrete viewer typically involves the following steps:
029: * <ul>
030: * <li>
031: * create SWT controls for viewer (in constructor) (optional)
032: * </li>
033: * <li>
034: * initialize SWT controls from input (inputChanged)
035: * </li>
036: * <li>
037: * define viewer-specific update methods
038: * </li>
039: * <li>
040: * support selections (<code>setSelection</code>, <code>getSelection</code>)
041: * </li>
042: * </ul>
043: * </p>
044: */
045: public abstract class Viewer implements IInputSelectionProvider {
046:
047: /**
048: * List of selection change listeners (element type: <code>ISelectionChangedListener</code>).
049: *
050: * @see #fireSelectionChanged
051: */
052: private ListenerList selectionChangedListeners = new ListenerList();
053:
054: /**
055: * List of help request listeners (element type: <code>org.eclipse.swt.events.HelpListener</code>).
056: * Help request listeners.
057: *
058: * @see #handleHelpRequest
059: */
060: private ListenerList helpListeners = new ListenerList();
061:
062: /**
063: * The names of this viewer's properties.
064: * <code>null</code> if this viewer has no properties.
065: *
066: * @see #setData
067: */
068: private String[] keys;
069:
070: /**
071: * The values of this viewer's properties.
072: * <code>null</code> if this viewer has no properties.
073: * This array parallels the value of the <code>keys</code> field.
074: *
075: * @see #setData
076: */
077: private Object[] values;
078:
079: /**
080: * Remembers whether we've hooked the help listener on the control or not.
081: */
082: private boolean helpHooked = false;
083:
084: /**
085: * Help listener for the control, created lazily when client's first help listener is added.
086: */
087: private HelpListener helpListener = null;
088:
089: /**
090: * Unique key for associating element data with widgets.
091: * @see org.eclipse.swt.widgets.Widget#setData(String, Object)
092: */
093: protected static final String WIDGET_DATA_KEY = "org.eclipse.jface.viewers.WIDGET_DATA";//$NON-NLS-1$
094:
095: /**
096: * Creates a new viewer.
097: */
098: protected Viewer() {
099: }
100:
101: /**
102: * Adds a listener for help requests in this viewer.
103: * Has no effect if an identical listener is already registered.
104: *
105: * @param listener a help listener
106: */
107: public void addHelpListener(HelpListener listener) {
108: helpListeners.add(listener);
109: if (!helpHooked) {
110: Control control = getControl();
111: if (control != null && !control.isDisposed()) {
112: if (this .helpListener == null) {
113: this .helpListener = new HelpListener() {
114: public void helpRequested(HelpEvent event) {
115: handleHelpRequest(event);
116: }
117: };
118: }
119: control.addHelpListener(this .helpListener);
120: helpHooked = true;
121: }
122: }
123: }
124:
125: /* (non-Javadoc)
126: * Method declared on ISelectionProvider.
127: */
128: public void addSelectionChangedListener(
129: ISelectionChangedListener listener) {
130: selectionChangedListeners.add(listener);
131: }
132:
133: /**
134: * Notifies any help listeners that help has been requested.
135: * Only listeners registered at the time this method is called are notified.
136: *
137: * @param event a help event
138: *
139: * @see HelpListener#helpRequested(org.eclipse.swt.events.HelpEvent)
140: */
141: protected void fireHelpRequested(HelpEvent event) {
142: Object[] listeners = helpListeners.getListeners();
143: for (int i = 0; i < listeners.length; ++i) {
144: ((HelpListener) listeners[i]).helpRequested(event);
145: }
146: }
147:
148: /**
149: * Notifies any selection changed listeners that the viewer's selection has changed.
150: * Only listeners registered at the time this method is called are notified.
151: *
152: * @param event a selection changed event
153: *
154: * @see ISelectionChangedListener#selectionChanged
155: */
156: protected void fireSelectionChanged(
157: final SelectionChangedEvent event) {
158: Object[] listeners = selectionChangedListeners.getListeners();
159: for (int i = 0; i < listeners.length; ++i) {
160: final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
161: SafeRunnable.run(new SafeRunnable() {
162: public void run() {
163: l.selectionChanged(event);
164: }
165: });
166: }
167: }
168:
169: /**
170: * Returns the primary control associated with this viewer.
171: *
172: * @return the SWT control which displays this viewer's content
173: */
174: public abstract Control getControl();
175:
176: /**
177: * Returns the value of the property with the given name,
178: * or <code>null</code> if the property is not found.
179: * <p>
180: * The default implementation performs a (linear) search of
181: * an internal table. Overriding this method is generally not
182: * required if the number of different keys is small. If a more
183: * efficient representation of a viewer's properties is required,
184: * override both <code>getData</code> and <code>setData</code>.
185: * </p>
186: *
187: * @param key the property name
188: * @return the property value, or <code>null</code> if
189: * the property is not found
190: */
191: public Object getData(String key) {
192: Assert.isNotNull(key);
193: if (keys == null) {
194: return null;
195: }
196: for (int i = 0; i < keys.length; i++) {
197: if (keys[i].equals(key)) {
198: return values[i];
199: }
200: }
201: return null;
202: }
203:
204: /* (non-Javadoc)
205: * Copy-down of method declared on <code>IInputProvider</code>.
206: */
207: public abstract Object getInput();
208:
209: /* (non-Javadoc)
210: * Copy-down of method declared on <code>ISelectionProvider</code>.
211: */
212: public abstract ISelection getSelection();
213:
214: /**
215: * Handles a help request from the underlying SWT control.
216: * The default behavior is to fire a help request,
217: * with the event's data modified to hold this viewer.
218: * @param event the event
219: *
220: */
221: protected void handleHelpRequest(HelpEvent event) {
222: Object oldData = event.data;
223: event.data = this ;
224: fireHelpRequested(event);
225: event.data = oldData;
226: }
227:
228: /**
229: * Internal hook method called when the input to this viewer is
230: * initially set or subsequently changed.
231: * <p>
232: * The default implementation does nothing. Subclassers may override
233: * this method to do something when a viewer's input is set.
234: * A typical use is populate the viewer.
235: * </p>
236: *
237: * @param input the new input of this viewer, or <code>null</code> if none
238: * @param oldInput the old input element or <code>null</code> if there
239: * was previously no input
240: */
241: protected void inputChanged(Object input, Object oldInput) {
242: }
243:
244: /**
245: * Refreshes this viewer completely with information freshly obtained from this
246: * viewer's model.
247: */
248: public abstract void refresh();
249:
250: /**
251: * Removes the given help listener from this viewer.
252: * Has no affect if an identical listener is not registered.
253: *
254: * @param listener a help listener
255: */
256: public void removeHelpListener(HelpListener listener) {
257: helpListeners.remove(listener);
258: if (helpListeners.size() == 0) {
259: Control control = getControl();
260: if (control != null && !control.isDisposed()) {
261: control.removeHelpListener(this .helpListener);
262: helpHooked = false;
263: }
264: }
265: }
266:
267: /* (non-Javadoc)
268: * Method declared on ISelectionProvider.
269: */
270: public void removeSelectionChangedListener(
271: ISelectionChangedListener listener) {
272: selectionChangedListeners.remove(listener);
273: }
274:
275: /**
276: * Scrolls the viewer's control down by one item from the given
277: * display-relative coordinates. Returns the newly revealed Item,
278: * or <code>null</code> if no scrolling occurred or if the viewer
279: * doesn't represent an item-based widget.
280: *
281: * @param x horizontal coordinate
282: * @param y vertical coordinate
283: * @return the item scrolled down to
284: */
285: public Item scrollDown(int x, int y) {
286: return null;
287: }
288:
289: /**
290: * Scrolls the viewer's control up by one item from the given
291: * display-relative coordinates. Returns the newly revealed Item,
292: * or <code>null</code> if no scrolling occurred or if the viewer
293: * doesn't represent an item-based widget.
294: *
295: * @param x horizontal coordinate
296: * @param y vertical coordinate
297: * @return the item scrolled up to
298: */
299: public Item scrollUp(int x, int y) {
300: return null;
301: }
302:
303: /**
304: * Sets the value of the property with the given name to the
305: * given value, or to <code>null</code> if the property is to be
306: * removed. If this viewer has such a property, its value is
307: * replaced; otherwise a new property is added.
308: * <p>
309: * The default implementation records properties in an internal
310: * table which is searched linearly. Overriding this method is generally not
311: * required if the number of different keys is small. If a more
312: * efficient representation of a viewer's properties is required,
313: * override both <code>getData</code> and <code>setData</code>.
314: * </p>
315: *
316: * @param key the property name
317: * @param value the property value, or <code>null</code> if
318: * the property is not found
319: */
320: public void setData(String key, Object value) {
321: Assert.isNotNull(key);
322: /* Remove the key/value pair */
323: if (value == null) {
324: if (keys == null) {
325: return;
326: }
327: int index = 0;
328: while (index < keys.length && !keys[index].equals(key)) {
329: index++;
330: }
331: if (index == keys.length) {
332: return;
333: }
334: if (keys.length == 1) {
335: keys = null;
336: values = null;
337: } else {
338: String[] newKeys = new String[keys.length - 1];
339: Object[] newValues = new Object[values.length - 1];
340: System.arraycopy(keys, 0, newKeys, 0, index);
341: System.arraycopy(keys, index + 1, newKeys, index,
342: newKeys.length - index);
343: System.arraycopy(values, 0, newValues, 0, index);
344: System.arraycopy(values, index + 1, newValues, index,
345: newValues.length - index);
346: keys = newKeys;
347: values = newValues;
348: }
349: return;
350: }
351:
352: /* Add the key/value pair */
353: if (keys == null) {
354: keys = new String[] { key };
355: values = new Object[] { value };
356: return;
357: }
358: for (int i = 0; i < keys.length; i++) {
359: if (keys[i].equals(key)) {
360: values[i] = value;
361: return;
362: }
363: }
364: String[] newKeys = new String[keys.length + 1];
365: Object[] newValues = new Object[values.length + 1];
366: System.arraycopy(keys, 0, newKeys, 0, keys.length);
367: System.arraycopy(values, 0, newValues, 0, values.length);
368: newKeys[keys.length] = key;
369: newValues[values.length] = value;
370: keys = newKeys;
371: values = newValues;
372: }
373:
374: /**
375: * Sets or clears the input for this viewer.
376: *
377: * @param input the input of this viewer, or <code>null</code> if none
378: */
379: public abstract void setInput(Object input);
380:
381: /**
382: * The viewer implementation of this <code>ISelectionProvider</code>
383: * method make the new selection for this viewer without making it visible.
384: * <p>
385: * This method is equivalent to <code>setSelection(selection,false)</code>.
386: * </p>
387: * <p>
388: * Note that some implementations may not be able to set the selection
389: * without also revealing it, for example (as of 3.3) TreeViewer.
390: * </p>
391: */
392: public void setSelection(ISelection selection) {
393: setSelection(selection, false);
394: }
395:
396: /**
397: * Sets a new selection for this viewer and optionally makes it visible.
398: * <p>
399: * Subclasses must implement this method.
400: * </p>
401: *
402: * @param selection the new selection
403: * @param reveal <code>true</code> if the selection is to be made
404: * visible, and <code>false</code> otherwise
405: */
406: public abstract void setSelection(ISelection selection,
407: boolean reveal);
408: }
|