/******************************************************************************* * 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: * rmcamara@us.ibm.com - initial API and implementation * tom.schindl@bestsolution.at - various significant contributions * mirko.paturzo@exeura.eu - improve performance *******************************************************************************/ package org.eclipse.nebula.jface.gridviewer; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.jface.viewers.AbstractTableViewer; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnViewerEditor; import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerRow; import org.eclipse.nebula.jface.gridviewer.internal.CellSelection; import org.eclipse.nebula.jface.gridviewer.internal.SelectionWithFocusRow; import org.eclipse.nebula.widgets.grid.DataVisualizer; import org.eclipse.nebula.widgets.grid.Grid; import org.eclipse.nebula.widgets.grid.GridItem; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; 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.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 table viewers must not implement the {@code * ITreeContentProvider} interface. Instead a {@link GridTreeViewer} should be * used. * <p> * * @author Unknown... * @author Mirko Paturzo <mirko.paturzo@exeura.eu> * * Mirko modified improve performace and reduce used memory fix memory * leak and slow disposed object */ public class GridTableViewer extends AbstractTableViewer { /** This viewer's grid control. */ private final Grid grid; private GridViewerRow cachedRow; private CellLabelProvider rowHeaderLabelProvider; /** * If true, this grid viewer will ensure that the grid's rows / GridItems * are always sized to their preferred height. */ private boolean autoPreferredHeight = false; /** * Creates a grid 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 GridTableViewer(Composite parent) { this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); } /** * Creates a grid 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 dataVisualizer * @param parent * the parent control * @param style * the SWT style bits used to create the grid. */ public GridTableViewer(DataVisualizer dataVisualizer, Composite parent, int style) { this(new Grid(dataVisualizer, parent, style)); } /** * Creates a grid 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 dataVisualizer * @param parent * the parent control */ public GridTableViewer(DataVisualizer dataVisualizer, Composite parent) { this(dataVisualizer, parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); } /** * Creates a grid 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 GridTableViewer(Composite parent, int style) { this(new Grid(parent, style)); } /** * Creates a grid 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 GridTableViewer(Grid grid) { this.grid = grid; hookControl(grid); } /** * Returns the underlying Grid Control. * * @return grid control. */ public Grid getGrid() { return grid; } /** {@inheritDoc} */ @Override protected ViewerRow internalCreateNewRowPart(int style, int rowIndex) { GridItem item; if (rowIndex >= 0) { item = new GridItem(grid, style, rowIndex); } else { item = new GridItem(grid, style); } return getViewerRowFromItem(item); } /** {@inheritDoc} */ @Override protected ColumnViewerEditor createViewerEditor() { return new GridViewerEditor(this, new ColumnViewerEditorActivationStrategy(this), ColumnViewerEditor.DEFAULT); } /** {@inheritDoc} */ @Override protected void doClear(int index) { grid.getDataVisualizer().clearRow(grid.getItem(index)); } /** {@inheritDoc} */ @Override protected void doClearAll() { grid.getDataVisualizer().clearAll(); } /** * @see org.eclipse.jface.viewers.StructuredViewer#refresh() */ @Override public void refresh() { try { super.refresh(); } finally { grid.refreshData(); } } /** {@inheritDoc} */ @Override protected void doSetItemCount(int count) { grid.setItemCount(count); } /** {@inheritDoc} */ @Override protected void doDeselectAll() { grid.deselectAll(); } /** {@inheritDoc} */ @Override protected Widget doGetColumn(int index) { return grid.getColumn(index); } /** {@inheritDoc} */ @Override protected int doGetColumnCount() { return grid.getColumnCount(); } /** {@inheritDoc} */ @Override protected Item doGetItem(int index) { return grid.getItem(index); } /** {@inheritDoc} */ @Override protected int doGetItemCount() { return grid.getItemCount(); } /** {@inheritDoc} */ @Override protected Item[] doGetItems() { return grid.getItems(); } /** {@inheritDoc} */ @Override protected Item[] doGetSelection() { return grid.getSelection(); } /** {@inheritDoc} */ @Override protected int[] doGetSelectionIndices() { return grid.getSelectionIndices(); } /** {@inheritDoc} */ @Override protected int doIndexOf(Item item) { return ((GridItem) item).getRowIndex(); } /** {@inheritDoc} */ @Override protected void doRemove(int[] indices) { grid.remove(indices); } /** {@inheritDoc} */ @Override protected void doRemove(int start, int end) { grid.remove(start, end); } /** {@inheritDoc} */ @SuppressWarnings("deprecation") @Override protected void doRemoveAll() { grid.removeAll(); } /** * (non-Javadoc) * * @see org.eclipse.jface.viewers.AbstractTableViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) * fix crossed reference for GC */ @Override protected void handleDispose(final DisposeEvent event) { super.handleDispose(event); cachedRow = null; rowHeaderLabelProvider = null; getGrid().setRedraw(false); getGrid().disposeAllItems(); getGrid().clearItems(); } /** {@inheritDoc} */ @Override protected void doSetSelection(Item[] items) { GridItem[] items2 = new GridItem[items.length]; for (int i = 0; i < items.length; i++) { items2[i] = (GridItem) items[i]; } grid.setSelection(items2); grid.showSelection(); } /** {@inheritDoc} */ @Override protected void doSetSelection(int[] indices) { grid.setSelection(indices); } /** {@inheritDoc} */ @Override protected void doShowItem(Item item) { grid.showItem((GridItem) item); } /** {@inheritDoc} */ @Override protected void doShowSelection() { grid.showSelection(); } /** {@inheritDoc} */ @Override protected Item getItemAt(Point point) { return grid.getItem(point); } /** {@inheritDoc} */ @Override public Control getControl() { return grid; } /** {@inheritDoc} */ @Override protected ViewerRow getViewerRowFromItem(Widget item) { if (cachedRow == null) { cachedRow = new GridViewerRow((GridItem) item); } else { cachedRow.setItem((GridItem) item); } return cachedRow; } /** * {@inheritDoc} */ @Override protected void doResetItem(Item item) { GridItem gridItem = (GridItem) item; int columnCount = Math.max(1, grid.getColumnCount()); for (int i = 0; i < columnCount; i++) { gridItem.setText(i, ""); //$NON-NLS-1$ gridItem.setImage(null); } } /** * {@inheritDoc} */ @Override protected void doSelect(int[] indices) { grid.select(indices); } /** * 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). * * @param autoPreferredHeight */ 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} */ @Override protected void doUpdateItem(Widget widget, Object element, boolean fullMap) { super.doUpdateItem(widget, element, fullMap); updateRowHeader(widget); if (autoPreferredHeight && !widget.isDisposed()) ((GridItem) widget).pack(); } private void updateRowHeader(Widget widget) { if (rowHeaderLabelProvider != null) { ViewerCell cell = getViewerRowFromItem(widget).getCell(Integer.MAX_VALUE); rowHeaderLabelProvider.update(cell); } } /** * Label provider used by calculate the row header text * * @param rowHeaderLabelProvider * the provider */ public void setRowHeaderLabelProvider(CellLabelProvider rowHeaderLabelProvider) { this.rowHeaderLabelProvider = rowHeaderLabelProvider; } /** * Refresh row headers only * * @param element * the element to start or <code>null</code> if all rows should * be refreshed */ public void refreshRowHeaders(Object element) { boolean refresh = element == null; for (int i = 0; i < getGrid().getItemCount(); i++) { if (refresh || element.equals(getGrid().getItem(i).getData())) { refresh = true; updateRowHeader(getGrid().getItem(i)); } } } /** * {@inheritDoc} */ @Override public void editElement(Object element, int column) { try { getControl().setRedraw(false); Widget item = findItem(element); if (item != null) { ViewerRow row = getViewerRowFromItem(item); if (row != null) { ViewerCell cell = row.getCell(column); if (cell != null) { triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell)); } } } } finally { getControl().setRedraw(true); } // } } /** * {@inheritDoc} */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected void setSelectionToWidget(ISelection selection, boolean reveal) { if (!grid.isCellSelectionEnabled() || !(selection instanceof CellSelection)) { super.setSelectionToWidget(selection, reveal); if (selection instanceof SelectionWithFocusRow) { Object el = ((SelectionWithFocusRow) selection).getFocusElement(); if (el != null) { for (int i = 0; i < grid.getItemCount(); i++) { GridItem item = grid.getItem(i); if (item.getData() == el || el.equals(item.getData()) || (getComparer() != null && getComparer().equals(item.getData(), el))) { grid.setFocusItem(item); break; } } } } } else { CellSelection cellSelection = (CellSelection) selection; List l = cellSelection.toList(); ArrayList pts = new ArrayList(); for (int i = 0; i < grid.getItemCount(); i++) { Iterator it = l.iterator(); Object itemObject = grid.getItem(i).getData(); while (it.hasNext()) { Object checkObject = it.next(); if (itemObject == checkObject || (getComparer() != null && getComparer().equals(itemObject, checkObject))) { Iterator idxIt = cellSelection.getIndices(checkObject).iterator(); while (idxIt.hasNext()) { Integer idx = (Integer) idxIt.next(); pts.add(new Point(idx.intValue(), i)); } } } } Point[] tmp = new Point[pts.size()]; pts.toArray(tmp); grid.setCellSelection(tmp); if (cellSelection.getFocusElement() != null) { Object el = cellSelection.getFocusElement(); for (int i = 0; i < grid.getItemCount(); i++) { GridItem item = grid.getItem(i); if (item.getData() == el || item.getData().equals(el) || (getComparer() != null && getComparer().equals(item.getData(), el))) { grid.setFocusItem(item); break; } } } } } /** * {@inheritDoc} */ @Override public ISelection getSelection() { if (!grid.isCellSelectionEnabled()) { IStructuredSelection selection = (IStructuredSelection) super.getSelection(); Object el = null; if (grid.getFocusItem() != null) { el = grid.getFocusItem().getData(); } return new SelectionWithFocusRow(selection.toList(), el, getComparer()); } else { return createCellSelection(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private CellSelection createCellSelection() { Point[] ps = grid.getCellSelection(); Arrays.sort(ps, new Comparator() { @Override public int compare(Object arg0, Object arg1) { Point a = (Point) arg0; Point b = (Point) arg1; int rv = a.y - b.y; if (rv == 0) { rv = a.x - b.x; } return rv; } }); ArrayList objectList = new ArrayList(); ArrayList indiceLists = new ArrayList(); ArrayList indiceList = new ArrayList(); int curLine = -1; for (int i = 0; i < ps.length; i++) { if (curLine != ps[i].y) { curLine = ps[i].y; indiceList = new ArrayList(); indiceLists.add(indiceList); objectList.add(grid.getItem(curLine).getData()); } indiceList.add(new Integer(ps[i].x)); } Object focusElement = null; if (grid.getFocusItem() != null && !grid.getFocusItem().isDisposed()) { focusElement = grid.getFocusItem().getData(); } return new CellSelection(objectList, indiceLists, focusElement, getComparer()); } }