blob: 307ccbd354eb0766083023f895e5745ce8ad8245 [file] [log] [blame]
package org.eclipse.debug.internal.ui.viewers;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.internal.ui.viewers.update.DefaultTableUpdatePolicy;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.progress.WorkbenchJob;
/**
* TODO non virtual table support
* TODO sorting by column
*/
public class AsynchronousTableViewer extends AsynchronousViewer {
private Table fTable;
private AsynchronousTableViewerContentManager fContentManager;
private TableEditor fTableEditor;
private TableEditorImpl fTableEditorImpl;
public AsynchronousTableViewer(Composite parent) {
this(parent, SWT.VIRTUAL);
}
public AsynchronousTableViewer(Composite parent, int style) {
this(new Table(parent, style));
}
/**
* Table must be SWT.VIRTUAL. This is intentional. Labels will never be
* retrieved for non-visible items.
*
* @see SWT.VIRTUAL
* @param table
*/
public AsynchronousTableViewer(Table table) {
Assert.isTrue((table.getStyle() & SWT.VIRTUAL) != 0);
fTable = table;
fContentManager = createContentManager();
hookControl(fTable);
fTableEditor = new TableEditor(fTable);
fTableEditorImpl = createTableEditorImpl();
}
protected void hookControl(Control control) {
super.hookControl(control);
control.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
fTableEditorImpl.handleMouseDown(e);
}
});
}
public IUpdatePolicy createUpdatePolicy() {
return new DefaultTableUpdatePolicy();
}
public synchronized void dispose() {
fContentManager.dispose();
fTable.dispose();
fTableEditor.dispose();
super.dispose();
}
private AsynchronousTableViewerContentManager createContentManager() {
return new AsynchronousTableViewerContentManager(this);
}
protected ISelection doAttemptSelectionToWidget(ISelection selection, boolean reveal) {
if (acceptsSelection(selection)) {
List list = ((IStructuredSelection) selection).toList();
if (list == null) {
fTable.deselectAll();
return StructuredSelection.EMPTY;
}
int[] indices = new int[list.size()];
Object[] elements = fContentManager.getElements();
int index = 0;
// I'm not sure if it would be faster to check TableItems first...
for (int i = 0; i < elements.length; i++) {
Object element = elements[i];
if (list.contains(element)) {
indices[index] = i;
index++;
}
}
fTable.setSelection(indices);
if (reveal && indices.length > 0) {
TableItem item = fTable.getItem(indices[0]);
fTable.showItem(item);
}
}
return StructuredSelection.EMPTY;
}
protected boolean acceptsSelection(ISelection selection) {
return selection instanceof IStructuredSelection;
}
protected ISelection getEmptySelection() {
return StructuredSelection.EMPTY;
}
protected Widget getParent(Widget widget) {
if (widget instanceof TableItem) {
return fTable;
}
return null;
}
protected Widget doFindInputItem(Object element) {
if (element.equals(getInput())) {
return fTable;
}
TableItem[] items = fTable.getItems();
for (int i = 0; i < items.length; i++) {
Object data = items[i].getData();
if (data != null && equals(data, element)) {
return items[i];
}
}
return null;
}
protected List getSelectionFromWidget() {
TableItem[] selection = fTable.getSelection();
List datas = new ArrayList(selection.length);
for (int i = 0; i < selection.length; i++) {
datas.add(selection[i].getData());
}
return datas;
}
public Control getControl() {
return fTable;
}
public Table getTable() {
return (Table) getControl();
}
void setChildren(Widget widget, List children) {
Object[] elements = filter(children.toArray());
if (elements.length > 0) {
ViewerSorter sorter = getSorter();
if (sorter != null)
sorter.sort(this, elements);
fContentManager.setElements(elements);
}
}
protected void doUpdateItem(Widget item, Object element, boolean fullMap) {
super.doUpdateItem(item, element, fullMap);
}
protected void updateLabel(Object element, Widget item) {
if (item instanceof Item) {
IAsynchronousLabelAdapter adapter = getLabelAdapter(element);
if (adapter != null) {
ILabelRequestMonitor labelUpdate = new LabelRequestMonitor(item, this);
schedule(labelUpdate);
adapter.retrieveLabel(element, getPresentationContext(), labelUpdate);
}
}
}
protected void inputChanged(Object input, Object oldInput) {
super.inputChanged(input, oldInput);
map(input, fTable);
refresh();
}
protected void internalRefresh(Object element, Widget widget) {
super.internalRefresh(element, widget);
if (element.equals(getRoot())) {
IAsynchronousTreeContentAdapter adapter = getTableContentAdapter(element);
if (adapter != null) {
IChildrenRequestMonitor update = new ChildrenRequestMonitor(widget, this);
schedule(update);
adapter.retrieveChildren(element, getPresentationContext(), update);
}
}
}
private IAsynchronousTreeContentAdapter getTableContentAdapter(Object element) {
IAsynchronousTreeContentAdapter adapter = null;
if (element instanceof IAsynchronousTreeContentAdapter) {
adapter = (IAsynchronousTreeContentAdapter) element;
} else if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
adapter = (IAsynchronousTreeContentAdapter) adaptable.getAdapter(IAsynchronousTreeContentAdapter.class);
}
return adapter;
}
public void setLabels(Widget widget, String[] labels, ImageDescriptor[] imageDescriptors) {
TableItem item = (TableItem) widget;
item.setText(labels);
Image[] images = new Image[labels.length];
if (imageDescriptors != null) {
for (int i = 0; i < images.length; i++) {
if (i < imageDescriptors.length)
images[i] = getImage(imageDescriptors[i]);
}
}
item.setImage(images);
}
public void setColors(Widget widget, RGB[] foregrounds, RGB[] backgrounds) {
TableItem item = (TableItem) widget;
if (foregrounds == null) {
foregrounds = new RGB[fTable.getColumnCount()];
}
if (backgrounds == null) {
backgrounds = new RGB[fTable.getColumnCount()];
}
for (int i = 0; i < foregrounds.length; i++) {
Color fg = getColor(foregrounds[i]);
item.setForeground(i, fg);
}
for (int i = 0; i < backgrounds.length; i++) {
Color bg = getColor(backgrounds[i]);
item.setBackground(i, bg);
}
}
public void setFonts(Widget widget, FontData[] fontDatas) {
TableItem item = (TableItem) widget;
if (fontDatas != null) {
for (int i = 0; i < fontDatas.length; i++) {
Font font = getFont(fontDatas[i]);
item.setFont(i, font);
}
}
}
public void setColumnHeaders(final String[] headers) {
fTableEditorImpl.setColumnProperties(headers);
}
public Object[] getColumnProperties() {
return fTableEditorImpl.getColumnProperties();
}
public void showColumnHeader(final boolean showHeaders) {
WorkbenchJob job = new WorkbenchJob("Set Header Visibility") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
fTable.setHeaderVisible(showHeaders);
return Status.OK_STATUS;
}
};
job.setPriority(Job.INTERACTIVE);
job.setSystem(true);
job.schedule();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
*/
public void reveal(Object element) {
Assert.isNotNull(element);
Widget w = findItem(element);
if (w instanceof TableItem)
getTable().showItem((TableItem) w);
}
/**
* Sets the cell editors of this table viewer.
*
* @param editors
* the list of cell editors
*/
public void setCellEditors(CellEditor[] editors) {
fTableEditorImpl.setCellEditors(editors);
}
/**
* Sets the cell modifier of this table viewer.
*
* @param modifier
* the cell modifier
*/
public void setCellModifier(ICellModifier modifier) {
fTableEditorImpl.setCellModifier(modifier);
}
protected TableEditorImpl createTableEditorImpl() {
return new TableEditorImpl(this) {
Rectangle getBounds(Item item, int columnNumber) {
return ((TableItem) item).getBounds(columnNumber);
}
int getColumnCount() {
return getTable().getColumnCount();
}
Item[] getSelection() {
return getTable().getSelection();
}
void setEditor(Control w, Item item, int columnNumber) {
fTableEditor.setEditor(w, (TableItem) item, columnNumber);
}
void setSelection(StructuredSelection selection, boolean b) {
AsynchronousTableViewer.this.setSelection(selection, b);
}
void showSelection() {
getTable().showSelection();
}
void setLayoutData(CellEditor.LayoutData layoutData) {
fTableEditor.grabHorizontal = layoutData.grabHorizontal;
fTableEditor.horizontalAlignment = layoutData.horizontalAlignment;
fTableEditor.minimumWidth = layoutData.minimumWidth;
}
void handleDoubleClickEvent() {
Viewer viewer = getViewer();
fireDoubleClick(new DoubleClickEvent(viewer, viewer.getSelection()));
fireOpen(new OpenEvent(viewer, viewer.getSelection()));
}
};
}
protected ISelection newSelectionFromWidget() {
Control control = getControl();
if (control == null || control.isDisposed()) {
return StructuredSelection.EMPTY;
}
List list = getSelectionFromWidget();
return new StructuredSelection(list);
}
public CellEditor[] getCellEditors() {
return fTableEditorImpl.getCellEditors();
}
public ICellModifier getCellModifier() {
return fTableEditorImpl.getCellModifier();
}
public boolean isCellEditorActive() {
return fTableEditorImpl.isCellEditorActive();
}
/**
* FIXME: currently requires UI Thread
*
* @param index
*/
void clear(int index) {
TableItem item = fTable.getItem(index);
Object data = item.getData();
if (item.getData() != null) {
unmap(data, item);
}
fTable.clear(index);
}
/**
* This is not asynchronous. This method must be called in the UI Thread.
*/
public void cancelEditing() {
fTableEditorImpl.cancelEditing();
}
/**
* This is not asynchronous. This method must be called in the UI Thread.
*
* @param element
* The element to edit. Each element maps to a row in the Table.
* @param column
* The column to edit
*/
public void editElement(Object element, int column) {
fTableEditorImpl.editElement(element, column);
}
protected int indexForElement(Object element) {
ViewerSorter sorter = getSorter();
if (sorter == null)
return fTable.getItemCount();
int count = fTable.getItemCount();
int min = 0, max = count - 1;
while (min <= max) {
int mid = (min + max) / 2;
Object data = fTable.getItem(mid).getData();
int compare = sorter.compare(this, data, element);
if (compare == 0) {
// find first item > element
while (compare == 0) {
++mid;
if (mid >= count) {
break;
}
data = fTable.getItem(mid).getData();
compare = sorter.compare(this, data, element);
}
return mid;
}
if (compare < 0)
min = mid + 1;
else
max = mid - 1;
}
return min;
}
public void add(Object element) {
if (element != null)
add(new Object[] { element });
}
public void add(Object[] elements) {
if (elements == null || elements.length == 0)
return; // done
ViewerSorter sorter = getSorter();
if (sorter != null) {
insert(elements, 0);
return;
}
final Object[] filteredElements = filter(elements);
if (filteredElements.length == 0)
return;
WorkbenchJob job = new WorkbenchJob("AsychronousTableViewer.add") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (!monitor.isCanceled())
fContentManager.add(filteredElements);
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
public void remove(Object element) {
if (element != null)
remove(new Object[] { element });
}
public void remove(final Object[] elements) {
if (elements == null || elements.length == 0)
return; // done
WorkbenchJob job = new WorkbenchJob("AsychronousTableViewer.remove") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (!monitor.isCanceled())
fContentManager.remove(elements);
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
public void insert(Object element, int position) {
if (element != null)
insert(new Object[] { element }, position);
}
public void insert(Object[] elements, final int position) {
if (elements == null || elements.length == 0)
return;
final Object[] filteredElements = filter(elements);
WorkbenchJob job = new WorkbenchJob("AsychronousTableViewer.insert") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (monitor.isCanceled())
return Status.OK_STATUS;
ViewerSorter sorter = getSorter();
if (sorter == null) {
fContentManager.insert(filteredElements, position);
} else {
for (int i = 0; i < filteredElements.length; i++) {
Object element = filteredElements[i];
int index = indexForElement(element);
fContentManager.insert(new Object[] { element }, index);
}
}
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
public void replace(final Object element, final Object replacement) {
if (element == null || replacement == null)
throw new IllegalArgumentException("unexpected null parameter"); //$NON-NLS-1$
Object[] filtered = filter(new Object[] { replacement });
if (filtered.length == 0) {
remove(element);
return;
}
WorkbenchJob job = new WorkbenchJob("AsychronousTableViewer.replace") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (monitor.isCanceled())
return Status.OK_STATUS;
ViewerSorter sorter = getSorter();
if (sorter == null) {
fContentManager.replace(element, replacement);
} else {
fContentManager.remove(new Object[] { element });
int position = indexForElement(replacement);
fContentManager.insert(new Object[] { replacement }, position);
}
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.setPriority(Job.INTERACTIVE);
job.schedule();
}
}