/******************************************************************************* * Copyright (c) 2005, 2013 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Assert; 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.jface.resource.ImageDescriptor; 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.ViewerComparator; 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.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.progress.WorkbenchJob; /** * @since 3.2 */ public class AsynchronousTableViewer extends AsynchronousViewer implements Listener { private Table fTable; 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 the backing table widget */ public AsynchronousTableViewer(Table table) { Assert.isTrue((table.getStyle() & SWT.VIRTUAL) != 0); fTable = table; hookControl(fTable); fTableEditor = new TableEditor(fTable); fTableEditorImpl = createTableEditorImpl(); } @Override protected void hookControl(Control control) { super.hookControl(control); control.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { fTableEditorImpl.handleMouseDown(e); } }); } @Override public synchronized void dispose() { fTableEditor.dispose(); fTable.dispose(); super.dispose(); } @Override 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()]; ModelNode[] nodes = getModel().getRootNode().getChildrenNodes(); if (nodes != null) { int index = 0; // I'm not sure if it would be faster to check TableItems first... for (int i = 0; i < nodes.length; i++) { Object element = nodes[i].getElement(); 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; } @Override protected boolean acceptsSelection(ISelection selection) { return selection instanceof IStructuredSelection; } @Override protected ISelection getEmptySelection() { return StructuredSelection.EMPTY; } protected Widget getParent(Widget widget) { if (widget instanceof TableItem) { return fTable; } return null; } @Override protected List getSelectionFromWidget() { TableItem[] selection = fTable.getSelection(); List datas = new ArrayList<>(selection.length); for (TableItem element : selection) { datas.add(element.getData()); } return datas; } @Override public Control getControl() { return fTable; } public Table getTable() { return (Table) getControl(); } @Override protected void internalRefresh(ModelNode node) { super.internalRefresh(node); if (node.getElement().equals(getInput())) { updateChildren(node); } } @Override protected void restoreLabels(Item item) { TableItem tableItem = (TableItem) item; String[] values = (String[]) tableItem.getData(OLD_LABEL); Image[] images = (Image[]) tableItem.getData(OLD_IMAGE); if (values != null) { tableItem.setText(values); tableItem.setImage(images); } } @Override public void setLabels(Widget widget, String[] labels, ImageDescriptor[] imageDescriptors) { TableItem item = (TableItem) widget; item.setText(labels); item.setData(OLD_LABEL, labels); Image[] images = new Image[labels.length]; item.setData(OLD_IMAGE, images); if (imageDescriptors != null) { for (int i = 0; i < images.length; i++) { if (i < imageDescriptors.length) { images[i] = getImage(imageDescriptors[i]); } } } item.setImage(images); } @Override 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); } } @Override 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$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (!fTable.isDisposed()) { fTable.setHeaderVisible(showHeaders); } return Status.OK_STATUS; } }; job.setPriority(Job.INTERACTIVE); job.setSystem(true); job.schedule(); } @Override 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) { @Override Rectangle getBounds(Item item, int columnNumber) { return ((TableItem) item).getBounds(columnNumber); } @Override int getColumnCount() { return getTable().getColumnCount(); } @Override Item[] getSelection() { return getTable().getSelection(); } @Override void setEditor(Control w, Item item, int columnNumber) { fTableEditor.setEditor(w, (TableItem) item, columnNumber); } @Override void setSelection(StructuredSelection selection, boolean b) { AsynchronousTableViewer.this.setSelection(selection, b); } @Override void showSelection() { getTable().showSelection(); } @Override void setLayoutData(CellEditor.LayoutData layoutData) { fTableEditor.grabHorizontal = layoutData.grabHorizontal; fTableEditor.horizontalAlignment = layoutData.horizontalAlignment; fTableEditor.minimumWidth = layoutData.minimumWidth; } @Override void handleDoubleClickEvent() { Viewer viewer = getViewer(); fireDoubleClick(new DoubleClickEvent(viewer, viewer.getSelection())); fireOpen(new OpenEvent(viewer, viewer.getSelection())); } }; } @Override 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(); } /** * 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) { ViewerComparator comparator = getComparator(); if (comparator == 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 = comparator.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 = comparator.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 } ((AsynchronousTableModel)getModel()).add(elements); } 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 } ((AsynchronousTableModel)getModel()).remove(elements); } public void insert(Object element, int position) { if (element != null) { insert(new Object[] { element }, position); } } public void insert(Object[] elements, int position) { if (elements == null || elements.length == 0) { return; } ((AsynchronousTableModel)getModel()).insert(elements, position); } public void replace(Object element, Object replacement) { if (element == null || replacement == null) { throw new IllegalArgumentException("unexpected null parameter"); //$NON-NLS-1$ } ((AsynchronousTableModel)getModel()).replace(element, replacement); } @Override protected AsynchronousModel createModel() { return new AsynchronousTableModel(this); } @Override protected void setItemCount(Widget parent, int itemCount) { fTable.setItemCount(itemCount); } protected int getVisibleItemCount(int top) { int itemCount = fTable.getItemCount(); return Math.min((fTable.getBounds().height / fTable.getItemHeight()) + 2, itemCount - top); } @Override public AbstractUpdatePolicy createUpdatePolicy() { return new TableUpdatePolicy(); } @Override protected Widget getParentWidget(Widget widget) { if (widget instanceof TableItem) { return ((TableItem)widget).getParent(); } return null; } @Override protected Widget getChildWidget(Widget parent, int index) { if (index < fTable.getItemCount()) { return fTable.getItem(index); } return null; } @Override protected void clear(Widget item) { if (item instanceof TableItem) { int i = fTable.indexOf((TableItem)item); if (i >= 0) { fTable.clear(i); } } } @Override protected void clearChild(Widget parent, int childIndex) { if (parent instanceof Table) { fTable.clear(childIndex); } } @Override protected void clearChildren(Widget item) { if (item instanceof Table) { fTable.clearAll(); } } @Override protected int indexOf(Widget parent, Widget child) { if (parent instanceof Table) { return ((Table)parent).indexOf((TableItem)child); } return -1; } }