diff options
author | Robin Stocker | 2012-07-25 21:06:23 +0000 |
---|---|---|
committer | Robin Stocker | 2012-08-28 15:14:40 +0000 |
commit | 37160e084363d3156e76f53811b2d1687a68d563 (patch) | |
tree | e994089fa36d9aca80dedbc7b77260ba7d780dbf | |
parent | c6a7ffee2b6a0f9298f733bf4987d0a9e75d4e80 (diff) | |
download | egit-37160e084363d3156e76f53811b2d1687a68d563.tar.gz egit-37160e084363d3156e76f53811b2d1687a68d563.tar.xz egit-37160e084363d3156e76f53811b2d1687a68d563.zip |
Show problem decorations in staging view and commit dialog
This makes errors/warnings on the files visible while committing.
Before, these could only be noticed in other places before committing
(e.g. the package explorer).
In an earlier version of this change, IDecoratorManager was used. The
problem with that was that it could be turned off in the preferences and
that updating behavior was not correct.
So in this version, a plain ILabelDecorator is used and the decorations
are updated using a IResourceChangeListener.
Bug: 382755
Change-Id: I142a5f20b35f98675b90170dc1328ea637468c06
Signed-off-by: Robin Stocker <robin@nibor.org>
6 files changed, 270 insertions, 15 deletions
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java index f7ad02095a..65beef52ae 100644 --- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java +++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java @@ -18,6 +18,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.jgit.lib.Repository; @@ -42,6 +43,24 @@ public class ResourceUtil { } /** + * Get the {@link IFile} corresponding to the arguments, using + * {@link IWorkspaceRoot#getFileForLocation(org.eclipse.core.runtime.IPath)} + * . + * + * @param repository + * the repository of the file + * @param repoRelativePath + * the repository-relative path of the file to search for + * @return the IFile corresponding to this path, or null + */ + public static IFile getFileForLocation(Repository repository, + String repoRelativePath) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath path = new Path(repository.getWorkTree().getAbsolutePath()).append(repoRelativePath); + return root.getFileForLocation(path); + } + + /** * The method splits the given resources by their repository. For each * occurring repository a list is built containing the repository relative * paths of the related resources. diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IProblemDecoratable.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IProblemDecoratable.java new file mode 100644 index 0000000000..3818966f7c --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IProblemDecoratable.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (C) 2012, Robin Stocker <robin@nibor.org> + * + * 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 + *******************************************************************************/ +package org.eclipse.egit.ui.internal.decorators; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; + +/** + * Interface which decoratable element classes must implement to be decorated by + * the {@link ProblemLabelDecorator}. + */ +public interface IProblemDecoratable { + + /** + * Indication for no problem. + */ + public static int SEVERITY_NONE = -1; + + /** + * Should return the problem severity of the decoratable element, e.g. + * {@link IMarker#SEVERITY_ERROR}. Return + * {@link IProblemDecoratable#SEVERITY_NONE} for no problem. + * <p> + * Implementation can use + * {@link IResource#findMaxProblemSeverity(String, boolean, int)} for + * resources. + * + * @return problem severity + */ + int getProblemSeverity(); +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/ProblemLabelDecorator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/ProblemLabelDecorator.java new file mode 100644 index 0000000000..cf146c74b0 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/ProblemLabelDecorator.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2012, Robin Stocker <robin@nibor.org> + * + * 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 + *******************************************************************************/ +package org.eclipse.egit.ui.internal.decorators; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.egit.core.AdapterUtils; +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.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.DecorationOverlayIcon; +import org.eclipse.jface.viewers.IDecoration; +import org.eclipse.jface.viewers.ILabelDecorator; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.team.ui.ISharedImages; +import org.eclipse.team.ui.TeamImages; + +/** + * Label decorator for warning/error problem markers (used in Staging View and + * Commit Dialog). + * <p> + * Users must make the decoratable element implement {@link IProblemDecoratable}. + */ +public class ProblemLabelDecorator extends BaseLabelProvider implements + ILabelDecorator, IResourceChangeListener { + + private final StructuredViewer viewer; + + private final ResourceManager resourceManager = new LocalResourceManager( + JFaceResources.getResources()); + + /** + * @param viewer + * the viewer to use for label updates because of changed + * resources, or null for none + */ + public ProblemLabelDecorator(StructuredViewer viewer) { + this.viewer = viewer; + if (this.viewer != null) + ResourcesPlugin.getWorkspace().addResourceChangeListener(this); + } + + public void dispose() { + resourceManager.dispose(); + if (this.viewer != null) + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + super.dispose(); + } + + public Image decorateImage(Image image, Object element) { + IProblemDecoratable decoratable = getProblemDecoratable(element); + if (decoratable != null) { + int problemSeverity = decoratable.getProblemSeverity(); + if (problemSeverity == IMarker.SEVERITY_ERROR) + return getDecoratedImage(image, ISharedImages.IMG_ERROR_OVR); + else if (problemSeverity == IMarker.SEVERITY_WARNING) + return getDecoratedImage(image, ISharedImages.IMG_WARNING_OVR); + } + return null; + } + + public String decorateText(String text, Object element) { + // No decoration + return null; + } + + private IProblemDecoratable getProblemDecoratable(Object element) { + if (element instanceof IProblemDecoratable) + return (IProblemDecoratable) element; + else + return null; + } + + private Image getDecoratedImage(Image base, String teamImageId) { + ImageDescriptor overlay = TeamImages.getImageDescriptor(teamImageId); + DecorationOverlayIcon decorated = new DecorationOverlayIcon(base, + overlay, IDecoration.BOTTOM_LEFT); + return (Image) this.resourceManager.get(decorated); + } + + public void resourceChanged(IResourceChangeEvent event) { + Set<IResource> resources = new HashSet<IResource>(); + + IMarkerDelta[] markerDeltas = event.findMarkerDeltas(IMarker.PROBLEM, + true); + for (IMarkerDelta delta : markerDeltas) + resources.add(delta.getResource()); + + if (!resources.isEmpty()) + updateLabels(resources); + } + + private void updateLabels(Set<IResource> changedResources) { + List<Object> elements = getAffectedElements(changedResources); + if (!elements.isEmpty()) { + final Object[] updateElements = elements.toArray(new Object[elements.size()]); + Display display = viewer.getControl().getDisplay(); + display.asyncExec(new Runnable() { + public void run() { + viewer.update(updateElements, null); + } + }); + } + } + + private List<Object> getAffectedElements(Set<IResource> resources) { + List<Object> result = new ArrayList<Object>(); + if (viewer.getContentProvider() instanceof IStructuredContentProvider) { + IStructuredContentProvider contentProvider = (IStructuredContentProvider) viewer.getContentProvider(); + Object[] elements = contentProvider.getElements(null); + for (Object element : elements) { + IResource resource = AdapterUtils.adapt(element, IResource.class); + if (resource != null && resources.contains(resource)) + result.add(element); + } + } + return result; + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitDialog.java index 62dc919da0..5e57d4685c 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitDialog.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CommitDialog.java @@ -26,11 +26,15 @@ import java.util.Iterator; import java.util.Set; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Path; import org.eclipse.egit.core.Activator; import org.eclipse.egit.core.AdaptableFileTreeIterator; +import org.eclipse.egit.core.internal.util.ResourceUtil; import org.eclipse.egit.ui.UIIcons; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.UIText; @@ -39,6 +43,8 @@ import org.eclipse.egit.ui.internal.CompareUtils; import org.eclipse.egit.ui.internal.commit.CommitHelper; import org.eclipse.egit.ui.internal.commit.CommitMessageHistory; import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor; +import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable; +import org.eclipse.egit.ui.internal.decorators.ProblemLabelDecorator; import org.eclipse.egit.ui.internal.dialogs.CommitItem.Status; import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponent.CommitStatus; import org.eclipse.jface.dialogs.IDialogConstants; @@ -57,14 +63,19 @@ import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; import org.eclipse.jface.viewers.DecorationOverlayIcon; +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; @@ -121,7 +132,8 @@ public class CommitDialog extends TitleAreaDialog { return org.eclipse.egit.ui.Activator.getDefault().getPreferenceStore(); } - static class CommitStatusLabelProvider extends ColumnLabelProvider { + static class CommitStatusLabelProvider extends BaseLabelProvider implements + IStyledLabelProvider { private Image DEFAULT = PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_OBJ_FILE); @@ -151,8 +163,8 @@ public class CommitDialog extends TitleAreaDialog { return (Image) this.resourceManager.get(decorated); } - public String getText(Object obj) { - return ""; //$NON-NLS-1$ + public StyledString getStyledText(Object element) { + return new StyledString(); } public Image getImage(Object element) { @@ -178,16 +190,12 @@ public class CommitDialog extends TitleAreaDialog { decorator) : getEditorImage(item); } - public String getToolTipText(Object element) { - return ((CommitItem) element).status.getText(); - } - + @Override public void dispose() { SUBMODULE.dispose(); resourceManager.dispose(); super.dispose(); } - } static class CommitPathLabelProvider extends ColumnLabelProvider { @@ -413,6 +421,7 @@ public class CommitDialog extends TitleAreaDialog { item.status = getFileStatus(path, indexDiff); item.submodule = FileMode.GITLINK == indexDiff.getIndexMode(path); item.path = path; + item.problemSeverity = getProblemSeverity(repository, path); items.add(item); } @@ -784,7 +793,7 @@ public class CommitDialog extends TitleAreaDialog { filesViewer = new CheckboxTableViewer(resourcesTable); new TableViewerColumn(filesViewer, statCol) - .setLabelProvider(new CommitStatusLabelProvider()); + .setLabelProvider(createStatusLabelProvider()); new TableViewerColumn(filesViewer, resourceCol) .setLabelProvider(new CommitPathLabelProvider()); ColumnViewerToolTipSupport.enableFor(filesViewer); @@ -925,6 +934,17 @@ public class CommitDialog extends TitleAreaDialog { return container; } + private static CellLabelProvider createStatusLabelProvider() { + CommitStatusLabelProvider baseProvider = new CommitStatusLabelProvider(); + ProblemLabelDecorator decorator = new ProblemLabelDecorator(null); + return new DecoratingStyledCellLabelProvider(baseProvider, decorator, null) { + @Override + public String getToolTipText(Object element) { + return ((CommitItem) element).status.getText(); + } + }; + } + private void updateMessage() { String message = null; int type = IMessageProvider.NONE; @@ -1069,6 +1089,19 @@ public class CommitDialog extends TitleAreaDialog { return Status.UNKNOWN; } + private static int getProblemSeverity(Repository repository, String path) { + IFile file = ResourceUtil.getFileForLocation(repository, path); + if (file != null) { + try { + int severity = file.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ONE); + return severity; + } catch (CoreException e) { + // Fall back to below + } + } + return IProblemDecoratable.SEVERITY_NONE; + } + @Override protected void okPressed() { if (!isCommitWithoutFilesAllowed()) { @@ -1113,13 +1146,20 @@ public class CommitDialog extends TitleAreaDialog { } } -class CommitItem { +class CommitItem implements IProblemDecoratable { + Status status; String path; boolean submodule; + int problemSeverity; + + public int getProblemSeverity() { + return problemSeverity; + } + /** The ordinal of this {@link Enum} is used to provide the "native" sorting of the list */ public static enum Status { /** */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java index 5717d10286..a1f2cf2fd3 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingEntry.java @@ -14,19 +14,22 @@ import java.util.EnumSet; import java.util.Set; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable; import org.eclipse.jgit.lib.Repository; /** * A staged/unstaged entry in the table */ -public class StagingEntry implements IAdaptable { +public class StagingEntry implements IAdaptable, IProblemDecoratable { /** * State of the node */ @@ -161,6 +164,19 @@ public class StagingEntry implements IAdaptable { return absolutePath; } + public int getProblemSeverity() { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IFile file = root.getFileForLocation(getLocation()); + if (file == null) + return SEVERITY_NONE; + + try { + return file.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ONE); + } catch (CoreException e) { + return SEVERITY_NONE; + } + } + public Object getAdapter(Class adapter) { if (adapter == IResource.class) { return getFile(); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java index 404e44d097..c060fd3a4d 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/staging/StagingView.java @@ -55,6 +55,7 @@ import org.eclipse.egit.ui.internal.commit.CommitMessageHistory; import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor; import org.eclipse.egit.ui.internal.commit.CommitUI; import org.eclipse.egit.ui.internal.components.ToggleableWarningLabel; +import org.eclipse.egit.ui.internal.decorators.ProblemLabelDecorator; import org.eclipse.egit.ui.internal.dialogs.CommitMessageArea; import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponent; import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponentState; @@ -77,6 +78,7 @@ import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.IBaseLabelProvider; @@ -346,7 +348,7 @@ public class StagingView extends ViewPart { unstagedTableViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); unstagedTableViewer.getTable().setLinesVisible(true); - unstagedTableViewer.setLabelProvider(createLabelProvider()); + unstagedTableViewer.setLabelProvider(createLabelProvider(unstagedTableViewer)); unstagedTableViewer.setContentProvider(new StagingViewContentProvider( true)); unstagedTableViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY @@ -466,7 +468,7 @@ public class StagingView extends ViewPart { stagedTableViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); stagedTableViewer.getTable().setLinesVisible(true); - stagedTableViewer.setLabelProvider(createLabelProvider()); + stagedTableViewer.setLabelProvider(createLabelProvider(stagedTableViewer)); stagedTableViewer.setContentProvider(new StagingViewContentProvider( false)); stagedTableViewer.addDragSupport( @@ -736,11 +738,13 @@ public class StagingView extends ViewPart { undoRedoActionGroup.fillActionBars(actionBars); } - private IBaseLabelProvider createLabelProvider() { + private IBaseLabelProvider createLabelProvider(TableViewer tableViewer) { StagingViewLabelProvider baseProvider = new StagingViewLabelProvider(); baseProvider.setFileNameMode(getPreferenceStore().getBoolean( UIPreferences.STAGING_VIEW_FILENAME_MODE)); - return new DelegatingStyledCellLabelProvider(baseProvider); + + ProblemLabelDecorator decorator = new ProblemLabelDecorator(tableViewer); + return new DecoratingStyledCellLabelProvider(baseProvider, decorator, null); } private IPreferenceStore getPreferenceStore() { |