1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.ui;
13 import java.util.Iterator;
15 import net.sourceforge.phpdt.core.ICompilationUnit;
16 import net.sourceforge.phpdt.core.IJavaElement;
17 import net.sourceforge.phpdt.core.ISourceRange;
18 import net.sourceforge.phpdt.core.ISourceReference;
19 import net.sourceforge.phpdt.core.JavaModelException;
20 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
21 import net.sourceforge.phpdt.internal.ui.viewsupport.IProblemChangedListener;
22 import net.sourceforge.phpdt.internal.ui.viewsupport.ImageDescriptorRegistry;
23 import net.sourceforge.phpdt.internal.ui.viewsupport.ImageImageDescriptor;
24 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
25 import net.sourceforge.phpeclipse.ui.WebUI;
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IMarker;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.jface.resource.ImageDescriptor;
32 import org.eclipse.jface.text.Position;
33 import org.eclipse.jface.text.source.Annotation;
34 import org.eclipse.jface.text.source.IAnnotationModel;
36 //import org.eclipse.jface.util.ListenerList;
37 import org.eclipse.core.runtime.ListenerList;
38 import org.eclipse.jface.viewers.IBaseLabelProvider;
39 import org.eclipse.jface.viewers.IDecoration;
40 import org.eclipse.jface.viewers.ILabelDecorator;
41 import org.eclipse.jface.viewers.ILabelProviderListener;
42 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
43 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
44 import org.eclipse.swt.graphics.Image;
45 import org.eclipse.swt.graphics.Point;
46 import org.eclipse.swt.graphics.Rectangle;
47 import org.eclipse.ui.part.FileEditorInput;
48 import org.eclipse.ui.texteditor.MarkerAnnotation;
51 * LabelDecorator that decorates an element's image with error and warning
52 * overlays that represent the severity of markers attached to the element's
53 * underlying resource. To see a problem decoration for a marker, the marker
54 * needs to be a subtype of <code>IMarker.PROBLEM</code>.
56 * Note: Only images for elements in Java projects are currently updated on
62 public class ProblemsLabelDecorator implements ILabelDecorator,
63 ILightweightLabelDecorator {
66 * This is a special <code>LabelProviderChangedEvent</code> carring
67 * additional information whether the event orgins from a maker change.
69 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
70 * ProblemsLabelDecorator</code>s.
73 public static class ProblemsLabelChangedEvent extends
74 LabelProviderChangedEvent {
76 private boolean fMarkerChange;
79 * Note: This constructor is for internal use only. Clients should not
80 * call this constructor.
82 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
83 IResource[] changedResource, boolean isMarkerChange) {
84 super(source, changedResource);
85 fMarkerChange = isMarkerChange;
89 * Returns whether this event origins from marker changes. If
90 * <code>false</code> an annotation model change is the origin. In
91 * this case viewers not displaying working copies can ignore these
94 * @return if this event origins from a marker change.
96 public boolean isMarkerChange() {
102 private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
104 private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
106 private ImageDescriptorRegistry fRegistry;
108 private boolean fUseNewRegistry = false;
110 private IProblemChangedListener fProblemChangedListener;
112 private ListenerList fListeners;
115 * Creates a new <code>ProblemsLabelDecorator</code>.
117 public ProblemsLabelDecorator() {
119 fUseNewRegistry = true;
123 * Creates decorator with a shared image registry.
125 * @param registry The registry to use or <code>null</code> to use the
126 * Java plugin's image registry.
129 * Note: This constructor is for internal use only. Clients should not call
132 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
133 fRegistry = registry;
134 fProblemChangedListener = null;
137 private ImageDescriptorRegistry getRegistry() {
138 if (fRegistry == null) {
139 fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
140 : WebUI.getImageDescriptorRegistry();
148 * @see ILabelDecorator#decorateText(String, Object)
150 public String decorateText(String text, Object element) {
157 * @see ILabelDecorator#decorateImage(Image, Object)
159 public Image decorateImage(Image image, Object obj) {
160 int adornmentFlags = computeAdornmentFlags(obj);
161 if (adornmentFlags != 0) {
162 ImageDescriptor baseImage = new ImageImageDescriptor(image);
163 Rectangle bounds = image.getBounds();
164 return getRegistry().get(
165 new JavaElementImageDescriptor(baseImage, adornmentFlags,
166 new Point(bounds.width, bounds.height)));
172 * Note: This method is for internal use only. Clients should not call this
175 protected int computeAdornmentFlags(Object obj) {
177 if (obj instanceof IJavaElement) {
178 IJavaElement element = (IJavaElement) obj;
179 int type = element.getElementType();
181 case IJavaElement.JAVA_PROJECT:
182 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
183 return getErrorTicksFromMarkers(element.getResource(),
184 IResource.DEPTH_INFINITE, null);
185 case IJavaElement.PACKAGE_FRAGMENT:
186 case IJavaElement.CLASS_FILE:
187 return getErrorTicksFromMarkers(element.getResource(),
188 IResource.DEPTH_ONE, null);
189 case IJavaElement.COMPILATION_UNIT:
190 case IJavaElement.PACKAGE_DECLARATION:
191 case IJavaElement.IMPORT_DECLARATION:
192 case IJavaElement.IMPORT_CONTAINER:
193 case IJavaElement.TYPE:
194 case IJavaElement.INITIALIZER:
195 case IJavaElement.METHOD:
196 case IJavaElement.FIELD:
197 ICompilationUnit cu = (ICompilationUnit) element
198 .getAncestor(IJavaElement.COMPILATION_UNIT);
200 ISourceReference ref = (type == IJavaElement.COMPILATION_UNIT) ? null
201 : (ISourceReference) element;
202 // The assumption is that only source elements in
203 // compilation unit can have markers
204 if (cu.isWorkingCopy()) {
205 // working copy: look at annotation model
206 return getErrorTicksFromWorkingCopy(
207 (ICompilationUnit) cu.getOriginalElement(),
210 return getErrorTicksFromMarkers(cu.getResource(),
211 IResource.DEPTH_ONE, ref);
216 } else if (obj instanceof IResource) {
217 return getErrorTicksFromMarkers((IResource) obj,
218 IResource.DEPTH_INFINITE, null);
220 } catch (CoreException e) {
221 if (e instanceof JavaModelException) {
222 if (((JavaModelException) e).isDoesNotExist()) {
226 PHPeclipsePlugin.log(e);
231 private int getErrorTicksFromMarkers(IResource res, int depth,
232 ISourceReference sourceElement) throws CoreException {
233 if (res == null || !res.isAccessible()) {
238 IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true, depth);
239 if (markers != null) {
240 for (int i = 0; i < markers.length && (info != ERRORTICK_ERROR); i++) {
241 IMarker curr = markers[i];
242 if (sourceElement == null
243 || isMarkerInRange(curr, sourceElement)) {
244 int priority = curr.getAttribute(IMarker.SEVERITY, -1);
245 if (priority == IMarker.SEVERITY_WARNING) {
246 info = ERRORTICK_WARNING;
247 } else if (priority == IMarker.SEVERITY_ERROR) {
248 info = ERRORTICK_ERROR;
256 private boolean isMarkerInRange(IMarker marker,
257 ISourceReference sourceElement) throws CoreException {
258 if (marker.isSubtypeOf(IMarker.TEXT)) {
259 int pos = marker.getAttribute(IMarker.CHAR_START, -1);
260 return isInside(pos, sourceElement);
265 private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
266 ISourceReference sourceElement) throws CoreException {
268 FileEditorInput editorInput = new FileEditorInput((IFile) original
270 IAnnotationModel model = WebUI.getDefault()
271 .getCompilationUnitDocumentProvider().getAnnotationModel(
274 Iterator iter = model.getAnnotationIterator();
275 while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
276 Annotation curr = (Annotation) iter.next();
277 IMarker marker = isAnnotationInRange(model, curr, sourceElement);
278 if (marker != null) {
279 int priority = marker.getAttribute(IMarker.SEVERITY, -1);
280 if (priority == IMarker.SEVERITY_WARNING) {
281 info = ERRORTICK_WARNING;
282 } else if (priority == IMarker.SEVERITY_ERROR) {
283 info = ERRORTICK_ERROR;
291 private IMarker isAnnotationInRange(IAnnotationModel model,
292 Annotation annot, ISourceReference sourceElement)
293 throws CoreException {
294 if (annot instanceof MarkerAnnotation) {
295 IMarker marker = ((MarkerAnnotation) annot).getMarker();
296 if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) {
297 Position pos = model.getPosition(annot);
298 if (sourceElement == null
299 || isInside(pos.getOffset(), sourceElement)) {
308 * Tests if a position is inside the source range of an element.
311 * Position to be tested.
312 * @param sourceElement
313 * Source element (must be a IJavaElement)
314 * @return boolean Return <code>true</code> if position is located inside
315 * the source element.
316 * @throws CoreException
317 * Exception thrown if element range could not be accessed.
321 protected boolean isInside(int pos, ISourceReference sourceElement)
322 throws CoreException {
323 ISourceRange range = sourceElement.getSourceRange();
325 int rangeOffset = range.getOffset();
326 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
334 * @see IBaseLabelProvider#dispose()
336 public void dispose() {
337 if (fProblemChangedListener != null) {
338 WebUI.getDefault().getProblemMarkerManager()
339 .removeListener(fProblemChangedListener);
340 fProblemChangedListener = null;
342 if (fRegistry != null && fUseNewRegistry) {
350 * @see IBaseLabelProvider#isLabelProperty(Object, String)
352 public boolean isLabelProperty(Object element, String property) {
359 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
361 public void addListener(ILabelProviderListener listener) {
362 if (fListeners == null) {
363 fListeners = new ListenerList();
365 fListeners.add(listener);
366 if (fProblemChangedListener == null) {
367 fProblemChangedListener = new IProblemChangedListener() {
368 public void problemsChanged(IResource[] changedResources,
369 boolean isMarkerChange) {
370 fireProblemsChanged(changedResources, isMarkerChange);
373 WebUI.getDefault().getProblemMarkerManager()
374 .addListener(fProblemChangedListener);
381 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
383 public void removeListener(ILabelProviderListener listener) {
384 if (fListeners != null) {
385 fListeners.remove(listener);
386 if (fListeners.isEmpty() && fProblemChangedListener != null) {
387 WebUI.getDefault().getProblemMarkerManager()
388 .removeListener(fProblemChangedListener);
389 fProblemChangedListener = null;
394 private void fireProblemsChanged(IResource[] changedResources,
395 boolean isMarkerChange) {
396 if (fListeners != null && !fListeners.isEmpty()) {
397 LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(
398 this, changedResources, isMarkerChange);
399 Object[] listeners = fListeners.getListeners();
400 for (int i = 0; i < listeners.length; i++) {
401 ((ILabelProviderListener) listeners[i])
402 .labelProviderChanged(event);
410 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
411 * org.eclipse.jface.viewers.IDecoration)
413 public void decorate(Object element, IDecoration decoration) {
414 int adornmentFlags = computeAdornmentFlags(element);
415 if (adornmentFlags == ERRORTICK_ERROR) {
416 decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR);
417 } else if (adornmentFlags == ERRORTICK_WARNING) {
418 decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING);