blob: ed2681e0db69fbe4fa64106bccbdbe2eec13e32f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.jface.util.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>
*/
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.
*
* @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.
* <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);
}
}
}
}
}