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 java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.core.runtime.ListenerList;
016: import org.eclipse.core.runtime.Assert;
017: import org.eclipse.jface.util.SafeRunnable;
018: import org.eclipse.swt.SWT;
019: import org.eclipse.swt.events.SelectionEvent;
020: import org.eclipse.swt.widgets.Composite;
021: import org.eclipse.swt.widgets.Table;
022: import org.eclipse.swt.widgets.TableColumn;
023: import org.eclipse.swt.widgets.TableItem;
024: import org.eclipse.swt.widgets.Widget;
025:
026: /**
027: * A concrete viewer based on an SWT <code>Table</code>
028: * control with checkboxes on each node.
029: * <p>
030: * This class is not intended to be subclassed outside the viewer framework.
031: * It is designed to be instantiated with a pre-existing SWT table control and configured
032: * with a domain-specific content provider, label provider, element filter (optional),
033: * and element sorter (optional).
034: * </p>
035: */
036: public class CheckboxTableViewer extends TableViewer implements
037: ICheckable {
038:
039: /**
040: * List of check state listeners (element type: <code>ICheckStateListener</code>).
041: */
042: private ListenerList checkStateListeners = new ListenerList();
043:
044: /**
045: * Creates a table viewer on a newly-created table control under the given parent.
046: * The table control is created using the SWT style bits:
047: * <code>SWT.CHECK</code> and <code>SWT.BORDER</code>.
048: * The table has one column.
049: * The viewer has no input, no content provider, a default label provider,
050: * no sorter, and no filters.
051: * <p>
052: * This is equivalent to calling <code>new CheckboxTableViewer(parent, SWT.BORDER)</code>.
053: * See that constructor for more details.
054: * </p>
055: *
056: * @param parent the parent control
057: *
058: * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table)
059: * instead (see below for details)
060: */
061: public CheckboxTableViewer(Composite parent) {
062: this (parent, SWT.BORDER);
063: }
064:
065: /**
066: * Creates a table viewer on a newly-created table control under the given parent.
067: * The table control is created using the given SWT style bits, plus the
068: * <code>SWT.CHECK</code> style bit.
069: * The table has one column.
070: * The viewer has no input, no content provider, a default label provider,
071: * no sorter, and no filters.
072: * <p>
073: * This also adds a <code>TableColumn</code> for the single column,
074: * and sets a <code>TableLayout</code> on the table which sizes the column to fill
075: * the table for its initial sizing, but does nothing on subsequent resizes.
076: * </p>
077: * <p>
078: * If the caller just needs to show a single column with no header,
079: * it is preferable to use the <code>newCheckList</code> factory method instead,
080: * since SWT properly handles the initial sizing and subsequent resizes in this case.
081: * </p>
082: * <p>
083: * If the caller adds its own columns, uses <code>Table.setHeadersVisible(true)</code>,
084: * or needs to handle dynamic resizing of the table, it is recommended to
085: * create the <code>Table</code> itself, specifying the <code>SWT.CHECK</code> style bit
086: * (along with any other style bits needed), and use <code>new CheckboxTableViewer(Table)</code>
087: * rather than this constructor.
088: * </p>
089: *
090: * @param parent the parent control
091: * @param style SWT style bits
092: *
093: * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table)
094: * instead (see above for details)
095: */
096: public CheckboxTableViewer(Composite parent, int style) {
097: this (createTable(parent, style));
098: }
099:
100: /**
101: * Creates a table viewer on a newly-created table control under the given parent.
102: * The table control is created using the given SWT style bits, plus the
103: * <code>SWT.CHECK</code> style bit.
104: * The table shows its contents in a single column, with no header.
105: * The viewer has no input, no content provider, a default label provider,
106: * no sorter, and no filters.
107: * <p>
108: * No <code>TableColumn</code> is added. SWT does not require a
109: * <code>TableColumn</code> if showing only a single column with no header.
110: * SWT correctly handles the initial sizing and subsequent resizes in this case.
111: *
112: * @param parent the parent control
113: * @param style SWT style bits
114: *
115: * @since 2.0
116: * @return CheckboxTableViewer
117: */
118: public static CheckboxTableViewer newCheckList(Composite parent,
119: int style) {
120: Table table = new Table(parent, SWT.CHECK | style);
121: return new CheckboxTableViewer(table);
122: }
123:
124: /**
125: * Creates a table viewer on the given table control.
126: * The <code>SWT.CHECK</code> style bit must be set on the given table control.
127: * The viewer has no input, no content provider, a default label provider,
128: * no sorter, and no filters.
129: *
130: * @param table the table control
131: */
132: public CheckboxTableViewer(Table table) {
133: super (table);
134: }
135:
136: /* (non-Javadoc)
137: * Method declared on ICheckable.
138: */
139: public void addCheckStateListener(ICheckStateListener listener) {
140: checkStateListeners.add(listener);
141: }
142:
143: /**
144: * Creates a new table control with one column.
145: *
146: * @param parent the parent control
147: * @param style style bits
148: * @return a new table control
149: */
150: protected static Table createTable(Composite parent, int style) {
151: Table table = new Table(parent, SWT.CHECK | style);
152:
153: // Although this table column is not needed, and can cause resize problems,
154: // it can't be removed since this would be a breaking change against R1.0.
155: // See bug 6643 for more details.
156: new TableColumn(table, SWT.NONE);
157: TableLayout layout = new TableLayout();
158: layout.addColumnData(new ColumnWeightData(100));
159: table.setLayout(layout);
160:
161: return table;
162: }
163:
164: /**
165: * Notifies any check state listeners that a check state changed has been received.
166: * Only listeners registered at the time this method is called are notified.
167: *
168: * @param event a check state changed event
169: *
170: * @see ICheckStateListener#checkStateChanged
171: */
172: private void fireCheckStateChanged(
173: final CheckStateChangedEvent event) {
174: Object[] array = checkStateListeners.getListeners();
175: for (int i = 0; i < array.length; i++) {
176: final ICheckStateListener l = (ICheckStateListener) array[i];
177: SafeRunnable.run(new SafeRunnable() {
178: public void run() {
179: l.checkStateChanged(event);
180: }
181: });
182: }
183: }
184:
185: /* (non-Javadoc)
186: * Method declared on ICheckable.
187: */
188: public boolean getChecked(Object element) {
189: Widget widget = findItem(element);
190: if (widget instanceof TableItem) {
191: return ((TableItem) widget).getChecked();
192: }
193: return false;
194: }
195:
196: /**
197: * Returns a list of elements corresponding to checked table items in this
198: * viewer.
199: * <p>
200: * This method is typically used when preserving the interesting
201: * state of a viewer; <code>setCheckedElements</code> is used during the restore.
202: * </p>
203: *
204: * @return the array of checked elements
205: * @see #setCheckedElements
206: */
207: public Object[] getCheckedElements() {
208: TableItem[] children = getTable().getItems();
209: ArrayList v = new ArrayList(children.length);
210: for (int i = 0; i < children.length; i++) {
211: TableItem item = children[i];
212: if (item.getChecked()) {
213: v.add(item.getData());
214: }
215: }
216: return v.toArray();
217: }
218:
219: /**
220: * Returns the grayed state of the given element.
221: *
222: * @param element the element
223: * @return <code>true</code> if the element is grayed,
224: * and <code>false</code> if not grayed
225: */
226: public boolean getGrayed(Object element) {
227: Widget widget = findItem(element);
228: if (widget instanceof TableItem) {
229: return ((TableItem) widget).getGrayed();
230: }
231: return false;
232: }
233:
234: /**
235: * Returns a list of elements corresponding to grayed nodes in this
236: * viewer.
237: * <p>
238: * This method is typically used when preserving the interesting
239: * state of a viewer; <code>setGrayedElements</code> is used during the restore.
240: * </p>
241: *
242: * @return the array of grayed elements
243: * @see #setGrayedElements
244: */
245: public Object[] getGrayedElements() {
246: TableItem[] children = getTable().getItems();
247: List v = new ArrayList(children.length);
248: for (int i = 0; i < children.length; i++) {
249: TableItem item = children[i];
250: if (item.getGrayed()) {
251: v.add(item.getData());
252: }
253: }
254: return v.toArray();
255: }
256:
257: /* (non-Javadoc)
258: * Method declared on StructuredViewer.
259: */
260: public void handleSelect(SelectionEvent event) {
261: if (event.detail == SWT.CHECK) {
262: super .handleSelect(event); // this will change the current selection
263:
264: TableItem item = (TableItem) event.item;
265: Object data = item.getData();
266: if (data != null) {
267: fireCheckStateChanged(new CheckStateChangedEvent(this ,
268: data, item.getChecked()));
269: }
270: } else {
271: super .handleSelect(event);
272: }
273: }
274:
275: /* (non-Javadoc)
276: * Method declared on Viewer.
277: */
278: protected void preservingSelection(Runnable updateCode) {
279:
280: TableItem[] children = getTable().getItems();
281: CustomHashtable checked = newHashtable(children.length * 2 + 1);
282: CustomHashtable grayed = newHashtable(children.length * 2 + 1);
283:
284: for (int i = 0; i < children.length; i++) {
285: TableItem item = children[i];
286: Object data = item.getData();
287: if (data != null) {
288: if (item.getChecked()) {
289: checked.put(data, data);
290: }
291: if (item.getGrayed()) {
292: grayed.put(data, data);
293: }
294: }
295: }
296:
297: super .preservingSelection(updateCode);
298:
299: children = getTable().getItems();
300: for (int i = 0; i < children.length; i++) {
301: TableItem item = children[i];
302: Object data = item.getData();
303: if (data != null) {
304: item.setChecked(checked.containsKey(data));
305: item.setGrayed(grayed.containsKey(data));
306: }
307: }
308: }
309:
310: /* (non-Javadoc)
311: * Method declared on ICheckable.
312: */
313: public void removeCheckStateListener(ICheckStateListener listener) {
314: checkStateListeners.remove(listener);
315: }
316:
317: /**
318: * Sets to the given value the checked state for all elements in this viewer.
319: * Does not fire events to check state listeners.
320: *
321: * @param state <code>true</code> if the element should be checked,
322: * and <code>false</code> if it should be unchecked
323: */
324: public void setAllChecked(boolean state) {
325: TableItem[] children = getTable().getItems();
326: for (int i = 0; i < children.length; i++) {
327: TableItem item = children[i];
328: item.setChecked(state);
329: }
330: }
331:
332: /**
333: * Sets to the given value the grayed state for all elements in this viewer.
334: *
335: * @param state <code>true</code> if the element should be grayed,
336: * and <code>false</code> if it should be ungrayed
337: */
338: public void setAllGrayed(boolean state) {
339: TableItem[] children = getTable().getItems();
340: for (int i = 0; i < children.length; i++) {
341: TableItem item = children[i];
342: item.setGrayed(state);
343: }
344: }
345:
346: /* (non-Javadoc)
347: * Method declared on ICheckable.
348: */
349: public boolean setChecked(Object element, boolean state) {
350: Assert.isNotNull(element);
351: Widget widget = findItem(element);
352: if (widget instanceof TableItem) {
353: ((TableItem) widget).setChecked(state);
354: return true;
355: }
356: return false;
357: }
358:
359: /**
360: * Sets which nodes are checked in this viewer.
361: * The given list contains the elements that are to be checked;
362: * all other nodes are to be unchecked.
363: * Does not fire events to check state listeners.
364: * <p>
365: * This method is typically used when restoring the interesting
366: * state of a viewer captured by an earlier call to <code>getCheckedElements</code>.
367: * </p>
368: *
369: * @param elements the list of checked elements (element type: <code>Object</code>)
370: * @see #getCheckedElements
371: */
372: public void setCheckedElements(Object[] elements) {
373: assertElementsNotNull(elements);
374: CustomHashtable set = newHashtable(elements.length * 2 + 1);
375: for (int i = 0; i < elements.length; ++i) {
376: set.put(elements[i], elements[i]);
377: }
378: TableItem[] items = getTable().getItems();
379: for (int i = 0; i < items.length; ++i) {
380: TableItem item = items[i];
381: Object element = item.getData();
382: if (element != null) {
383: boolean check = set.containsKey(element);
384: // only set if different, to avoid flicker
385: if (item.getChecked() != check) {
386: item.setChecked(check);
387: }
388: }
389: }
390: }
391:
392: /**
393: * Sets the grayed state for the given element in this viewer.
394: *
395: * @param element the element
396: * @param state <code>true</code> if the item should be grayed,
397: * and <code>false</code> if it should be ungrayed
398: * @return <code>true</code> if the element is visible and the gray
399: * state could be set, and <code>false</code> otherwise
400: */
401: public boolean setGrayed(Object element, boolean state) {
402: Assert.isNotNull(element);
403: Widget widget = findItem(element);
404: if (widget instanceof TableItem) {
405: ((TableItem) widget).setGrayed(state);
406: return true;
407: }
408: return false;
409: }
410:
411: /**
412: * Sets which nodes are grayed in this viewer.
413: * The given list contains the elements that are to be grayed;
414: * all other nodes are to be ungrayed.
415: * <p>
416: * This method is typically used when restoring the interesting
417: * state of a viewer captured by an earlier call to <code>getGrayedElements</code>.
418: * </p>
419: *
420: * @param elements the array of grayed elements
421: *
422: * @see #getGrayedElements
423: */
424: public void setGrayedElements(Object[] elements) {
425: assertElementsNotNull(elements);
426: CustomHashtable set = newHashtable(elements.length * 2 + 1);
427: for (int i = 0; i < elements.length; ++i) {
428: set.put(elements[i], elements[i]);
429: }
430: TableItem[] items = getTable().getItems();
431: for (int i = 0; i < items.length; ++i) {
432: TableItem item = items[i];
433: Object element = item.getData();
434: if (element != null) {
435: boolean gray = set.containsKey(element);
436: // only set if different, to avoid flicker
437: if (item.getGrayed() != gray) {
438: item.setGrayed(gray);
439: }
440: }
441: }
442: }
443: }
|