/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.internal.problemsview; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.actions.InstrumentedAction; import com.google.dart.tools.ui.instrumentation.UIInstrumentation; import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder; import com.google.dart.tools.ui.internal.util.SWTUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; 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.jface.action.Action; import org.eclipse.jface.action.GroupMarker; 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.action.Separator; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.DecorationOverlayIcon; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IMarkerResolution; import org.eclipse.ui.IMarkerResolution2; import org.eclipse.ui.IMemento; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.SelectionProviderAction; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.IWorkbenchSiteProgressService; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * This view is a replacement for the default Problems view. It has much less UI surface area, and * actively sorts the problems and warnings so the most import one shows up first in the list. */ @SuppressWarnings("restriction") public class ProblemsView extends ViewPart implements MarkersChangeService.MarkerChangeListener { static class TypeLabelProvider extends ColumnLabelProvider { @Override public String getText(Object element) { if (element instanceof IMarker) { return getMarkerTypeLabel((IMarker) element); } else { return ""; } } } private class CopyMarkerAction extends SelectionProviderAction { public CopyMarkerAction() { super(tableViewer, "&Copy"); setEnabled(false); setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY); setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor( ISharedImages.IMG_TOOL_COPY)); setDisabledImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor( ISharedImages.IMG_TOOL_COPY_DISABLED)); } @Override public void run() { UIInstrumentationBuilder instrumentation = UIInstrumentation.builder("ProblemsView.CopyMarkerAction"); try { StringBuilder builder = new StringBuilder(); for (Object obj : getStructuredSelection().toArray()) { if (obj instanceof IMarker) { IMarker marker = (IMarker) obj; if (builder.length() > 0) { builder.append("\n"); } try { builder.append(marker.getAttribute(IMarker.MESSAGE)); } catch (CoreException e) { } } } instrumentation.metric("text-length", builder.length()); if (builder.length() > 0) { instrumentation.data("text", builder.toString()); copyToClipboard(builder.toString()); } } catch (RuntimeException e) { instrumentation.metric("Exception", e.getClass().toString()); instrumentation.data("Exception", e.toString()); throw e; } finally { instrumentation.log(); } } @Override public void selectionChanged(ISelection selection) { setEnabled(!selection.isEmpty()); } @Override public void selectionChanged(IStructuredSelection selection) { setEnabled(!selection.isEmpty()); } private void copyToClipboard(String str) { clipboard.setContents(new Object[] {str}, new Transfer[] {TextTransfer.getInstance()}); } } private static class CorrectionLabelProvider extends ColumnLabelProvider { @Override public String getText(Object element) { if (element instanceof IMarker) { IMarker marker = (IMarker) element; return marker.getAttribute(DartCore.MARKER_ATTR_CORRECTION, null); } else { return super.getText(element); } } } private static class DescriptionLabelProvider extends ColumnLabelProvider { @Override public Image getImage(Object element) { if (element instanceof IMarker) { IMarker marker = (IMarker) element; if (marker != null && marker.exists()) { Image image = AnnotationTypesExtManager.getModel().getImageForMarker(marker); if (image != null) { image = decorateImage(marker, image); } else { try { image = WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider().getImage(marker); } catch (Throwable t) { } } return image; } } return null; } // @Override // public Font getFont(Object element) { // return null; // } @Override public String getText(Object element) { if (element instanceof IMarker) { IMarker marker = (IMarker) element; return marker.getAttribute(IMarker.MESSAGE, null); } else { return super.getText(element); } } private Image decorateImage(IMarker marker, Image image) { if (image == null) { return null; } if (IDE.getMarkerHelpRegistry().hasResolutions(marker)) { ImageDescriptor[] descriptors = new ImageDescriptor[5]; descriptors[IDecoration.BOTTOM_RIGHT] = DartToolsPlugin.getBundledImageDescriptor("icons/full/ovr16/contassist_ovr.gif"); image = getImageManager().createImage(new DecorationOverlayIcon(image, descriptors)); } return image; } } private class ErrorViewerFilter extends ViewerFilter { public ErrorViewerFilter() { } @Override public Object[] filter(Viewer viewer, Object parent, Object[] elements) { preFilter(); List<IMarker> results = new ArrayList<IMarker>(); for (Object object : elements) { if (object instanceof IMarker) { if (select(viewer, parent, object)) { results.add((IMarker) object); } } } postFilter(results); return results.toArray(); } @Override public boolean select(Viewer viewer, Object parentElement, Object element) { IMarker marker = (IMarker) element; if (!showHintsAction.isChecked()) { if (marker.getAttribute(IMarker.PRIORITY, IMarker.PRIORITY_NORMAL) == IMarker.PRIORITY_HIGH && marker.getAttribute(IMarker.SEVERITY, 0) == IMarker.SEVERITY_INFO) { return false; } } if (!showInfosAction.isChecked()) { if (marker.getAttribute(IMarker.PRIORITY, IMarker.PRIORITY_NORMAL) == IMarker.PRIORITY_NORMAL && marker.getAttribute(IMarker.SEVERITY, 0) == IMarker.SEVERITY_INFO) { return false; } } if (focusOnProjectAction.isChecked()) { IResource resource = marker.getResource(); if (resource instanceof IWorkspaceRoot) { return true; // markers on the root should always show } if (focusedProject != null) { return isChildOf(focusedProject, resource); } else { return false; } } return true; } protected void preFilter() { } private boolean isChildOf(IProject project, IResource resource) { if (resource == null) { return false; } else if (resource instanceof IProject) { IProject other = (IProject) resource; return project.equals(other); } else { return isChildOf(project, resource.getParent()); } } private void postFilter(List<IMarker> results) { updateContentDescription(results); } } private static class ErrorViewTreeContentProvider implements ITreeContentProvider { private List<IMarker> markers = new ArrayList<IMarker>(); private static final Object[] EMPTY_ARRAY = new Object[0]; @Override public void dispose() { } @Override public Object[] getChildren(Object parentElement) { return EMPTY_ARRAY; } @Override public Object[] getElements(Object inputElement) { List<Object> list = new ArrayList<Object>(); list.addAll(markers); return list.toArray(); } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { return false; } @Override @SuppressWarnings("unchecked") public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { markers = (List<IMarker>) newInput; } } private static class FileNameLabelProvider extends DelegatingStyledCellLabelProvider { public FileNameLabelProvider() { super(new FileNameStyledLabelProvider()); } } private static class FileNameStyledLabelProvider extends LabelProvider implements IStyledLabelProvider, IColorProvider { @Override public Color getBackground(Object element) { return null; } @Override public Color getForeground(Object element) { return null; } @Override public StyledString getStyledText(Object element) { StyledString str = new StyledString(); if (element instanceof IMarker) { IMarker marker = (IMarker) element; IResource resource = marker.getResource(); if (resource != null) { String name = resource.getName(); if (resource instanceof IWorkspaceRoot) { name = "Workspace"; } if (name == null) { name = ""; } str.append(name); if (name.length() > 0) { int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, -1); if (lineNumber != -1) { String num = NumberFormat.getIntegerInstance().format(lineNumber); str.append(" [line " + num + "]", StyledString.DECORATIONS_STYLER); } } } } return str; } } private class FocusOnProjectAction extends InstrumentedAction { public FocusOnProjectAction() { super("Focus on current project", AS_CHECK_BOX); setImageDescriptor(DartToolsPlugin.getBundledImageDescriptor("icons/full/eview16/filter_history.gif")); // restore state setChecked(getMementoBoolean("focusOnProject", true)); } @Override protected void doRun(Event event, UIInstrumentationBuilder instrumentation) { updateFilters(); } } private class GoToMarkerAction extends SelectionProviderAction { private static final String GOINTO_RESOURCE_IMG_PATH = "elcl16/gotoobj_tsk.gif"; //$NON-NLS-1$ private static final String GOINTO_RESOURCE_DISABLED_IMG_PATH = "dlcl16/gotoobj_tsk.gif"; //$NON-NLS-1$ public GoToMarkerAction() { super(tableViewer, "Go to"); setEnabled(false); setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_GO_INTO); setImageDescriptor(IDEWorkbenchPlugin.getIDEImageDescriptor(GOINTO_RESOURCE_IMG_PATH)); setDisabledImageDescriptor(IDEWorkbenchPlugin.getIDEImageDescriptor(GOINTO_RESOURCE_DISABLED_IMG_PATH)); } @Override public void run() { UIInstrumentationBuilder instrumentation = UIInstrumentation.builder("GoToMarkerAction.run"); try { // This will record the selection, the action needs seperate instrumetnations so we know // that it came from the GotoMarker action. openSelectedMarker(instrumentation); } catch (RuntimeException e) { instrumentation.metric("Exception", e.getClass().toString()); instrumentation.data("Exception", e.toString()); throw e; } finally { instrumentation.log(); } } @Override public void selectionChanged(ISelection selection) { setEnabled(!selection.isEmpty()); } @Override public void selectionChanged(IStructuredSelection selection) { setEnabled(!selection.isEmpty() && selection.size() == 1); } } private class MarkersRefreshJob extends WorkspaceJob { private final Display display; MarkersRefreshJob(Display display) { super("Refresh Problems"); this.display = display; setSystem(true); } @Override public boolean belongsTo(Object family) { return family == REFRESH_MARKERS_JOB_FAMILY; } @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { monitor.beginTask("Refresh problems", IProgressMonitor.UNKNOWN); try { List<IMarker> markers = new ArrayList<IMarker>(); for (String markerId : MarkersUtils.getInstance().getErrorsViewMarkerIds()) { IMarker[] marks = ResourcesPlugin.getWorkspace().getRoot().findMarkers( markerId, true, IResource.DEPTH_INFINITE); markers.addAll(Arrays.asList(marks)); } showMarkers(display, markers); } catch (CoreException ce) { DartToolsPlugin.log(ce); } finally { if (rescheduleJob) { rescheduleJob = false; schedule(250); } else { refreshJob = null; } } monitor.done(); return Status.OK_STATUS; } } private final class PageSelectionListener implements ISelectionListener { @Override public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (part != ProblemsView.this) { focusOn(part, selection); } } } private class ShowHintsAction extends InstrumentedAction { public ShowHintsAction() { super("Show hints", AS_CHECK_BOX); setImageDescriptor(DartToolsPlugin.getBundledImageDescriptor("icons/full/misc/info.png")); setChecked(getMementoBoolean("showHints", true)); } @Override protected void doRun(Event event, UIInstrumentationBuilder instrumentation) { updateFilters(); } } private class ShowInfosAction extends InstrumentedAction { public ShowInfosAction() { super("Show tasks", AS_CHECK_BOX); setImageDescriptor(DartToolsPlugin.getBundledImageDescriptor("icons/full/eview16/tasks_tsk.gif")); setChecked(getMementoBoolean("showInfos", false)); } @Override protected void doRun(Event event, UIInstrumentationBuilder instrumentation) { updateFilters(); } } private static class TableSorter extends ViewerSorter { private static final int DESCENDING = 1; private int sortColumn; private int direction; public TableSorter() { direction = DESCENDING; } @Override public int compare(Viewer viewer, Object e1, Object e2) { if (direction == DESCENDING) { return compareMarkers((TableSorterMarker) e1, (TableSorterMarker) e2); } else { return -1 * compareMarkers((TableSorterMarker) e1, (TableSorterMarker) e2); } } public void setColumn(int column) { if (sortColumn == column) { direction = -1 * direction; } else { sortColumn = column; direction = DESCENDING; } } @Override public void sort(final Viewer viewer, Object[] elements) { // We cache views on the markers and sort those, in order to protect against concurrent // modifications to the marker information. TableSorterMarker[] sortables = new TableSorterMarker[elements.length]; for (int i = 0; i < sortables.length; i++) { sortables[i] = new TableSorterMarker((IMarker) elements[i]); } try { Arrays.sort(sortables, new Comparator<TableSorterMarker>() { @Override public int compare(TableSorterMarker marker1, TableSorterMarker marker2) { return TableSorter.this.compare(viewer, marker1, marker2); } }); } catch (Throwable t) { // catch all exceptions having to do with sorting DartCore.logError(t.toString()); } for (int i = 0; i < sortables.length; i++) { elements[i] = sortables[i].marker; } } private final int compareLineNumber(TableSorterMarker marker1, TableSorterMarker marker2) { return marker1.line - marker2.line; } private final int compareMarkers(TableSorterMarker marker1, TableSorterMarker marker2) { // sort by severity // then by resource name // then by line number // then by problem description if (marker1 == null || marker2 == null || !marker1.exists || !marker2.exists) { return 0; } int val = 0; if (sortColumn == 0) { val = compareSeverity(marker1, marker2); if (val != 0) { return val; } val = compareProblemDescription(marker1, marker2); if (val != 0) { return val; } val = compareResourceName(marker1, marker2); if (val != 0) { return val; } return compareLineNumber(marker1, marker2); } else if (sortColumn == 1) { val = compareSeverity(marker1, marker2); if (val != 0) { return val; } val = compareResourceName(marker1, marker2); if (val != 0) { return val; } val = compareLineNumber(marker1, marker2); if (val != 0) { return val; } return compareProblemDescription(marker1, marker2); } else { val = compareType(marker1, marker2); if (val != 0) { return val; } val = compareSeverity(marker1, marker2); if (val != 0) { return val; } val = compareResourceName(marker1, marker2); if (val != 0) { return val; } val = compareLineNumber(marker1, marker2); if (val != 0) { return val; } return compareProblemDescription(marker1, marker2); } } private final int compareProblemDescription(TableSorterMarker marker1, TableSorterMarker marker2) { return marker1.description.compareToIgnoreCase(marker2.description); } private final int compareResourceName(TableSorterMarker marker1, TableSorterMarker marker2) { return marker1.resourceName.compareToIgnoreCase(marker2.resourceName); } private final int compareSeverity(TableSorterMarker marker1, TableSorterMarker marker2) { if (marker1.severity == marker2.severity) { return marker2.priority - marker1.priority; } // This order (sev2 - sev1) is deliberate. return marker2.severity - marker1.severity; } private final int compareType(TableSorterMarker marker1, TableSorterMarker marker2) { return marker1.description.compareToIgnoreCase(marker2.description); } } private static class TableSorterMarker { public IMarker marker; String resourceName; int line; int severity; int priority; String description; boolean exists; public TableSorterMarker(IMarker marker) { this.marker = marker; this.resourceName = marker.getResource().getName(); this.line = marker.getAttribute(IMarker.LINE_NUMBER, 0); this.severity = marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); this.priority = marker.getAttribute(IMarker.PRIORITY, IMarker.PRIORITY_NORMAL); this.description = marker.getAttribute(IMarker.MESSAGE, ""); this.exists = marker.exists(); } } public static final ILabelProvider DESCRIPTION_LABEL_PROVIDER = new DescriptionLabelProvider(); public static final ILabelProvider CORRECTION_LABEL_PROVIDER = new CorrectionLabelProvider(); private final PageSelectionListener pageSelectionListener = new PageSelectionListener(); private static Object REFRESH_MARKERS_JOB_FAMILY = new Object(); private static ResourceManager resourceManager; private static ResourceManager getImageManager() { if (resourceManager == null) { resourceManager = new LocalResourceManager(JFaceResources.getResources()); } return resourceManager; } @SuppressWarnings("unused") private static String getMarkerToolTipText(IMarker marker) { StringBuilder builder = new StringBuilder(); builder.append(marker.getAttribute(IMarker.MESSAGE, null)); return builder.toString(); } private static String getMarkerTypeLabel(IMarker marker) { try { String typeId = marker.getType(); return MarkersExtManager.getInstance().getLabelforTypeId(typeId); } catch (CoreException ce) { return ""; } } private static Object getSingleSelection(ISelection selection) { if (selection == null || selection.isEmpty()) { return null; } if (selection instanceof IStructuredSelection) { return ((IStructuredSelection) selection).getFirstElement(); } else { return null; } } private static boolean safeEquals(Object o1, Object o2) { if (o1 == o2) { return true; } if (o1 == null || o2 == null) { return false; } return o1.equals(o2); } private IMemento memento; protected TableViewer tableViewer; protected TableSorter tableSorter; private ErrorViewerFilter tableFilter; private IProject focusedProject; private FocusOnProjectAction focusOnProjectAction; private ShowHintsAction showHintsAction; private ShowInfosAction showInfosAction; private Clipboard clipboard; private CopyMarkerAction copyAction; private GoToMarkerAction goToMarkerAction; private Job refreshJob; private boolean rescheduleJob; private Display swtDisplay; private IPreferenceStore preferences; private IPropertyChangeListener propertyChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { doPropertyChange(event); } }; public ProblemsView() { } @Override public void createPartControl(Composite parent) { preferences = DartToolsPlugin.getDefault().getCombinedPreferenceStore(); swtDisplay = parent.getDisplay(); clipboard = new Clipboard(parent.getDisplay()); tableViewer = new TableViewer(parent, SWT.H_SCROLL | SWT.VIRTUAL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION); tableViewer.setContentProvider(new ErrorViewTreeContentProvider()); tableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { UIInstrumentationBuilder instrumentation = UIInstrumentation.builder("ProblemView.doubleClick"); try { openSelectedMarker(instrumentation); } catch (RuntimeException e) { instrumentation.metric("Exception", e.getClass().toString()); instrumentation.data("Exception", e.toString()); throw e; } finally { instrumentation.log(); } } }); tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { updateStatusLine((IStructuredSelection) selection); } } }); final Table table = tableViewer.getTable(); table.setBackgroundMode(SWT.INHERIT_FORCE); table.addListener(SWT.EraseItem, new Listener() { @Override public void handleEvent(Event event) { SWTUtil.eraseSelection(event, table, getPreferences()); } }); // Create actions; must be done after the construction of the tableViewer goToMarkerAction = new GoToMarkerAction(); copyAction = new CopyMarkerAction(); tableViewer.addSelectionChangedListener(copyAction); tableViewer.addSelectionChangedListener(goToMarkerAction); tableSorter = new TableSorter(); tableSorter.setColumn(1); tableViewer.setComparator(tableSorter); tableViewer.getTable().setSortDirection(SWT.UP); TableViewerColumn descriptionColumn = new TableViewerColumn(tableViewer, SWT.LEFT); descriptionColumn.setLabelProvider(new DescriptionLabelProvider()); descriptionColumn.getColumn().setText("Description"); descriptionColumn.getColumn().setWidth(520); descriptionColumn.getColumn().setResizable(true); enableSorting(descriptionColumn.getColumn(), 0); TableViewerColumn fileNameColumn = new TableViewerColumn(tableViewer, SWT.LEFT); fileNameColumn.setLabelProvider(new FileNameLabelProvider()); fileNameColumn.getColumn().setText("Location"); fileNameColumn.getColumn().setWidth(220); fileNameColumn.getColumn().setResizable(true); enableSorting(fileNameColumn.getColumn(), 1); TableViewerColumn correctionColumn = new TableViewerColumn(tableViewer, SWT.LEFT); correctionColumn.setLabelProvider(new CorrectionLabelProvider()); correctionColumn.getColumn().setText("Correction"); correctionColumn.getColumn().setWidth(520); correctionColumn.getColumn().setResizable(true); enableSorting(correctionColumn.getColumn(), 2); tableViewer.getTable().setSortColumn(fileNameColumn.getColumn()); restoreColumnWidths(); table.setLayoutData(new GridData(GridData.FILL_BOTH)); // table.setFont(parent.getFont()); SWTUtil.bindJFaceResourcesFontToControl(table); updateColors(); table.setLinesVisible(true); table.setHeaderVisible(true); table.layout(true); getSite().setSelectionProvider(tableViewer); IToolBarManager toolbar = getViewSite().getActionBars().getToolBarManager(); fillInToolbar(toolbar); registerContextMenu(); tableFilter = new ErrorViewerFilter(); updateFilters(); startUpdateJob(swtDisplay); MarkersChangeService.getService().addListener(this); getPreferences().addPropertyChangeListener(propertyChangeListener); focusOnActiveEditor(); } @Override public void dispose() { if (copyAction != null) { copyAction.dispose(); copyAction = null; } if (clipboard != null) { clipboard.dispose(); clipboard = null; } if (goToMarkerAction != null) { goToMarkerAction.dispose(); goToMarkerAction = null; } if (propertyChangeListener != null) { getPreferences().removePropertyChangeListener(propertyChangeListener); propertyChangeListener = null; } getSite().getPage().removeSelectionListener(pageSelectionListener); MarkersChangeService.getService().removeListener(this); super.dispose(); } @Override public void handleResourceChange() { if (swtDisplay != null) { startUpdateJob(swtDisplay); } } @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site, memento); this.memento = memento; IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService) getViewSite().getAdapter( IWorkbenchSiteProgressService.class); if (progressService != null) { initProgressService(progressService); } getSite().getPage().addSelectionListener(pageSelectionListener); // Reset focused project when it is deleted. ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() { @Override public void resourceChanged(IResourceChangeEvent event) { if (safeEquals(event.getResource(), focusedProject)) { Display display = Display.getDefault(); if (display != null) { display.asyncExec(new Runnable() { @Override public void run() { focusOn(null); } }); } } } }, IResourceChangeEvent.PRE_DELETE); } @Override public void saveState(IMemento memento) { super.saveState(memento); if (showInfosAction != null) { memento.putBoolean("focusOnProject", focusOnProjectAction.isChecked()); memento.putBoolean("showHints", showHintsAction.isChecked()); memento.putBoolean("showInfos", showInfosAction.isChecked()); } StringBuilder builder = new StringBuilder(); for (TableColumn column : tableViewer.getTable().getColumns()) { if (builder.length() > 0) { builder.append(";"); } builder.append(Integer.toString(column.getWidth())); } memento.putString("columnWidths", builder.toString()); } @Override public void setFocus() { tableViewer.getTable().setFocus(); } protected void fillInToolbar(IToolBarManager toolbar) { showHintsAction = new ShowHintsAction(); toolbar.add(showHintsAction); showInfosAction = new ShowInfosAction(); toolbar.add(showInfosAction); focusOnProjectAction = new FocusOnProjectAction(); toolbar.add(focusOnProjectAction); } protected IMemento getMemento() { return memento; } protected boolean getMementoBoolean(String key, boolean defaultValue) { if (getMemento() != null) { Boolean b = getMemento().getBoolean(key); return b == null ? defaultValue : b.booleanValue(); } else { return defaultValue; } } protected String getStatusSummary(IMarker[] markers) { return getStatusSummary(Arrays.asList(markers)); } protected String getStatusSummary(List<IMarker> markers) { return MarkersUtils.getInstance().summarizeMarkers(markers); } protected TableViewer getViewer() { return tableViewer; } protected void initProgressService(IWorkbenchSiteProgressService progressService) { progressService.showBusyForFamily(ResourcesPlugin.FAMILY_MANUAL_BUILD); progressService.showBusyForFamily(ResourcesPlugin.FAMILY_AUTO_BUILD); progressService.showBusyForFamily(REFRESH_MARKERS_JOB_FAMILY); } protected void showMarkers(Display display, final List<IMarker> markers) { for (int i = markers.size() - 1; i >= 0; i--) { if (!markers.get(i).exists()) { markers.remove(i); } } if (tableViewer.getControl() == null || tableViewer.getControl().isDisposed()) { return; } display.asyncExec(new Runnable() { @Override public void run() { if (tableViewer.getControl() != null && !tableViewer.getControl().isDisposed()) { tableViewer.setInput(markers); } } }); } protected void startUpdateJob(final Display display) { if (refreshJob != null) { rescheduleJob = true; } else { refreshJob = new MarkersRefreshJob(display); refreshJob.schedule(250); } } protected void updateColors() { SWTUtil.setColors(getViewer().getTable(), getPreferences()); } protected void updateContentDescription(List<IMarker> markers) { if (markers == null) { markers = new ArrayList<IMarker>(); } String desc = getStatusSummary(markers); if (focusedProject != null && focusOnProjectAction.isChecked()) { desc = "[" + focusedProject.getName() + "] " + desc; } setContentDescription(desc); } private void addActionsForSelection(IMenuManager menuManager) { IStructuredSelection selection = (IStructuredSelection) getViewer().getSelection(); if (selection.size() == 1) { Object element = selection.getFirstElement(); if (!(element instanceof IMarker)) { return; } final IMarker marker = (IMarker) element; IMarkerResolution[] resolutions = IDE.getMarkerHelpRegistry().getResolutions(marker); for (final IMarkerResolution resolution : resolutions) { Action action = new Action(escapeSpecialChars(resolution.getLabel())) { @Override public void run() { resolution.run(marker); } }; if (resolution instanceof IMarkerResolution2) { IMarkerResolution2 resolution2 = (IMarkerResolution2) resolution; Image image = resolution2.getImage(); if (image != null) { action.setImageDescriptor(ImageDescriptor.createFromImage(image)); } } menuManager.add(action); } } } private void doPropertyChange(PropertyChangeEvent event) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { updateColors(); getViewer().refresh(false); } }); } private void enableSorting(final TableColumn column, final int index) { column.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { tableSorter.setColumn(index); int dir = tableViewer.getTable().getSortDirection(); if (tableViewer.getTable().getSortColumn() == column) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { dir = SWT.UP; } tableViewer.getTable().setSortDirection(dir); tableViewer.getTable().setSortColumn(column); tableViewer.refresh(); } }); } private String escapeSpecialChars(String label) { if (label == null) { return ""; } // handle OS X return label.trim().replace("@", ""); } private void fillContextMenu(IMenuManager menuManager) { addActionsForSelection(menuManager); menuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); if (menuManager.getItems().length > 1) { menuManager.add(new Separator()); } if (!getViewer().getSelection().isEmpty()) { menuManager.add(goToMarkerAction); menuManager.add(copyAction); } } private void focusOn(IProject project) { if (!safeEquals(focusedProject, project)) { focusedProject = project; updateFilters(); } } private void focusOn(IWorkbenchPart part, ISelection selection) { Object sel = getSingleSelection(selection); // See if it's a resource if (sel instanceof IResource) { IResource resource = (IResource) sel; focusOn(resource.getProject()); return; } // See if it can be adapted to a resource if (sel instanceof IAdaptable) { IAdaptable adaptable = (IAdaptable) sel; IResource resource = (IResource) adaptable.getAdapter(IResource.class); if (resource != null) { focusOn(resource.getProject()); return; } } // See if the part is an editor. if (part instanceof IEditorPart) { IEditorPart editor = (IEditorPart) part; if (editor.getEditorInput() instanceof IFileEditorInput) { IFileEditorInput input = (IFileEditorInput) editor.getEditorInput(); if (input.getFile() != null) { focusOn(input.getFile().getProject()); } } } } private void focusOnActiveEditor() { IEditorPart part = getViewSite().getPage().getActiveEditor(); if (part == null) { focusOn(getViewSite().getPage().getActivePart(), null); } else { focusOn(part, null); } } @SuppressWarnings("unused") private IEditorPart getEditorFor(IFile file) { IWorkbenchPage page = getViewSite().getPage(); for (IEditorReference editorReference : page.getEditorReferences()) { try { IEditorInput editorInput = editorReference.getEditorInput(); if (editorInput instanceof IFileEditorInput) { IFileEditorInput input = (IFileEditorInput) editorInput; if (file.equals(input.getFile())) { return editorReference.getEditor(true); } } } catch (PartInitException pie) { // ignore } } return null; } private IPreferenceStore getPreferences() { return preferences; } private void openSelectedMarker(UIInstrumentationBuilder instrumentation) { ISelection sel = tableViewer.getSelection(); try { instrumentation.record(sel); if (sel instanceof IStructuredSelection) { Object element = ((IStructuredSelection) sel).getFirstElement(); if (element instanceof IMarker) { IMarker marker = (IMarker) element; if (marker.getResource() instanceof IFile) { try { IDE.openEditor(getViewSite().getPage(), marker); } catch (PartInitException e) { ErrorDialog.openError( getSite().getShell(), "Error Opening Marker", "Unable to open an editor for the given marker: " + e.getClass().getSimpleName(), new Status(IStatus.ERROR, DartToolsPlugin.PLUGIN_ID, e.toString(), e)); DartToolsPlugin.log(e); } } } } } catch (RuntimeException e) { instrumentation.metric("Exception", e.getClass().toString()); instrumentation.data("Exception", e.toString()); throw e; } } private void registerContextMenu() { getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction); MenuManager mm = new MenuManager(); mm.setRemoveAllWhenShown(true); mm.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager mgr) { fillContextMenu(mgr); } }); Viewer viewer = getViewer(); Menu menu = mm.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); // Register menu for extension. getSite().registerContextMenu(mm, viewer); } private void restoreColumnWidths() { if (memento != null) { String str = memento.getString("columnWidths"); if (str != null) { String[] widths = str.split(";"); TableColumn[] columns = tableViewer.getTable().getColumns(); for (int i = 0; i < widths.length; i++) { if (columns.length > i) { try { columns[i].setWidth(Integer.parseInt(widths[i])); } catch (NumberFormatException nfe) { } } } } } } private void updateFilters() { if (tableViewer.getFilters().length == 0) { tableViewer.setFilters(new ViewerFilter[] {tableFilter}); } //tableFilter.setEnabled(filterContentsAction.isChecked()); tableViewer.refresh(); } private void updateStatusLine(IStructuredSelection selection) { String message; if (selection == null || selection.size() == 0) { message = ""; } else if (selection.size() == 1) { Object sel = selection.getFirstElement(); if (sel instanceof IMarker) { IMarker marker = (IMarker) sel; message = marker.getAttribute(IMarker.MESSAGE, ""); } else { message = ""; } } else { List<IMarker> selMarkers = new ArrayList<IMarker>(); for (Object obj : selection.toList()) { if (obj instanceof IMarker) { selMarkers.add((IMarker) obj); } } message = getStatusSummary(selMarkers.toArray(new IMarker[selMarkers.size()])); } getViewSite().getActionBars().getStatusLineManager().setMessage(message); } }