/*******************************************************************************
* Copyright (c) 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:
* Michael Houston <schmeeky@gmail.com> - initial API and implementation
* Tom Schindl <tom.schindl@bestsolution.at> - bug fix in: 191216
*******************************************************************************/
package org.eclipse.nebula.jface.gridviewer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerRow;
import org.eclipse.nebula.widgets.grid.Grid;
import org.eclipse.nebula.widgets.grid.GridItem;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
/**
* A concrete viewer based on an Grid control.
* <p>
* This class is not intended to be subclassed outside the viewer framework. It
* is designed to be instantiated with a pre-existing Grid control and
* configured with a domain-specific content provider, label provider, element
* filter (optional), and element sorter (optional).
* <p>
* Content providers for grid tree viewers must implement the
* {@link ITreeContentProvider} interface.
* <p><b>The current implementation does not support lazy content providers.</b></p>
*/
public class GridTreeViewer extends AbstractTreeViewer {
/** This viewer's grid control. */
private Grid grid;
private GridViewerRow cachedRow;
/**
* If true, this grid viewer will ensure that the grid's
* rows / GridItems are always sized to their preferred height.
*/
private boolean autoPreferredHeight = false;
private CellLabelProvider rowHeaderLabelProvider;
/**
* Creates a grid tree viewer on a newly-created grid control under the given
* parent. The grid control is created using the SWT style bits
* <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>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 GridTreeViewer(Composite parent) {
this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
}
/**
* Creates a grid tree viewer on a newly-created grid control under the given
* parent. The grid 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 used to create the grid.
*/
public GridTreeViewer(Composite parent, int style) {
this(new Grid(parent, style));
}
/**
* Creates a grid tree viewer on the given grid control. The viewer has no
* input, no content provider, a default label provider, no sorter, and no
* filters.
*
* @param grid
* the grid control
*/
public GridTreeViewer(Grid grid) {
this.grid = grid;
hookControl(grid);
}
/**
* Returns the underlying {@link Grid} Control.
*
* @return grid control.
*/
public Grid getGrid() {
return grid;
}
/** {@inheritDoc} */
protected Item getItemAt(Point point) {
return grid.getItem(point);
}
/** {@inheritDoc} */
protected ColumnViewerEditor createViewerEditor() {
return new GridViewerEditor(this,
new ColumnViewerEditorActivationStrategy(this),
ColumnViewerEditor.DEFAULT);
}
/** {@inheritDoc} */
protected void addTreeListener(Control control, TreeListener listener) {
((Grid) control).addTreeListener(listener);
}
/** {@inheritDoc} */
protected Item[] getChildren(Widget o) {
if (o instanceof GridItem) {
return ((GridItem) o).getItems();
}
if (o instanceof Grid) {
return ((Grid) o).getRootItems();
}
return null;
}
/** {@inheritDoc} */
protected boolean getExpanded(Item item) {
return ((GridItem) item).isExpanded();
}
/** {@inheritDoc} */
protected int getItemCount(Control control) {
return ((Grid) control).getItemCount();
}
/** {@inheritDoc} */
protected int getItemCount(Item item) {
return ((GridItem) item).getItemCount();
}
/** {@inheritDoc} */
protected Item[] getItems(Item item) {
return ((GridItem) item).getItems();
}
/** {@inheritDoc} */
protected Item getParentItem(Item item) {
return ((GridItem) item).getParentItem();
}
/** {@inheritDoc} */
protected Item[] getSelection(Control control) {
return ((Grid) control).getSelection();
}
/** {@inheritDoc} */
protected Item newItem(Widget parent, int style, int index) {
GridItem item;
if (parent instanceof GridItem) {
item = (GridItem) createNewRowPart(getViewerRowFromItem(parent),
style, index).getItem();
} else {
item = (GridItem) createNewRowPart(null, style, index).getItem();
}
return item;
}
/**
* Create a new ViewerRow at rowIndex
*
* @param parent
* the parent row
* @param style
* the style bits to use for the new row
* @param rowIndex
* the index at which the new row should be created under the parent
* @return ViewerRow
* the new row
*/
private ViewerRow createNewRowPart(ViewerRow parent, int style, int rowIndex) {
if (parent == null) {
if (rowIndex >= 0) {
return getViewerRowFromItem(new GridItem(grid, style, rowIndex));
}
return getViewerRowFromItem(new GridItem(grid, style));
}
if (rowIndex >= 0) {
return getViewerRowFromItem(new GridItem((GridItem) parent
.getItem(), SWT.NONE, rowIndex));
}
return getViewerRowFromItem(new GridItem((GridItem) parent.getItem(),
SWT.NONE));
}
/** {@inheritDoc} */
protected void removeAll(Control control) {
((Grid) control).removeAll();
}
/** {@inheritDoc} */
protected void setExpanded(Item item, boolean expand) {
((GridItem) item).setExpanded(expand);
}
/** {@inheritDoc} */
protected void setSelection(List items) {
Item[] current = getSelection(getGrid());
// Don't bother resetting the same selection
if (isSameSelection(items, current)) {
return;
}
GridItem[] newItems = new GridItem[items.size()];
items.toArray(newItems);
getGrid().setSelection(newItems);
getGrid().showSelection();
}
/** {@inheritDoc} */
protected void showItem(Item item) {
getGrid().showItem((GridItem) item);
}
/** {@inheritDoc} */
public Control getControl() {
return getGrid();
}
/** {@inheritDoc} */
protected ViewerRow getViewerRowFromItem(Widget item) {
if (cachedRow == null) {
cachedRow = new GridViewerRow((GridItem) item);
} else {
cachedRow.setItem((GridItem) item);
}
return cachedRow;
}
/** {@inheritDoc} */
protected Widget getColumnViewerOwner(int columnIndex) {
if (columnIndex < 0
|| (columnIndex > 0 && columnIndex >= getGrid()
.getColumnCount())) {
return null;
}
if (getGrid().getColumnCount() == 0)// Hang it off the table if it
return getGrid();
return getGrid().getColumn(columnIndex);
}
/**
* Returns the number of columns of this viewer.
*
* @return the number of columns
*/
protected int doGetColumnCount() {
return grid.getColumnCount();
}
/**
* When set to true, this grid viewer will ensure that each of
* the grid's items is always automatically sized to its preferred
* height. The default is false.
* <p>
* Since this mechanism usually leads to a grid with rows of
* different heights and thus to a grid with decreased performance,
* it should only be applied if that is intended. To set the
* height of all items to a specific value, use {@link Grid#setItemHeight(int)}
* instead.
* <p>
* When a column with activated word wrapping is resized
* by dragging the column resizer, the items are only auto-resized
* properly if you use {@link GridViewerColumn} to create the
* columns.
* <p>
* When this method is called, existing rows are not resized to their
* preferred height. Therefore it is suggested that this method be called
* before rows are populated (i.e. before setInput).
*/
public void setAutoPreferredHeight(boolean autoPreferredHeight) {
this.autoPreferredHeight = autoPreferredHeight;
}
/**
* @return true if this grid viewer sizes its rows to their
* preferred height
* @see #setAutoPreferredHeight(boolean)
*/
public boolean getAutoPreferredHeight() {
return autoPreferredHeight;
}
/** {@inheritDoc} */
protected void doUpdateItem(final Item item, Object element) {
super.doUpdateItem(item, element);
updateRowHeader(item);
if(autoPreferredHeight && !item.isDisposed())
((GridItem)item).pack();
}
/**
* Removes the element at the specified index of the parent. The selection is updated if required.
*
* @param parentOrTreePath the parent element, the input element, or a tree path to the parent element
* @param index child index
* @since 3.3
*/
public void remove(final Object parentOrTreePath, final int index) {
if (checkBusy())
return;
final List oldSelection = new LinkedList(Arrays
.asList(((TreeSelection) getSelection()).getPaths()));
preservingSelection(new Runnable() {
public void run() {
TreePath removedPath = null;
if (internalIsInputOrEmptyPath(parentOrTreePath)) {
Tree tree = (Tree) getControl();
if (index < tree.getItemCount()) {
TreeItem item = tree.getItem(index);
if (item.getData() != null) {
removedPath = getTreePathFromItem(item);
disassociate(item);
}
item.dispose();
}
} else {
Widget[] parentItems = internalFindItems(parentOrTreePath);
for (int i = 0; i < parentItems.length; i++) {
TreeItem parentItem = (TreeItem) parentItems[i];
if (parentItem.isDisposed())
continue;
if (index < parentItem.getItemCount()) {
TreeItem item = parentItem.getItem(index);
if (item.getData() != null) {
removedPath = getTreePathFromItem(item);
disassociate(item);
}
item.dispose();
}
}
}
if (removedPath != null) {
boolean removed = false;
for (Iterator it = oldSelection.iterator(); it
.hasNext();) {
TreePath path = (TreePath) it.next();
if (path.startsWith(removedPath, getComparer())) {
it.remove();
removed = true;
}
}
if (removed) {
setSelection(new TreeSelection(
(TreePath[]) oldSelection
.toArray(new TreePath[oldSelection
.size()]), getComparer()),
false);
}
}
}
});
}
/**
* Label provider used by calculate the row header text
*
* @param rowHeaderLabelProvider
* the provider
*/
public void setRowHeaderLabelProvider(
CellLabelProvider rowHeaderLabelProvider) {
this.rowHeaderLabelProvider = rowHeaderLabelProvider;
}
private void updateRowHeader(Widget widget) {
if (rowHeaderLabelProvider != null) {
ViewerCell cell = getViewerRowFromItem(widget).getCell(
Integer.MAX_VALUE);
rowHeaderLabelProvider.update(cell);
}
}
}