package org.erlide.ui.util; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ILightweightLabelDecorator; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.erlang.ISourceRange; import org.erlide.engine.model.erlang.ISourceReference; import org.erlide.ui.ErlideImage; import org.erlide.ui.editors.erl.outline.ErlangElementImageDescriptor; import org.erlide.ui.internal.ErlideUIPlugin; public class ProblemsLabelDecorator implements ILabelDecorator, ILightweightLabelDecorator { /** * This is a special <code>LabelProviderChangedEvent</code> carrying * additional information whether the event origins from a maker change. * <p> * <code>ProblemsLabelChangedEvent</code>s are only generated by <code> * ProblemsLabelDecorator</code> s. * </p> */ public static class ProblemsLabelChangedEvent extends LabelProviderChangedEvent { /** * */ private static final long serialVersionUID = 1L; private final boolean fMarkerChange; /** * Note: This constructor is for internal use only. Clients should not * call this constructor. */ public ProblemsLabelChangedEvent(final IBaseLabelProvider source, final IResource[] changedResource, final boolean isMarkerChange) { super(source, changedResource); fMarkerChange = isMarkerChange; } /** * Returns whether this event origins from marker changes. If * <code>false</code> an annotation model change is the origin. In this * case viewers not displaying working copies can ignore these events. * * @return if this event origins from a marker change. */ public boolean isMarkerChange() { return fMarkerChange; } } private static final int ERRORTICK_WARNING = ErlangElementImageDescriptor.WARNING; private static final int ERRORTICK_ERROR = ErlangElementImageDescriptor.ERROR; private ListenerList fListeners; private IProblemChangedListener fProblemChangedListener; /* * Creates decorator with a shared image registry. * * @param registry The registry to use or <code>null</code> to use the * erlide plugin's image registry. */ /** * Note: This method is for internal use only. Clients should not call this * method. * * @param obj * the element to compute the flags for * * @return the adornment flags */ protected int computeAdornmentFlags(final Object obj) { try { final ISourceReference r = obj instanceof ISourceReference ? (ISourceReference) obj : null; if (obj instanceof IResource) { return getErrorTicksFromMarkers((IResource) obj, IResource.DEPTH_INFINITE, r); } else if (obj instanceof IErlElement) { final IErlElement e = (IErlElement) obj; return getErrorTicksFromMarkers(e.getResource(), IResource.DEPTH_INFINITE, r); } } catch (final CoreException e) { if (e.getStatus().getCode() == IResourceStatus.MARKER_NOT_FOUND) { return 0; } } return 0; } public static int getErrorTicksFromMarkers(final IResource res, final int depth, final ISourceReference sourceElement) throws CoreException { if (res == null || !res.isAccessible()) { return 0; } int severity = 0; if (sourceElement == null) { severity = res.findMaxProblemSeverity(IMarker.PROBLEM, true, depth); } else { final IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true, depth); if (markers != null && markers.length > 0) { for (int i = 0; i < markers.length && severity != IMarker.SEVERITY_ERROR; i++) { final IMarker curr = markers[i]; if (isMarkerInRange(curr, sourceElement)) { final int val = curr.getAttribute(IMarker.SEVERITY, -1); if (val == IMarker.SEVERITY_WARNING || val == IMarker.SEVERITY_ERROR) { severity = val; } } } } } if (severity == IMarker.SEVERITY_ERROR) { return ERRORTICK_ERROR; } else if (severity == IMarker.SEVERITY_WARNING) { return ERRORTICK_WARNING; } return 0; } private static boolean isMarkerInRange(final IMarker marker, final ISourceReference sourceElement) throws CoreException { final int pos = marker.getAttribute(IMarker.CHAR_START, -1); if (pos != -1) { return isInside(pos, sourceElement); } final int line = marker.getAttribute(IMarker.LINE_NUMBER, -1); if (line != -1) { return isInsideLines(line - 1, sourceElement); } return false; } // private boolean isInside(Position pos, ISourceReference sourceElement) // throws CoreException { // return pos != null && isInside(pos.getOffset(), sourceElement); // } private static boolean isInsideLines(final int line, final ISourceReference sourceElement) { return line >= sourceElement.getLineStart() && line <= sourceElement.getLineEnd(); } /** * Tests if a position is inside the source range of an element. * * @param pos * Position to be tested. * @param sourceElement * Source element (must be a IErlElement) * @return boolean Return <code>true</code> if position is located inside * the source element. * @throws CoreException * Exception thrown if element range could not be accessed. * */ protected static boolean isInside(final int pos, final ISourceReference sourceElement) throws CoreException { // if (fCachedRange == null) { // fCachedRange= sourceElement.getSourceRange(); // } // ISourceRange range= fCachedRange; final ISourceRange range = sourceElement.getSourceRange(); if (range != null) { final int rangeOffset = range.getOffset(); return rangeOffset <= pos && rangeOffset + range.getLength() > pos; } return false; } @Override public void decorate(final Object element, final IDecoration decoration) { final int adornmentFlags = computeAdornmentFlags(element); if (adornmentFlags == ERRORTICK_ERROR) { decoration.addOverlay(ErlideImage.OVR_ERROR.getDescriptor()); } else if (adornmentFlags == ERRORTICK_WARNING) { decoration.addOverlay(ErlideImage.OVR_WARNING.getDescriptor()); } } @Override public void addListener(final ILabelProviderListener listener) { if (fListeners == null) { fListeners = new ListenerList(); } fListeners.add(listener); if (fProblemChangedListener == null) { fProblemChangedListener = new IProblemChangedListener() { @Override public void problemsChanged(final IResource[] changedResources, final boolean isMarkerChange) { fireProblemsChanged(changedResources, isMarkerChange); } }; ErlideUIPlugin.getDefault().getProblemMarkerManager() .addListener(fProblemChangedListener); } } void fireProblemsChanged(final IResource[] changedResources, final boolean isMarkerChange) { if (fListeners != null && !fListeners.isEmpty()) { final LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(this, changedResources, isMarkerChange); final Object[] listeners = fListeners.getListeners(); for (int i = 0; i < listeners.length; i++) { ((ILabelProviderListener) listeners[i]).labelProviderChanged(event); } } } @Override public void dispose() { if (fProblemChangedListener != null) { ErlideUIPlugin.getDefault().getProblemMarkerManager() .removeListener(fProblemChangedListener); fProblemChangedListener = null; } // if (fRegistry != null && fUseNewRegistry) { // fRegistry.dispose(); // } } @Override public boolean isLabelProperty(final Object element, final String property) { return true; } @Override public void removeListener(final ILabelProviderListener listener) { if (fListeners != null) { fListeners.remove(listener); if (fListeners.isEmpty() && fProblemChangedListener != null) { ErlideUIPlugin.getDefault().getProblemMarkerManager() .removeListener(fProblemChangedListener); fProblemChangedListener = null; } } } @Override public String decorateText(final String text, final Object element) { return text; } @Override public Image decorateImage(final Image image, final Object obj) { final int adornmentFlags = computeAdornmentFlags(obj); if (adornmentFlags != 0) { final ImageDescriptor baseImage = new ImageImageDescriptor(image); final Rectangle bounds = image.getBounds(); return ErlideUIPlugin.getImageDescriptorRegistry() .get(new ErlangElementImageDescriptor(baseImage, adornmentFlags, new Point(bounds.width, bounds.height))); } return image; } }