/******************************************************************************* | |
* Copyright (c) 2000, 2010 IBM Corporation, See4sys and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
* See4sys - added support for problem markers on model objects (rather than | |
* only on workspace resources). Unfortunately, there was no other | |
* choice than copying the whole code from | |
* org.eclipse.ui.views.markers.internal for that purpose because | |
* many of the relevant classes, methods, and fields are private or | |
* package private. | |
*******************************************************************************/ | |
package org.eclipse.sphinx.emf.validation.ui.views; | |
import java.lang.reflect.InvocationTargetException; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.NullProgressMonitor; | |
import org.eclipse.jface.action.IAction; | |
import org.eclipse.jface.action.IMenuListener; | |
import org.eclipse.jface.action.IMenuManager; | |
import org.eclipse.jface.action.IToolBarManager; | |
import org.eclipse.jface.action.MenuManager; | |
import org.eclipse.jface.dialogs.IDialogSettings; | |
import org.eclipse.jface.operation.IRunnableWithProgress; | |
import org.eclipse.jface.viewers.ColumnLayoutData; | |
import org.eclipse.jface.viewers.ColumnPixelData; | |
import org.eclipse.jface.viewers.IOpenListener; | |
import org.eclipse.jface.viewers.ISelectionChangedListener; | |
import org.eclipse.jface.viewers.ISelectionProvider; | |
import org.eclipse.jface.viewers.IStructuredSelection; | |
import org.eclipse.jface.viewers.OpenEvent; | |
import org.eclipse.jface.viewers.SelectionChangedEvent; | |
import org.eclipse.jface.viewers.TableLayout; | |
import org.eclipse.jface.viewers.TreeViewer; | |
import org.eclipse.jface.viewers.TreeViewerColumn; | |
import org.eclipse.jface.viewers.Viewer; | |
import org.eclipse.jface.viewers.ViewerComparator; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.custom.BusyIndicator; | |
import org.eclipse.swt.events.KeyAdapter; | |
import org.eclipse.swt.events.KeyEvent; | |
import org.eclipse.swt.events.SelectionAdapter; | |
import org.eclipse.swt.events.SelectionEvent; | |
import org.eclipse.swt.events.SelectionListener; | |
import org.eclipse.swt.layout.FillLayout; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Menu; | |
import org.eclipse.swt.widgets.ScrollBar; | |
import org.eclipse.swt.widgets.Scrollable; | |
import org.eclipse.swt.widgets.Tree; | |
import org.eclipse.swt.widgets.TreeColumn; | |
import org.eclipse.ui.IActionBars; | |
import org.eclipse.ui.IMemento; | |
import org.eclipse.ui.IViewSite; | |
import org.eclipse.ui.PartInitException; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; | |
import org.eclipse.ui.part.ViewPart; | |
import org.eclipse.ui.preferences.ViewPreferencesAction; | |
import org.eclipse.ui.progress.IWorkbenchSiteProgressService; | |
/** | |
* The TableView is a view that generically implements views with tables. | |
*/ | |
public abstract class TableView extends ViewPart { | |
private static final String TAG_COLUMN_WIDTH = "columnWidth"; //$NON-NLS-1$ | |
private static final String TAG_COLUMN_ORDER = "columnOrder"; //$NON-NLS-1$ | |
private static final String TAG_COLUMN_ORDER_INDEX = "columnOrderIndex"; //$NON-NLS-1$ | |
private static final String TAG_VERTICAL_POSITION = "verticalPosition"; //$NON-NLS-1$ | |
private static final String TAG_HORIZONTAL_POSITION = "horizontalPosition"; //$NON-NLS-1$ | |
private TreeViewer viewer; | |
private IMemento memento; | |
private IAction sortAction; | |
private IAction filtersAction; | |
private IAction preferencesAction; | |
private MarkerTreeContentProvider contentProvider; | |
private ISelectionProvider selectionProvider = new MarkerSelectionProviderAdapter(); | |
/* | |
* (non-Javadoc) Method declared on IViewPart. | |
*/ | |
@Override | |
public void init(IViewSite site, IMemento memento) throws PartInitException { | |
super.init(site, memento); | |
this.memento = memento; | |
} | |
/** | |
* | |
*/ | |
// void haltTableUpdates() { | |
// content.cancelPendingChanges(); | |
// } | |
// void change(Collection toRefresh) { | |
// content.change(toRefresh); | |
// } | |
// void setContents(Collection contents, IProgressMonitor mon) { | |
// content.set(contents, mon); | |
// } | |
abstract protected void viewerSelectionChanged(IStructuredSelection selection); | |
/* | |
* (non-Javadoc) | |
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) | |
*/ | |
@Override | |
public void createPartControl(Composite parent) { | |
parent.setLayout(new FillLayout()); | |
viewer = new TreeViewer(createTree(parent)); | |
createColumns(viewer.getTree()); | |
viewer.setComparator(buildComparator()); | |
setSortIndicators(); | |
contentProvider = new MarkerTreeContentProvider(); | |
viewer.setContentProvider(contentProvider); | |
viewer.addSelectionChangedListener(new ISelectionChangedListener() { | |
@Override | |
public void selectionChanged(SelectionChangedEvent event) { | |
IStructuredSelection selection = (IStructuredSelection) event.getSelection(); | |
viewerSelectionChanged(selection); | |
} | |
}); | |
// create the actions before the input is set on the viewer but after | |
// the | |
// sorter and filter are set so the actions will be enabled correctly. | |
createActions(); | |
viewer.setInput(createViewerInput()); | |
Scrollable scrollable = (Scrollable) viewer.getControl(); | |
ScrollBar bar = scrollable.getVerticalBar(); | |
if (bar != null) { | |
bar.setSelection(restoreVerticalScrollBarPosition(memento)); | |
} | |
bar = scrollable.getHorizontalBar(); | |
if (bar != null) { | |
bar.setSelection(restoreHorizontalScrollBarPosition(memento)); | |
} | |
MenuManager mgr = initContextMenu(); | |
Menu menu = mgr.createContextMenu(viewer.getControl()); | |
viewer.getControl().setMenu(menu); | |
getSite().registerContextMenu(mgr, selectionProvider); | |
getSite().setSelectionProvider(selectionProvider); | |
IActionBars actionBars = getViewSite().getActionBars(); | |
initMenu(actionBars.getMenuManager()); | |
initToolBar(actionBars.getToolBarManager()); | |
registerGlobalActions(getViewSite().getActionBars()); | |
viewer.addOpenListener(new IOpenListener() { | |
@Override | |
public void open(OpenEvent event) { | |
handleOpenEvent(event); | |
} | |
}); | |
viewer.getControl().addKeyListener(new KeyAdapter() { | |
@Override | |
public void keyPressed(KeyEvent e) { | |
handleKeyPressed(e); | |
} | |
}); | |
} | |
/** | |
* Create the viewer input for the receiver. | |
* | |
* @return Object | |
*/ | |
abstract Object createViewerInput(); | |
/** | |
* Set the comparator to be the new comparator. This should only be called if the viewer has been created. | |
* | |
* @param comparator | |
*/ | |
void setComparator(TableComparator comparator) { | |
viewer.setComparator(comparator); | |
updateForNewComparator(comparator); | |
} | |
/** | |
* Update the viewer for comparator updates | |
* | |
* @param comparator | |
*/ | |
void updateForNewComparator(TableComparator comparator) { | |
comparator.saveState(getDialogSettings()); | |
viewer.refresh(); | |
setSortIndicators(); | |
} | |
/** | |
* Create the main tree control | |
* | |
* @param parent | |
* @return Tree | |
*/ | |
protected Tree createTree(Composite parent) { | |
Tree tree = new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION); | |
tree.setLinesVisible(true); | |
return tree; | |
} | |
/** | |
* Get the pixel data for the columns. | |
* | |
* @return ColumnPixelData[] | |
*/ | |
public ColumnPixelData[] getSavedColumnData() { | |
ColumnPixelData[] defaultData = getDefaultColumnLayouts(); | |
ColumnPixelData[] result = new ColumnPixelData[defaultData.length]; | |
for (int i = 0; i < defaultData.length; i++) { | |
ColumnPixelData defaultPixelData = defaultData[i]; | |
boolean addTrim = defaultPixelData.addTrim; | |
int width = defaultPixelData.width; | |
// non-resizable columns are always left at their default width | |
if (defaultPixelData.resizable) { | |
if (memento != null) { | |
Integer widthInt = memento.getInteger(TAG_COLUMN_WIDTH + i); | |
if (widthInt != null && widthInt.intValue() > 0) { | |
width = widthInt.intValue(); | |
addTrim = false; | |
} | |
} | |
} | |
result[i] = new ColumnPixelData(width, defaultPixelData.resizable, addTrim); | |
} | |
return result; | |
} | |
/** | |
* Return the column sizes from the actual widget. Returns the saved column sizes if the widget hasn't been created | |
* yet or its columns haven't been initialized yet. (Note that TableLayout only initializes the column widths after | |
* the first layout, so it is possible for the widget to exist but have all its columns incorrectly set to zero | |
* width - see bug 86329) | |
* | |
* @return ColumnPixelData | |
*/ | |
public ColumnPixelData[] getColumnData() { | |
ColumnPixelData[] defaultData = getSavedColumnData(); | |
Tree tree = getTree(); | |
if (tree != null && (tree.isDisposed() || tree.getBounds().width == 0)) { | |
tree = null; | |
} | |
TreeColumn[] column = null; | |
if (tree != null) { | |
column = tree.getColumns(); | |
} | |
ColumnPixelData[] result = new ColumnPixelData[defaultData.length]; | |
for (int i = 0; i < defaultData.length; i++) { | |
ColumnPixelData defaultPixelData = defaultData[i]; | |
int width = defaultData[i].width; | |
if (column != null && i < column.length) { | |
TreeColumn col = column[i]; | |
if (col.getWidth() > 0) { | |
width = col.getWidth(); | |
} | |
} | |
result[i] = new ColumnPixelData(width, defaultPixelData.resizable, defaultPixelData.addTrim); | |
} | |
return result; | |
} | |
/** | |
* Create the columns in the tree. | |
* | |
* @param tree | |
*/ | |
protected void createColumns(final Tree tree) { | |
TableLayout layout = new TableLayout(); | |
tree.setLayout(layout); | |
tree.setHeaderVisible(true); | |
final IField[] fields = getAllFields(); | |
ColumnLayoutData[] columnWidths = getSavedColumnData(); | |
for (int i = 0; i < fields.length; i++) { | |
layout.addColumnData(columnWidths[i]); | |
TreeColumn tc = new TreeColumn(tree, SWT.NONE, i); | |
IField field = fields[i]; | |
tc.setText(field.getColumnHeaderText()); | |
tc.setImage(field.getColumnHeaderImage()); | |
tc.setResizable(columnWidths[i].resizable); | |
tc.setMoveable(true); | |
tc.addSelectionListener(getHeaderListener()); | |
tc.setData(field); | |
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, tc); | |
viewerColumn.setLabelProvider(new MarkerViewLabelProvider(field)); | |
} | |
int[] order = restoreColumnOrder(memento); | |
if (order != null && order.length == fields.length) { | |
tree.setColumnOrder(order); | |
} | |
} | |
/** | |
* Create the actions for the receiver. | |
*/ | |
protected void createActions() { | |
if (getSortDialog() != null) { | |
sortAction = new TableSortAction(this, getSortDialog()); | |
} | |
} | |
protected MenuManager initContextMenu() { | |
MenuManager mgr = new MenuManager(); | |
mgr.setRemoveAllWhenShown(true); | |
mgr.addMenuListener(new IMenuListener() { | |
@Override | |
public void menuAboutToShow(IMenuManager mgr) { | |
getViewer().cancelEditing(); | |
fillContextMenu(mgr); | |
} | |
}); | |
return mgr; | |
} | |
protected abstract void initToolBar(IToolBarManager tbm); | |
/** | |
* Init the menu for the receiver. | |
* | |
* @param menu | |
*/ | |
protected void initMenu(IMenuManager menu) { | |
if (sortAction != null) { | |
menu.add(sortAction); | |
} | |
addDropDownContributions(menu); | |
if (filtersAction != null) { | |
menu.add(filtersAction); | |
} | |
if (preferencesAction != null) { | |
menu.add(preferencesAction); | |
} | |
} | |
/** | |
* Add any extra contributions to the drop down. | |
* | |
* @param menu | |
*/ | |
void addDropDownContributions(IMenuManager menu) { | |
// Do nothing by default. | |
} | |
protected abstract void registerGlobalActions(IActionBars actionBars); | |
protected abstract void fillContextMenu(IMenuManager manager); | |
/* | |
* (non-Javadoc) | |
* @see org.eclipse.ui.part.WorkbenchPart#setFocus() | |
*/ | |
@Override | |
public void setFocus() { | |
Viewer viewer = getViewer(); | |
if (viewer != null && !viewer.getControl().isDisposed()) { | |
viewer.getControl().setFocus(); | |
} | |
} | |
/** | |
* Build a comparator from the default settings. | |
* | |
* @return ViewerComparator | |
*/ | |
protected ViewerComparator buildComparator() { | |
return createTableComparator(); | |
} | |
/** | |
* Create a TableComparator for the receiver. | |
* | |
* @return TableComparator | |
*/ | |
TableComparator createTableComparator() { | |
TableComparator sorter = TableComparator.createTableSorter(getSortingFields()); | |
sorter.restoreState(getDialogSettings()); | |
return sorter; | |
} | |
// protected abstract ITableViewContentProvider getContentProvider(); | |
protected abstract IField[] getSortingFields(); | |
protected abstract IField[] getAllFields(); | |
protected abstract IDialogSettings getDialogSettings(); | |
/** | |
* Return the viewer. | |
* | |
* @return TreeViewer | |
*/ | |
protected TreeViewer getViewer() { | |
return viewer; | |
} | |
/** | |
* Return the tree for the receiver. | |
* | |
* @return Tree | |
*/ | |
protected Tree getTree() { | |
return getViewer().getTree(); | |
} | |
protected SelectionListener getHeaderListener() { | |
return new SelectionAdapter() { | |
/** | |
* Handles the case of user selecting the header area. | |
*/ | |
@Override | |
public void widgetSelected(SelectionEvent e) { | |
final TreeColumn column = (TreeColumn) e.widget; | |
final IField field = (IField) column.getData(); | |
try { | |
IWorkbenchSiteProgressService progressService = getProgressService(); | |
if (progressService == null) { | |
BusyIndicator.showWhile(getSite().getShell().getDisplay(), new Runnable() { | |
/* | |
* (non-Javadoc) | |
* @see java.lang.Runnable#run() | |
*/ | |
@Override | |
public void run() { | |
resortTable(column, field, new NullProgressMonitor()); | |
} | |
}); | |
} else { | |
getProgressService().busyCursorWhile(new IRunnableWithProgress() { | |
/* | |
* (non-Javadoc) | |
* @seeorg.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime. | |
* IProgressMonitor) | |
*/ | |
@Override | |
public void run(IProgressMonitor monitor) { | |
resortTable(column, field, monitor); | |
} | |
}); | |
} | |
} catch (InvocationTargetException e1) { | |
IDEWorkbenchPlugin.getDefault().getLog().log(Util.errorStatus(e1)); | |
} catch (InterruptedException e1) { | |
return; | |
} | |
} | |
/** | |
* Resort the table based on field. | |
* | |
* @param column | |
* the column being updated | |
* @param field | |
* @param monitor | |
*/ | |
private void resortTable(final TreeColumn column, final IField field, IProgressMonitor monitor) { | |
TableComparator sorter = getTableSorter(); | |
monitor.beginTask(MarkerMessages.sortDialog_title, 100); | |
monitor.worked(10); | |
if (field.equals(sorter.getTopField())) { | |
sorter.reverseTopPriority(); | |
} else { | |
sorter.setTopPriority(field); | |
} | |
monitor.worked(15); | |
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { | |
/* | |
* (non-Javadoc) | |
* @see java.lang.Runnable#run() | |
*/ | |
@Override | |
public void run() { | |
viewer.refresh(); | |
updateDirectionIndicator(column); | |
} | |
}); | |
monitor.done(); | |
} | |
}; | |
} | |
/* | |
* (non-Javadoc) | |
* @see org.eclipse.ui.views.markers.internal.TableView#getDefaultColumnLayouts() | |
*/ | |
protected ColumnPixelData[] getDefaultColumnLayouts() { | |
IField[] fields = getAllFields(); | |
ColumnPixelData[] datas = new ColumnPixelData[fields.length]; | |
for (int i = 0; i < fields.length; i++) { | |
int width = getWidth(fields[i]); | |
boolean resizable = width > 0; | |
datas[i] = new ColumnPixelData(width, resizable, resizable); | |
} | |
return datas; | |
} | |
/** | |
* Return the width of the field to display. | |
* | |
* @param field | |
* @return int | |
*/ | |
private int getWidth(IField field) { | |
if (!field.isShowing()) { | |
return 0; | |
} | |
return field.getPreferredWidth(); | |
} | |
/** | |
* Return a sort dialog for the receiver. | |
* | |
* @return TableSortDialog | |
*/ | |
protected TableSortDialog getSortDialog() { | |
return new TableSortDialog(getSite(), getTableSorter()); | |
} | |
/** | |
* Return the table sorter portion of the sorter. | |
* | |
* @return TableSorter | |
*/ | |
TableComparator getTableSorter() { | |
return (TableComparator) viewer.getComparator(); | |
} | |
protected abstract void handleKeyPressed(KeyEvent event); | |
protected abstract void handleOpenEvent(OpenEvent event); | |
/* | |
* (non-Javadoc) | |
* @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento) | |
*/ | |
@Override | |
public void saveState(IMemento memento) { | |
super.saveState(memento); | |
ColumnPixelData[] data = getColumnData(); | |
for (int i = 0; i < data.length; i++) { | |
ColumnPixelData data2 = data[i]; | |
memento.putInteger(TAG_COLUMN_WIDTH + i, data2.width); | |
} | |
// save column order | |
Tree tree = getTree(); | |
int[] columnOrder = tree.getColumnOrder(); | |
for (int element : columnOrder) { | |
IMemento child = memento.createChild(TAG_COLUMN_ORDER); | |
child.putInteger(TAG_COLUMN_ORDER_INDEX, element); | |
} | |
// save vertical position | |
Scrollable scrollable = (Scrollable) viewer.getControl(); | |
ScrollBar bar = scrollable.getVerticalBar(); | |
int position = bar != null ? bar.getSelection() : 0; | |
memento.putInteger(TAG_VERTICAL_POSITION, position); | |
// save horizontal position | |
bar = scrollable.getHorizontalBar(); | |
position = bar != null ? bar.getSelection() : 0; | |
memento.putInteger(TAG_HORIZONTAL_POSITION, position); | |
} | |
private int[] restoreColumnOrder(IMemento memento) { | |
if (memento == null) { | |
return null; | |
} | |
IMemento children[] = memento.getChildren(TAG_COLUMN_ORDER); | |
if (children != null) { | |
int n = children.length; | |
int[] values = new int[n]; | |
for (int i = 0; i < n; i++) { | |
Integer val = children[i].getInteger(TAG_COLUMN_ORDER_INDEX); | |
if (val != null) { | |
values[i] = val.intValue(); | |
} else { | |
// invalid entry so use default column order | |
return null; | |
} | |
} | |
return values; | |
} | |
return null; | |
} | |
private int restoreVerticalScrollBarPosition(IMemento memento) { | |
if (memento == null) { | |
return 0; | |
} | |
Integer position = memento.getInteger(TAG_VERTICAL_POSITION); | |
return position == null ? 0 : position.intValue(); | |
} | |
private int restoreHorizontalScrollBarPosition(IMemento memento) { | |
if (memento == null) { | |
return 0; | |
} | |
Integer position = memento.getInteger(TAG_HORIZONTAL_POSITION); | |
return position == null ? 0 : position.intValue(); | |
} | |
/** | |
* Get the IWorkbenchSiteProgressService for the receiver. | |
* | |
* @return IWorkbenchSiteProgressService or <code>null</code>. | |
*/ | |
protected IWorkbenchSiteProgressService getProgressService() { | |
IWorkbenchSiteProgressService service = null; | |
Object siteService = getSite().getAdapter(IWorkbenchSiteProgressService.class); | |
if (siteService != null) { | |
service = (IWorkbenchSiteProgressService) siteService; | |
} | |
return service; | |
} | |
/** | |
* Set the filters action. | |
* | |
* @param action | |
*/ | |
void setFilterAction(FiltersAction action) { | |
filtersAction = action; | |
} | |
/** | |
* Return the filter action for the receiver. | |
* | |
* @return IAction | |
*/ | |
IAction getFilterAction() { | |
return filtersAction; | |
} | |
/** | |
* Return the preferences action. | |
* | |
* @return IAction | |
*/ | |
IAction getPreferencesAction() { | |
return preferencesAction; | |
} | |
/** | |
* Set the preferences action. | |
* | |
* @param preferencesAction | |
*/ | |
void setPreferencesAction(ViewPreferencesAction preferencesAction) { | |
this.preferencesAction = preferencesAction; | |
} | |
/** | |
* Get the content provider | |
* | |
* @return MarkerTreeContentProvider | |
*/ | |
MarkerTreeContentProvider getContentProvider() { | |
return contentProvider; | |
} | |
/** | |
* Return the input to the viewer. | |
* | |
* @return Object | |
*/ | |
public Object getViewerInput() { | |
return getViewer().getInput(); | |
} | |
/* | |
* (non-Javadoc) | |
* @see org.eclipse.ui.views.markers.internal.TableView#setSortIndicators() | |
*/ | |
void setSortIndicators() { | |
IField top = getTableSorter().getTopField(); | |
TreeColumn[] columns = getViewer().getTree().getColumns(); | |
for (TreeColumn column : columns) { | |
if (column.getData().equals(top)) { | |
updateDirectionIndicator(column); | |
return; | |
} | |
} | |
} | |
/** | |
* Update the direction indicator as column is now the primary column. | |
* | |
* @param column | |
*/ | |
void updateDirectionIndicator(TreeColumn column) { | |
getViewer().getTree().setSortColumn(column); | |
if (getTableSorter().getTopPriorityDirection() == TableComparator.ASCENDING) { | |
getViewer().getTree().setSortDirection(SWT.UP); | |
} else { | |
getViewer().getTree().setSortDirection(SWT.DOWN); | |
} | |
} | |
/** | |
* Set the selection of the receiver. | |
* | |
* @param selection | |
*/ | |
protected void setSelection(IStructuredSelection selection) { | |
selectionProvider.setSelection(selection); | |
} | |
} |