blob: d89ae72b24366fc703029ae49550674112ad23ab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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
* Tom Schindl <tom.schindl@bestsolution.at> - bug 153993
*******************************************************************************/
package org.eclipse.jface.viewers;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableTree;
import org.eclipse.swt.custom.TableTreeEditor;
import org.eclipse.swt.custom.TableTreeItem;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Widget;
/**
* A concrete viewer based on a SWT <code>TableTree</code> control.
* <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 tree control and
* configured with a domain-specific content provider, label provider, element
* filter (optional), and element sorter (optional).
* </p>
* <p>
* Content providers for table tree viewers must implement the
* <code>ITreeContentProvider</code> interface.
* </p>
* <p>
* Label providers for table tree viewers must implement either the
* <code>ITableLabelProvider</code> or the <code>ILabelProvider</code>
* interface (see <code>TableTreeViewer.setLabelProvider</code> for more
* details).
* </p>
*
* @deprecated As of 3.1 use {@link TreeViewer} instead
*/
public class TableTreeViewer extends AbstractTreeViewer {
/**
* Internal table viewer implementation.
*/
private TableTreeEditorImpl tableEditorImpl;
/**
* This viewer's table tree control.
*/
private TableTree tableTree;
/**
* This viewer's table tree editor.
*/
private TableTreeEditor tableTreeEditor;
/**
* Copied from original TableEditorImpl and moved here since refactoring
* completely wiped out the original implementation in 3.3
*
* @since 3.1
*/
class TableTreeEditorImpl {
private CellEditor cellEditor;
private CellEditor[] cellEditors;
private ICellModifier cellModifier;
private String[] columnProperties;
private Item tableItem;
private int columnNumber;
private ICellEditorListener cellEditorListener;
private FocusListener focusListener;
private MouseListener mouseListener;
private int doubleClickExpirationTime;
private ColumnViewer viewer;
private TableTreeEditorImpl(ColumnViewer viewer) {
this.viewer = viewer;
initCellEditorListener();
}
/**
* Returns this <code>TableViewerImpl</code> viewer
*
* @return the viewer
*/
public ColumnViewer getViewer() {
return viewer;
}
private void activateCellEditor() {
if( cellEditors != null ) {
if( cellEditors[columnNumber] != null && cellModifier != null ) {
Object element = tableItem.getData();
String property = columnProperties[columnNumber];
if( cellModifier.canModify(element, property) ) {
cellEditor = cellEditors[columnNumber];
cellEditor.addListener(cellEditorListener);
Object value = cellModifier.getValue(element, property);
cellEditor.setValue(value);
// Tricky flow of control here:
// activate() can trigger callback to cellEditorListener
// which will clear cellEditor
// so must get control first, but must still call activate()
// even if there is no control.
final Control control = cellEditor.getControl();
cellEditor.activate();
if (control == null) {
return;
}
setLayoutData(cellEditor.getLayoutData());
setEditor(control, tableItem, columnNumber);
cellEditor.setFocus();
if (focusListener == null) {
focusListener = new FocusAdapter() {
public void focusLost(FocusEvent e) {
applyEditorValue();
}
};
}
control.addFocusListener(focusListener);
mouseListener = new MouseAdapter() {
public void mouseDown(MouseEvent e) {
// time wrap?
// check for expiration of doubleClickTime
if (e.time <= doubleClickExpirationTime) {
control.removeMouseListener(mouseListener);
cancelEditing();
handleDoubleClickEvent();
} else if (mouseListener != null) {
control.removeMouseListener(mouseListener);
}
}
};
control.addMouseListener(mouseListener);
}
}
}
}
/**
* Activate a cell editor for the given mouse position.
*/
private void activateCellEditor(MouseEvent event) {
if (tableItem == null || tableItem.isDisposed()) {
// item no longer exists
return;
}
int columnToEdit;
int columns = getColumnCount();
if (columns == 0) {
// If no TableColumn, Table acts as if it has a single column
// which takes the whole width.
columnToEdit = 0;
} else {
columnToEdit = -1;
for (int i = 0; i < columns; i++) {
Rectangle bounds = getBounds(tableItem, i);
if (bounds.contains(event.x, event.y)) {
columnToEdit = i;
break;
}
}
if (columnToEdit == -1) {
return;
}
}
columnNumber = columnToEdit;
activateCellEditor();
}
/**
* Deactivates the currently active cell editor.
*/
public void applyEditorValue() {
CellEditor c = this.cellEditor;
if (c != null) {
// null out cell editor before calling save
// in case save results in applyEditorValue being re-entered
// see 1GAHI8Z: ITPUI:ALL - How to code event notification when
// using cell editor ?
this.cellEditor = null;
Item t = this.tableItem;
// don't null out table item -- same item is still selected
if (t != null && !t.isDisposed()) {
saveEditorValue(c, t);
}
setEditor(null, null, 0);
c.removeListener(cellEditorListener);
Control control = c.getControl();
if (control != null) {
if (mouseListener != null) {
control.removeMouseListener(mouseListener);
}
if (focusListener != null) {
control.removeFocusListener(focusListener);
}
}
c.deactivate();
}
}
/**
* Cancels the active cell editor, without saving the value back to the
* domain model.
*/
public void cancelEditing() {
if (cellEditor != null) {
setEditor(null, null, 0);
cellEditor.removeListener(cellEditorListener);
CellEditor oldEditor = cellEditor;
cellEditor = null;
oldEditor.deactivate();
}
}
/**
* Start editing the given element.
*
* @param element
* @param column
*/
public void editElement(Object element, int column) {
if (cellEditor != null) {
applyEditorValue();
}
setSelection(new StructuredSelection(element), true);
Item[] selection = getSelection();
if (selection.length != 1) {
return;
}
tableItem = selection[0];
// Make sure selection is visible
showSelection();
columnNumber = column;
activateCellEditor();
}
/**
* Return the array of CellEditors used in the viewer
*
* @return the cell editors
*/
public CellEditor[] getCellEditors() {
return cellEditors;
}
/**
* Get the cell modifier
*
* @return the cell modifier
*/
public ICellModifier getCellModifier() {
return cellModifier;
}
/**
* Return the properties for the column
*
* @return the array of column properties
*/
public Object[] getColumnProperties() {
return columnProperties;
}
/**
* Handles the mouse down event; activates the cell editor.
*
* @param event
* the mouse event that should be handled
*/
public void handleMouseDown(MouseEvent event) {
if (event.button != 1) {
return;
}
if (cellEditor != null) {
applyEditorValue();
}
// activate the cell editor immediately. If a second mouseDown
// is received prior to the expiration of the doubleClick time then
// the cell editor will be deactivated and a doubleClick event will
// be processed.
//
doubleClickExpirationTime = event.time
+ Display.getCurrent().getDoubleClickTime();
Item[] items = getSelection();
// Do not edit if more than one row is selected.
if (items.length != 1) {
tableItem = null;
return;
}
tableItem = items[0];
activateCellEditor(event);
}
private void initCellEditorListener() {
cellEditorListener = new ICellEditorListener() {
public void editorValueChanged(boolean oldValidState,
boolean newValidState) {
// Ignore.
}
public void cancelEditor() {
TableTreeEditorImpl.this.cancelEditing();
}
public void applyEditorValue() {
TableTreeEditorImpl.this.applyEditorValue();
}
};
}
/**
* Return whether there is an active cell editor.
*
* @return <code>true</code> if there is an active cell editor;
* otherwise <code>false</code> is returned.
*/
public boolean isCellEditorActive() {
return cellEditor != null;
}
/**
* Saves the value of the currently active cell editor, by delegating to
* the cell modifier.
*/
private void saveEditorValue(CellEditor cellEditor, Item tableItem) {
if( cellModifier != null ) {
if( ! cellEditor.isValueValid() ) {
// Do what????
}
}
String property = null;
if( columnProperties != null && columnNumber < columnProperties.length ) {
property = columnProperties[columnNumber];
}
cellModifier.modify(tableItem, property, cellEditor.getValue());
}
/**
* Set the cell editors
*
* @param editors
*/
public void setCellEditors(CellEditor[] editors) {
this.cellEditors = editors;
}
/**
* Set the cell modifier
*
* @param modifier
*/
public void setCellModifier(ICellModifier modifier) {
this.cellModifier = modifier;
}
/**
* Set the column properties
*
* @param columnProperties
*/
public void setColumnProperties(String[] columnProperties) {
this.columnProperties = columnProperties;
}
Rectangle getBounds(Item item, int columnNumber) {
return ((TableTreeItem) item).getBounds(columnNumber);
}
int getColumnCount() {
// getColumnCount() should be a API in TableTree.
return getTableTree().getTable().getColumnCount();
}
Item[] getSelection() {
return getTableTree().getSelection();
}
void setEditor(Control w, Item item, int columnNumber) {
tableTreeEditor.setEditor(w, (TableTreeItem) item, columnNumber);
}
void setSelection(StructuredSelection selection, boolean b) {
TableTreeViewer.this.setSelection(selection, b);
}
void showSelection() {
getTableTree().showSelection();
}
void setLayoutData(CellEditor.LayoutData layoutData) {
tableTreeEditor.horizontalAlignment = layoutData.horizontalAlignment;
tableTreeEditor.grabHorizontal = layoutData.grabHorizontal;
tableTreeEditor.minimumWidth = layoutData.minimumWidth;
}
void handleDoubleClickEvent() {
Viewer viewer = getViewer();
fireDoubleClick(new DoubleClickEvent(viewer, viewer.getSelection()));
fireOpen(new OpenEvent(viewer, viewer.getSelection()));
}
}
/**
* Creates a table tree viewer on the given table tree control. The viewer
* has no input, no content provider, a default label provider, no sorter,
* and no filters.
*
* @param tree
* the table tree control
*/
public TableTreeViewer(TableTree tree) {
super();
tableTree = tree;
hookControl(tree);
tableTreeEditor = new TableTreeEditor(tableTree);
tableEditorImpl = new TableTreeEditorImpl(this);
}
/**
* Creates a table tree viewer on a newly-created table tree control under
* the given parent. The table tree control is created using the SWT style
* bits <code>MULTI, H_SCROLL, V_SCROLL, and BORDER</code>. The viewer
* has no input, no content provider, a default label provider, no sorter,
* and no filters.
*
* @param parent
* the parent control
*/
public TableTreeViewer(Composite parent) {
this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
}
/**
* Creates a table tree viewer on a newly-created table tree control under
* the given parent. The table tree control is created using the given SWT
* style bits. The viewer has no input, no content provider, a default label
* provider, no sorter, and no filters.
*
* @param parent
* the parent control
* @param style
* the SWT style bits
*/
public TableTreeViewer(Composite parent, int style) {
this(new TableTree(parent, style));
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected void addTreeListener(Control c, TreeListener listener) {
((TableTree) c).addTreeListener(listener);
}
/**
* Cancels a currently active cell editor. All changes already done in the
* cell editor are lost.
*/
public void cancelEditing() {
tableEditorImpl.cancelEditing();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected void doUpdateItem(Item item, Object element) {
// update icon and label
// Similar code in TableTreeViewer.doUpdateItem()
IBaseLabelProvider prov = getLabelProvider();
ITableLabelProvider tprov = null;
if (prov instanceof ITableLabelProvider) {
tprov = (ITableLabelProvider) prov;
}
int columnCount = tableTree.getTable().getColumnCount();
TableTreeItem ti = (TableTreeItem) item;
// Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
// TableViewer with 0 columns does not work
for (int column = 0; column < columnCount || column == 0; column++) {
String text = "";//$NON-NLS-1$
Image image = null;
if (tprov != null) {
text = tprov.getColumnText(element, column);
image = tprov.getColumnImage(element, column);
} else {
if (column == 0) {
ViewerLabel updateLabel = new ViewerLabel(item.getText(),
item.getImage());
buildLabel(updateLabel, element);
// As it is possible for user code to run the event
// loop check here.
if (item.isDisposed()) {
unmapElement(element, item);
return;
}
text = updateLabel.getText();
image = updateLabel.getImage();
}
}
// Avoid setting text to null
if (text == null) {
text = ""; //$NON-NLS-1$
}
ti.setText(column, text);
// Apparently a problem to setImage to null if already null
if (ti.getImage(column) != image) {
ti.setImage(column, image);
}
getColorAndFontCollector().setFontsAndColors(element);
getColorAndFontCollector().applyFontsAndColors(ti);
}
}
/**
* Starts editing the given element.
*
* @param element
* the element
* @param column
* the column number
*/
public void editElement(Object element, int column) {
tableEditorImpl.editElement(element, column);
}
/**
* Returns the cell editors of this viewer.
*
* @return the list of cell editors
*/
public CellEditor[] getCellEditors() {
return tableEditorImpl.getCellEditors();
}
/**
* Returns the cell modifier of this viewer.
*
* @return the cell modifier
*/
public ICellModifier getCellModifier() {
return tableEditorImpl.getCellModifier();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected Item[] getChildren(Widget o) {
if (o instanceof TableTreeItem) {
return ((TableTreeItem) o).getItems();
}
if (o instanceof TableTree) {
return ((TableTree) o).getItems();
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt.widgets.Widget,
* int)
*/
protected Item getChild(Widget widget, int index) {
if (widget instanceof TableTreeItem) {
return ((TableTreeItem) widget).getItem(index);
}
if (widget instanceof TableTree) {
return ((TableTree) widget).getItem(index);
}
return null;
}
/**
* Returns the column properties of this viewer. The properties must
* correspond with the columns of the table control. They are used to
* identify the column in a cell modifier.
*
* @return the list of column properties
*/
public Object[] getColumnProperties() {
return tableEditorImpl.getColumnProperties();
}
/*
* (non-Javadoc) Method declared on Viewer.
*/
public Control getControl() {
return tableTree;
}
/**
* Returns the element with the given index from this viewer. Returns
* <code>null</code> if the index is out of range.
* <p>
* This method is internal to the framework.
* </p>
*
* @param index
* the zero-based index
* @return the element at the given index, or <code>null</code> if the
* index is out of range
*/
public Object getElementAt(int index) {
// XXX: Workaround for 1GBCSB1: SWT:WIN2000 - TableTree should have
// getItem(int index)
TableTreeItem i = tableTree.getItems()[index];
if (i != null) {
return i.getData();
}
return null;
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected boolean getExpanded(Item item) {
return ((TableTreeItem) item).getExpanded();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)
*/
protected Item getItemAt(Point p) {
return getTableTree().getTable().getItem(p);
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected int getItemCount(Control widget) {
return ((TableTree) widget).getItemCount();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected int getItemCount(Item item) {
return ((TableTreeItem) item).getItemCount();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected org.eclipse.swt.widgets.Item[] getItems(
org.eclipse.swt.widgets.Item item) {
return ((TableTreeItem) item).getItems();
}
/**
* The table tree viewer implementation of this <code>Viewer</code>
* framework method returns the label provider, which in the case of table
* tree viewers will be an instance of either
* <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If
* it is an <code>ITableLabelProvider</code>, then it provides a separate
* label text and image for each column. If it is an
* <code>ILabelProvider</code>, then it provides only the label text and
* image for the first column, and any remaining columns are blank.
*/
public IBaseLabelProvider getLabelProvider() {
return super.getLabelProvider();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected Item getParentItem(Item item) {
return ((TableTreeItem) item).getParentItem();
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected Item[] getSelection(Control widget) {
return ((TableTree) widget).getSelection();
}
/**
* Returns this table tree viewer's table tree control.
*
* @return the table tree control
*/
public TableTree getTableTree() {
return tableTree;
}
/*
* (non-Javadoc) Method declared on AbstractTreeViewer.
*/
protected void hookControl(Control control) {
super.hookControl(control);
tableTree.getTable().addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
/*
* If user clicked on the [+] or [-], do not activate
* CellEditor.
*/
// XXX: This code should not be here. SWT should either have
// support to see
// if the user clicked on the [+]/[-] or manage the table editor
// activation
org.eclipse.swt.widgets.TableItem[] items = tableTree
.getTable().getItems();
for (int i = 0; i < items.length; i++) {
Rectangle rect = items[i].getImageBounds(0);
if (rect.contains(e.x, e.y)) {
return;
}
}
tableEditorImpl.handleMouseDown(e);
}
});
}
/**
* Returns whether there is an active cell editor.
*
* @return <code>true</code> if there is an active cell editor, and
* <code>false</code> otherwise
*/
public boolean isCellEditorActive() {
return tableEditorImpl.isCellEditorActive();
}
/*
* (non-Javadoc) Method declared in AbstractTreeViewer.
*/
protected Item newItem(Widget parent, int flags, int ix) {
TableTreeItem item;
if (ix >= 0) {
if (parent instanceof TableTreeItem) {
item = new TableTreeItem((TableTreeItem) parent, flags, ix);
} else {
item = new TableTreeItem((TableTree) parent, flags, ix);
}
} else {
if (parent instanceof TableTreeItem) {
item = new TableTreeItem((TableTreeItem) parent, flags);
} else {
item = new TableTreeItem((TableTree) parent, flags);
}
}
return item;
}
/*
* (non-Javadoc) Method declared in AbstractTreeViewer.
*/
protected void removeAll(Control widget) {
((TableTree) widget).removeAll();
}
/**
* Sets the cell editors of this table viewer.
*
* @param editors
* the list of cell editors
*/
public void setCellEditors(CellEditor[] editors) {
tableEditorImpl.setCellEditors(editors);
}
/**
* Sets the cell modifier of this table viewer.
*
* @param modifier
* the cell modifier
*/
public void setCellModifier(ICellModifier modifier) {
tableEditorImpl.setCellModifier(modifier);
}
/**
* Sets the column properties of this table viewer. The properties must
* correspond with the columns of the table control. They are used to
* identify the column in a cell modifier.
*
* @param columnProperties
* the list of column properties
*/
public void setColumnProperties(String[] columnProperties) {
tableEditorImpl.setColumnProperties(columnProperties);
}
/*
* (non-Javadoc) Method declared in AbstractTreeViewer.
*/
protected void setExpanded(Item node, boolean expand) {
((TableTreeItem) node).setExpanded(expand);
}
/*
* (non-Javadoc) Method declared in AbstractTreeViewer.
*/
protected void setSelection(List items) {
TableTreeItem[] newItems = new TableTreeItem[items.size()];
items.toArray(newItems);
getTableTree().setSelection(newItems);
}
/*
* (non-Javadoc) Method declared in AbstractTreeViewer.
*/
protected void showItem(Item item) {
getTableTree().showItem((TableTreeItem) item);
}
}