| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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 |
| *******************************************************************************/ |
| package org.eclipse.jface.viewers; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.swt.widgets.Widget; |
| |
| /** |
| * A concrete viewer based on an SWT <code>Table</code> |
| * control with checkboxes on each node. |
| * <p> |
| * This class is not intended to be subclassed outside the viewer framework. |
| * It is designed to be instantiated with a pre-existing SWT table control and configured |
| * with a domain-specific content provider, label provider, element filter (optional), |
| * and element sorter (optional). |
| * </p> |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class CheckboxTableViewer extends TableViewer implements ICheckable { |
| |
| /** |
| * List of check state listeners (element type: <code>ICheckStateListener</code>). |
| */ |
| private ListenerList checkStateListeners = new ListenerList(); |
| |
| /** |
| * Creates a table viewer on a newly-created table control under the given parent. |
| * The table control is created using the SWT style bits: |
| * <code>SWT.CHECK</code> and <code>SWT.BORDER</code>. |
| * The table has one column. |
| * The viewer has no input, no content provider, a default label provider, |
| * no sorter, and no filters. |
| * <p> |
| * This is equivalent to calling <code>new CheckboxTableViewer(parent, SWT.BORDER)</code>. |
| * See that constructor for more details. |
| * </p> |
| * |
| * @param parent the parent control |
| * |
| * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table) |
| * instead (see below for details) |
| */ |
| public CheckboxTableViewer(Composite parent) { |
| this(parent, SWT.BORDER); |
| } |
| |
| /** |
| * Creates a table viewer on a newly-created table control under the given parent. |
| * The table control is created using the given SWT style bits, plus the |
| * <code>SWT.CHECK</code> style bit. |
| * The table has one column. |
| * The viewer has no input, no content provider, a default label provider, |
| * no sorter, and no filters. |
| * <p> |
| * This also adds a <code>TableColumn</code> for the single column, |
| * and sets a <code>TableLayout</code> on the table which sizes the column to fill |
| * the table for its initial sizing, but does nothing on subsequent resizes. |
| * </p> |
| * <p> |
| * If the caller just needs to show a single column with no header, |
| * it is preferable to use the <code>newCheckList</code> factory method instead, |
| * since SWT properly handles the initial sizing and subsequent resizes in this case. |
| * </p> |
| * <p> |
| * If the caller adds its own columns, uses <code>Table.setHeadersVisible(true)</code>, |
| * or needs to handle dynamic resizing of the table, it is recommended to |
| * create the <code>Table</code> itself, specifying the <code>SWT.CHECK</code> style bit |
| * (along with any other style bits needed), and use <code>new CheckboxTableViewer(Table)</code> |
| * rather than this constructor. |
| * </p> |
| * |
| * @param parent the parent control |
| * @param style SWT style bits |
| * |
| * @deprecated use newCheckList(Composite, int) or new CheckboxTableViewer(Table) |
| * instead (see above for details) |
| */ |
| public CheckboxTableViewer(Composite parent, int style) { |
| this(createTable(parent, style)); |
| } |
| |
| /** |
| * Creates a table viewer on a newly-created table control under the given parent. |
| * The table control is created using the given SWT style bits, plus the |
| * <code>SWT.CHECK</code> style bit. |
| * The table shows its contents in a single column, with no header. |
| * The viewer has no input, no content provider, a default label provider, |
| * no sorter, and no filters. |
| * <p> |
| * No <code>TableColumn</code> is added. SWT does not require a |
| * <code>TableColumn</code> if showing only a single column with no header. |
| * SWT correctly handles the initial sizing and subsequent resizes in this case. |
| * |
| * @param parent the parent control |
| * @param style SWT style bits |
| * |
| * @since 2.0 |
| * @return CheckboxTableViewer |
| */ |
| public static CheckboxTableViewer newCheckList(Composite parent, int style) { |
| Table table = new Table(parent, SWT.CHECK | style); |
| return new CheckboxTableViewer(table); |
| } |
| |
| /** |
| * Creates a table viewer on the given table control. |
| * The <code>SWT.CHECK</code> style bit must be set on the given table control. |
| * The viewer has no input, no content provider, a default label provider, |
| * no sorter, and no filters. |
| * |
| * @param table the table control |
| */ |
| public CheckboxTableViewer(Table table) { |
| super(table); |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on ICheckable. |
| */ |
| public void addCheckStateListener(ICheckStateListener listener) { |
| checkStateListeners.add(listener); |
| } |
| |
| /** |
| * Creates a new table control with one column. |
| * |
| * @param parent the parent control |
| * @param style style bits |
| * @return a new table control |
| */ |
| protected static Table createTable(Composite parent, int style) { |
| Table table = new Table(parent, SWT.CHECK | style); |
| |
| // Although this table column is not needed, and can cause resize problems, |
| // it can't be removed since this would be a breaking change against R1.0. |
| // See bug 6643 for more details. |
| new TableColumn(table, SWT.NONE); |
| TableLayout layout = new TableLayout(); |
| layout.addColumnData(new ColumnWeightData(100)); |
| table.setLayout(layout); |
| |
| return table; |
| } |
| |
| /** |
| * Notifies any check state listeners that a check state changed has been received. |
| * Only listeners registered at the time this method is called are notified. |
| * |
| * @param event a check state changed event |
| * |
| * @see ICheckStateListener#checkStateChanged |
| */ |
| private void fireCheckStateChanged(final CheckStateChangedEvent event) { |
| Object[] array = checkStateListeners.getListeners(); |
| for (int i = 0; i < array.length; i++) { |
| final ICheckStateListener l = (ICheckStateListener) array[i]; |
| SafeRunnable.run(new SafeRunnable() { |
| public void run() { |
| l.checkStateChanged(event); |
| } |
| }); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on ICheckable. |
| */ |
| public boolean getChecked(Object element) { |
| Widget widget = findItem(element); |
| if (widget instanceof TableItem) { |
| return ((TableItem) widget).getChecked(); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns a list of elements corresponding to checked table items in this |
| * viewer. |
| * <p> |
| * This method is typically used when preserving the interesting |
| * state of a viewer; <code>setCheckedElements</code> is used during the restore. |
| * </p> |
| * |
| * @return the array of checked elements |
| * @see #setCheckedElements |
| */ |
| public Object[] getCheckedElements() { |
| TableItem[] children = getTable().getItems(); |
| ArrayList v = new ArrayList(children.length); |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| if (item.getChecked()) { |
| v.add(item.getData()); |
| } |
| } |
| return v.toArray(); |
| } |
| |
| /** |
| * Returns the grayed state of the given element. |
| * |
| * @param element the element |
| * @return <code>true</code> if the element is grayed, |
| * and <code>false</code> if not grayed |
| */ |
| public boolean getGrayed(Object element) { |
| Widget widget = findItem(element); |
| if (widget instanceof TableItem) { |
| return ((TableItem) widget).getGrayed(); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns a list of elements corresponding to grayed nodes in this |
| * viewer. |
| * <p> |
| * This method is typically used when preserving the interesting |
| * state of a viewer; <code>setGrayedElements</code> is used during the restore. |
| * </p> |
| * |
| * @return the array of grayed elements |
| * @see #setGrayedElements |
| */ |
| public Object[] getGrayedElements() { |
| TableItem[] children = getTable().getItems(); |
| List v = new ArrayList(children.length); |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| if (item.getGrayed()) { |
| v.add(item.getData()); |
| } |
| } |
| return v.toArray(); |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on StructuredViewer. |
| */ |
| public void handleSelect(SelectionEvent event) { |
| if (event.detail == SWT.CHECK) { |
| super.handleSelect(event); // this will change the current selection |
| |
| TableItem item = (TableItem) event.item; |
| Object data = item.getData(); |
| if (data != null) { |
| fireCheckStateChanged(new CheckStateChangedEvent(this, data, |
| item.getChecked())); |
| } |
| } else { |
| super.handleSelect(event); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on Viewer. |
| */ |
| protected void preservingSelection(Runnable updateCode) { |
| |
| TableItem[] children = getTable().getItems(); |
| CustomHashtable checked = newHashtable(children.length * 2 + 1); |
| CustomHashtable grayed = newHashtable(children.length * 2 + 1); |
| |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| Object data = item.getData(); |
| if (data != null) { |
| if (item.getChecked()) { |
| checked.put(data, data); |
| } |
| if (item.getGrayed()) { |
| grayed.put(data, data); |
| } |
| } |
| } |
| |
| super.preservingSelection(updateCode); |
| |
| children = getTable().getItems(); |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| Object data = item.getData(); |
| if (data != null) { |
| item.setChecked(checked.containsKey(data)); |
| item.setGrayed(grayed.containsKey(data)); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on ICheckable. |
| */ |
| public void removeCheckStateListener(ICheckStateListener listener) { |
| checkStateListeners.remove(listener); |
| } |
| |
| /** |
| * Sets to the given value the checked state for all elements in this viewer. |
| * Does not fire events to check state listeners. |
| * |
| * @param state <code>true</code> if the element should be checked, |
| * and <code>false</code> if it should be unchecked |
| */ |
| public void setAllChecked(boolean state) { |
| TableItem[] children = getTable().getItems(); |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| item.setChecked(state); |
| } |
| } |
| |
| /** |
| * Sets to the given value the grayed state for all elements in this viewer. |
| * |
| * @param state <code>true</code> if the element should be grayed, |
| * and <code>false</code> if it should be ungrayed |
| */ |
| public void setAllGrayed(boolean state) { |
| TableItem[] children = getTable().getItems(); |
| for (int i = 0; i < children.length; i++) { |
| TableItem item = children[i]; |
| item.setGrayed(state); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on ICheckable. |
| */ |
| public boolean setChecked(Object element, boolean state) { |
| Assert.isNotNull(element); |
| Widget widget = findItem(element); |
| if (widget instanceof TableItem) { |
| ((TableItem) widget).setChecked(state); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Sets which nodes are checked in this viewer. |
| * The given list contains the elements that are to be checked; |
| * all other nodes are to be unchecked. |
| * Does not fire events to check state listeners. |
| * <p> |
| * This method is typically used when restoring the interesting |
| * state of a viewer captured by an earlier call to <code>getCheckedElements</code>. |
| * </p> |
| * |
| * @param elements the list of checked elements (element type: <code>Object</code>) |
| * @see #getCheckedElements |
| */ |
| public void setCheckedElements(Object[] elements) { |
| assertElementsNotNull(elements); |
| CustomHashtable set = newHashtable(elements.length * 2 + 1); |
| for (int i = 0; i < elements.length; ++i) { |
| set.put(elements[i], elements[i]); |
| } |
| TableItem[] items = getTable().getItems(); |
| for (int i = 0; i < items.length; ++i) { |
| TableItem item = items[i]; |
| Object element = item.getData(); |
| if (element != null) { |
| boolean check = set.containsKey(element); |
| // only set if different, to avoid flicker |
| if (item.getChecked() != check) { |
| item.setChecked(check); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the grayed state for the given element in this viewer. |
| * |
| * @param element the element |
| * @param state <code>true</code> if the item should be grayed, |
| * and <code>false</code> if it should be ungrayed |
| * @return <code>true</code> if the element is visible and the gray |
| * state could be set, and <code>false</code> otherwise |
| */ |
| public boolean setGrayed(Object element, boolean state) { |
| Assert.isNotNull(element); |
| Widget widget = findItem(element); |
| if (widget instanceof TableItem) { |
| ((TableItem) widget).setGrayed(state); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Sets which nodes are grayed in this viewer. |
| * The given list contains the elements that are to be grayed; |
| * all other nodes are to be ungrayed. |
| * <p> |
| * This method is typically used when restoring the interesting |
| * state of a viewer captured by an earlier call to <code>getGrayedElements</code>. |
| * </p> |
| * |
| * @param elements the array of grayed elements |
| * |
| * @see #getGrayedElements |
| */ |
| public void setGrayedElements(Object[] elements) { |
| assertElementsNotNull(elements); |
| CustomHashtable set = newHashtable(elements.length * 2 + 1); |
| for (int i = 0; i < elements.length; ++i) { |
| set.put(elements[i], elements[i]); |
| } |
| TableItem[] items = getTable().getItems(); |
| for (int i = 0; i < items.length; ++i) { |
| TableItem item = items[i]; |
| Object element = item.getData(); |
| if (element != null) { |
| boolean gray = set.containsKey(element); |
| // only set if different, to avoid flicker |
| if (item.getGrayed() != gray) { |
| item.setGrayed(gray); |
| } |
| } |
| } |
| } |
| } |