| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.ui.synchronize; |
| |
| import org.eclipse.compare.CompareConfiguration; |
| import org.eclipse.compare.structuremergeviewer.ICompareInput; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.mapping.ModelProvider; |
| import org.eclipse.core.resources.mapping.ResourceMapping; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.DecorationOverlayIcon; |
| import org.eclipse.jface.viewers.IDecoration; |
| import org.eclipse.jface.viewers.IFontProvider; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ILabelProviderListener; |
| import org.eclipse.jface.viewers.TreePath; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.team.core.diff.IDiff; |
| import org.eclipse.team.core.diff.IThreeWayDiff; |
| import org.eclipse.team.core.mapping.ISynchronizationContext; |
| import org.eclipse.team.core.synchronize.SyncInfo; |
| import org.eclipse.team.internal.ui.IPreferenceIds; |
| import org.eclipse.team.internal.ui.TeamUIMessages; |
| import org.eclipse.team.internal.ui.TeamUIPlugin; |
| import org.eclipse.team.internal.ui.Utils; |
| import org.eclipse.team.internal.ui.mapping.ResourceDiffCompareInput; |
| import org.eclipse.team.internal.ui.synchronize.ImageManager; |
| import org.eclipse.team.ui.ISharedImages; |
| import org.eclipse.team.ui.TeamUI; |
| import org.eclipse.team.ui.mapping.ITeamContentProviderDescriptor; |
| import org.eclipse.team.ui.mapping.ITeamContentProviderManager; |
| import org.eclipse.team.ui.mapping.SynchronizationLabelProvider; |
| |
| /** |
| * A label provider wrapper that adds synchronization image and/or text decorations |
| * to the image and label obtained from the delegate provider. |
| * |
| * @since 3.2 |
| */ |
| public abstract class AbstractSynchronizeLabelProvider implements ILabelProvider { |
| |
| private ImageManager localImageManager; |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) |
| */ |
| @Override |
| public Image getImage(Object element) { |
| Image base = getDelegateImage(element); |
| if (isDecorationEnabled() && base != null) { |
| Image decorateImage = decorateImage(base, element); |
| base = decorateImage; |
| } |
| if (isIncludeOverlays() && base != null) { |
| base = addOverlays(base, element); |
| } |
| return base; |
| } |
| |
| /** |
| * Decorate the image with the appropriate diff decorations. |
| * By default, this method uses the diff associated with |
| * the given element to determine how to decorate the image. |
| * It then uses the {@link CompareConfiguration#getImage(Image, int)} |
| * method to apply the decoration to the base image. |
| * @param base the base image to be decorated. |
| * @param element the element |
| * @return the image decorated appropriately using the diff associated with |
| * the element |
| * @see #getDiff(Object) |
| * @see CompareConfiguration#getImage(Image, int) |
| */ |
| protected Image decorateImage(Image base, Object element) { |
| Image decoratedImage; |
| if (element instanceof ICompareInput) { |
| ICompareInput ci = (ICompareInput) element; |
| decoratedImage = getCompareImage(base, ci.getKind()); |
| } else { |
| IDiff node = getDiff(element); |
| decoratedImage = getCompareImage(base, node); |
| } |
| // The reason we still overlay the compare image is to |
| // ensure that the image width for all images shown in the viewer |
| // are consistent. |
| return decoratedImage; |
| } |
| |
| /** |
| * Return the image for the item from the delegate label provider. |
| * @param element the element |
| * @return the image for the item from the delegate label provider |
| */ |
| protected Image getDelegateImage(Object element) { |
| ILabelProvider modelLabelProvider = getDelegateLabelProvider(); |
| Image base = modelLabelProvider.getImage(internalGetElement(element)); |
| if (base == null && element instanceof ModelProvider) { |
| ModelProvider mp = (ModelProvider) element; |
| base = getImageManager().getImage(getImageDescriptor(mp)); |
| } |
| return base; |
| } |
| |
| private ImageDescriptor getImageDescriptor(ModelProvider provider) { |
| ITeamContentProviderManager manager = TeamUI.getTeamContentProviderManager(); |
| ITeamContentProviderDescriptor desc = manager.getDescriptor(provider.getId()); |
| return desc.getImageDescriptor(); |
| } |
| |
| private Object internalGetElement(Object element) { |
| if (element instanceof TreePath) { |
| TreePath tp = (TreePath) element; |
| element = tp.getLastSegment(); |
| } |
| return element; |
| } |
| |
| private Image getCompareImage(Image base, IDiff node) { |
| int compareKind = getCompareKind(node); |
| return getCompareImage(base, compareKind); |
| } |
| |
| /** |
| * Returns an image showing the specified change kind applied to a given base image. |
| * |
| * @nooverride This method is not intended to be re-implemented or extended by clients. |
| * @noreference This method is not intended to be referenced by clients. |
| */ |
| protected Image getCompareImage(Image base, int compareKind) { |
| return getImageManager().getImage(base, compareKind); |
| } |
| |
| private int getCompareKind(IDiff node) { |
| return ResourceDiffCompareInput.getCompareKind(node); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) |
| */ |
| @Override |
| public String getText(Object element) { |
| String base = getDelegateText(element); |
| if (isSyncInfoInTextEnabled()) { |
| return decorateText(base, element); |
| } |
| return base; |
| } |
| |
| /** |
| * Obtain the text for the object from the delegate label provider. |
| * @param element the element |
| * @return the text label for the element |
| */ |
| protected String getDelegateText(Object element) { |
| ILabelProvider modelLabelProvider = getDelegateLabelProvider(); |
| element = internalGetElement(element); |
| String base = modelLabelProvider.getText(element); |
| if (base == null || base.length() == 0) { |
| if (element instanceof ModelProvider) { |
| ModelProvider provider = (ModelProvider) element; |
| base = Utils.getLabel(provider); |
| } |
| } |
| return base; |
| } |
| |
| /** |
| * Decorate the text with the appropriate diff decorations. |
| * By default, this method uses the diff associated with |
| * the given element to determine how to decorate the text. |
| * @param base the base text to be decorated. |
| * @param element the element |
| * @return the text decorated appropriately using the diff associated with |
| * the element |
| * @see #getDiff(Object) |
| */ |
| protected String decorateText(String base, Object element) { |
| IDiff node = getDiff(element); |
| if (node != null && node.getKind() != IDiff.NO_CHANGE) { |
| String syncKindString = node.toDiffString(); |
| return NLS.bind(TeamUIMessages.AbstractSynchronizationLabelProvider_0, new String[] { base, syncKindString }); |
| } |
| return base; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) |
| */ |
| @Override |
| public void addListener(ILabelProviderListener listener) { |
| getDelegateLabelProvider().addListener(listener); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() |
| */ |
| @Override |
| public void dispose() { |
| if (localImageManager != null) |
| localImageManager.dispose(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) |
| */ |
| @Override |
| public boolean isLabelProperty(Object element, String property) { |
| return getDelegateLabelProvider().isLabelProperty(internalGetElement(element), property); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) |
| */ |
| @Override |
| public void removeListener(ILabelProviderListener listener) { |
| getDelegateLabelProvider().removeListener(listener); |
| } |
| |
| /** |
| * Returns whether the synchronization state should be included in the |
| * text of the label. By default, the Team preference is used to determine |
| * what to return. Subclasses may override. |
| * @return whether the synchronization state should be included in the |
| * text of the label |
| */ |
| protected boolean isSyncInfoInTextEnabled() { |
| return isDecorationEnabled() && TeamUIPlugin.getPlugin().getPreferenceStore().getBoolean(IPreferenceIds.SYNCVIEW_VIEW_SYNCINFO_IN_LABEL); |
| } |
| |
| /** |
| * Return the label provider that will return the text and image |
| * appropriate for the given model element. Subclasses are responsible for |
| * disposing of the label provider. |
| * @return the label provider that will return the text and image |
| * appropriate for the given model element |
| */ |
| protected abstract ILabelProvider getDelegateLabelProvider(); |
| |
| /** |
| * Return whether the label provider should decorate with the synchronization state. |
| * @return whether the label provider should decorate with the synchronization state |
| */ |
| protected abstract boolean isDecorationEnabled(); |
| |
| /** |
| * Return the sync kind of the given element. This is used |
| * to determine how to decorate the image and label of the |
| * element. The sync kind is described in the {@link SyncInfo} |
| * class. A <code>null</code> is returned by default. |
| * @param element the element being tested |
| * @return the sync kind of the given element |
| */ |
| protected IDiff getDiff(Object element) { |
| return null; |
| } |
| |
| private Image addOverlays(Image base, Object element) { |
| if (!isIncludeOverlays()) |
| return base; |
| |
| ImageDescriptor[] overlayImages = new ImageDescriptor[4]; |
| boolean hasOverlay = false; |
| |
| // Decorate with the busy indicator |
| if (isBusy(element)) { |
| overlayImages[IDecoration.TOP_LEFT] = TeamUIPlugin.getImageDescriptor(ISharedImages.IMG_HOURGLASS_OVR); |
| hasOverlay = true; |
| } |
| // Decorate with propagated conflicts and problem markers |
| if (!isConflicting(element)) { |
| // if the folder is already conflicting then don't bother propagating |
| if (hasDecendantConflicts(element)) { |
| overlayImages[IDecoration.BOTTOM_RIGHT] = TeamUIPlugin.getImageDescriptor(ISharedImages.IMG_CONFLICT_OVR); |
| hasOverlay = true; |
| } |
| } |
| int severity = getMarkerSeverity(element); |
| if (severity == IMarker.SEVERITY_ERROR) { |
| overlayImages[IDecoration.BOTTOM_LEFT] = TeamUIPlugin.getImageDescriptor(ISharedImages.IMG_ERROR_OVR); |
| hasOverlay = true; |
| } else if (severity == IMarker.SEVERITY_WARNING) { |
| overlayImages[IDecoration.BOTTOM_LEFT] = TeamUIPlugin.getImageDescriptor(ISharedImages.IMG_WARNING_OVR); |
| hasOverlay = true; |
| } |
| if (hasOverlay) { |
| ImageDescriptor overlay = new DecorationOverlayIcon(base, overlayImages, new Point(base.getBounds().width, base.getBounds().height)); |
| return getImageManager().getImage(overlay); |
| } |
| return base; |
| } |
| |
| /** |
| * Indicate whether the overlays provided by this class should be applied. |
| * By default, <code>false</code> is returned. Subclasses may override |
| * and control individual overlays by overriding the appropriate |
| * query methods. Overlays provided by this class include problem marker |
| * severity ({@link #getMarkerSeverity(Object)}), propagated conflicts |
| * ({@link #hasDecendantConflicts(Object)} and busy state ({@link #isBusy(Object)}). |
| * @return whether the overlays provided by this class should be applied |
| */ |
| protected boolean isIncludeOverlays() { |
| return false; |
| } |
| |
| /** |
| * Return the marker severity (one of IMarker.SEVERITY_ERROR or |
| * IMarker.SEVERITY_WARNING) to be overlayed on the given element or -1 if |
| * there are no markers. By Default, the element is adapted to resource |
| * mapping in order to look for markers. |
| * <p> |
| * Although this class handles providing the overlays, it does not react |
| * to marker changes. Subclasses must issue label updates when the markers on |
| * a logical model element change. |
| * |
| * @param element |
| * the element |
| * @return the marker severity |
| */ |
| protected int getMarkerSeverity(Object element) { |
| ResourceMapping mapping = Utils.getResourceMapping(internalGetElement(element)); |
| int result = -1; |
| if (mapping != null) { |
| try { |
| IMarker[] markers = mapping.findMarkers(IMarker.PROBLEM, true, null); |
| for (int i = 0; i < markers.length; i++) { |
| IMarker marker = markers[i]; |
| Integer severity = (Integer) marker.getAttribute(IMarker.SEVERITY); |
| if (severity != null) { |
| if (severity.intValue() == IMarker.SEVERITY_ERROR) { |
| return IMarker.SEVERITY_ERROR; |
| } else if (severity.intValue() == IMarker.SEVERITY_WARNING) { |
| result = IMarker.SEVERITY_WARNING; |
| } |
| } |
| } |
| } catch (CoreException e) { |
| // Ignore |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Return whether the given element has descendant conflicts. |
| * By default, <code>false</code> is returned. Subclasses |
| * may override. |
| * @param element the element |
| * @return whether the given element has descendant conflicts |
| */ |
| protected boolean hasDecendantConflicts(Object element) { |
| return false; |
| } |
| |
| private boolean isConflicting(Object element) { |
| IDiff node = getDiff(element); |
| if (node != null) { |
| if (node instanceof IThreeWayDiff) { |
| IThreeWayDiff twd = (IThreeWayDiff) node; |
| return twd.getDirection() == IThreeWayDiff.CONFLICTING; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Return whether the given element is busy (i.e. is involved |
| * in an operation. By default, <code>false</code> is returned. |
| * Subclasses may override. |
| * @param element the element |
| * @return whether the given element is busy |
| */ |
| protected boolean isBusy(Object element) { |
| return false; |
| } |
| |
| /** |
| * Method that provides a custom font for elements that are |
| * busy. Although this label provider does not implement |
| * {@link IFontProvider}, subclasses that wish to get |
| * busy indication using a font can do so. |
| * @param element the element |
| * @return the font to indicate that the element is busy |
| */ |
| public Font getFont(Object element) { |
| if(isBusy(internalGetElement(element))) { |
| return JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT); |
| } |
| return null; |
| } |
| |
| private ImageManager getImageManager() { |
| ISynchronizationContext context = getContext(); |
| if (context != null) { |
| return ImageManager.getImageManager(context, getConfiguration()); |
| } |
| if (localImageManager == null) { |
| localImageManager = new ImageManager(); |
| } |
| return localImageManager; |
| } |
| |
| private ISynchronizePageConfiguration getConfiguration() { |
| if (this instanceof SynchronizationLabelProvider) { |
| SynchronizationLabelProvider slp = (SynchronizationLabelProvider) this; |
| return (ISynchronizePageConfiguration)slp.getExtensionSite().getExtensionStateModel().getProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_PAGE_CONFIGURATION); |
| } |
| return null; |
| } |
| |
| private ISynchronizationContext getContext() { |
| if (this instanceof SynchronizationLabelProvider) { |
| SynchronizationLabelProvider slp = (SynchronizationLabelProvider) this; |
| return slp.getContext(); |
| } |
| return null; |
| } |
| |
| } |