| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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: |
| * IBM Corporation - initial API and implementation |
| * Sebastian Davids <sdavids@gmx.de> - 26823 [Markers] cannot reorder columns in task list |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.views.markers.internal; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.Arrays; |
| |
| 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.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. |
| */ |
| 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) |
| */ |
| public void createPartControl(Composite parent) { |
| parent.setLayout(new FillLayout()); |
| |
| viewer = new TreeViewer(createTree(parent)); |
| createColumns(viewer.getTree()); |
| |
| contentProvider = new MarkerTreeContentProvider(); |
| |
| viewer.setContentProvider(contentProvider); |
| |
| setLabelProviders(); |
| |
| viewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| IStructuredSelection selection = (IStructuredSelection) event |
| .getSelection(); |
| viewerSelectionChanged(selection); |
| } |
| }); |
| |
| viewer.setComparator(buildComparator()); |
| setSortIndicators(); |
| |
| // 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() { |
| public void open(OpenEvent event) { |
| handleOpenEvent(event); |
| } |
| }); |
| viewer.getControl().addKeyListener(new KeyAdapter() { |
| public void keyPressed(KeyEvent e) { |
| handleKeyPressed(e); |
| } |
| }); |
| } |
| |
| /** |
| * Set the label providers for the columns. |
| */ |
| private void setLabelProviders() { |
| |
| IField[] fields = getAllFields(); |
| for (int i = 0; i < fields.length; i++) { |
| IField field = fields[i]; |
| viewer.getViewerColumn(i).setLabelProvider(new MarkerViewLabelProvider(field)); |
| } |
| |
| } |
| |
| /** |
| * Create the viewer input for the receiver. |
| * |
| * @return Object |
| */ |
| abstract Object createViewerInput(); |
| |
| /** |
| * Set the comparator to be the new comparator. |
| * |
| * @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++) { |
| int width = defaultData[i].width; |
| ColumnPixelData defaultPixelData = defaultData[i]; |
| |
| // 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(); |
| } |
| } |
| } |
| |
| result[i] = new ColumnPixelData(width, defaultPixelData.resizable, |
| defaultPixelData.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); |
| tc.setText(fields[i].getColumnHeaderText()); |
| tc.setImage(fields[i].getColumnHeaderImage()); |
| tc.setResizable(columnWidths[i].resizable); |
| tc.setMoveable(true); |
| tc.addSelectionListener(getHeaderListener()); |
| tc.setData(fields[i]); |
| } |
| |
| 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() { |
| 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() |
| */ |
| public void setFocus() { |
| Viewer viewer = getViewer(); |
| if (viewer != null && !viewer.getControl().isDisposed()) { |
| |
| viewer.getControl().setFocus(); |
| } |
| } |
| |
| /** |
| * Build a comparator from the default settings. |
| * |
| * @return TableSorter |
| */ |
| protected ViewerComparator buildComparator() { |
| |
| IField[] sortingFields = getSortingFields(); |
| int[] priorities = new int[sortingFields.length]; |
| int[] directions = new int[sortingFields.length]; |
| for (int i = 0; i < sortingFields.length; i++) { |
| priorities[i] = i; |
| } |
| Arrays.fill(directions, TableComparator.ASCENDING); |
| TableComparator sorter = new TableComparator(sortingFields, priorities, |
| directions); |
| 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. |
| */ |
| 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() |
| */ |
| public void run() { |
| resortTable(column, field, |
| new NullProgressMonitor()); |
| |
| } |
| }); |
| else |
| getProgressService().busyCursorWhile( |
| new IRunnableWithProgress() { |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| 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() |
| */ |
| 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) |
| */ |
| 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 i = 0; i < columnOrder.length; i++) { |
| IMemento child = memento.createChild(TAG_COLUMN_ORDER); |
| child.putInteger(TAG_COLUMN_ORDER_INDEX, columnOrder[i]); |
| } |
| // 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 (int i = 0; i < columns.length; i++) { |
| TreeColumn column = columns[i]; |
| 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); |
| } |
| } |