blob: f07014bac3c40e4a5009049a798039113552778e [file] [log] [blame]
/*******************************************************************************
* 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;
}
}