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 {
79 private static final long serialVersionUID = -7557704732816971319L;
80 private boolean fMarkerChange;
83 * Note: This constructor is for internal use only. Clients should not
84 * call this constructor.
86 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
87 IResource[] changedResource, boolean isMarkerChange) {
88 super(source, changedResource);
89 fMarkerChange = isMarkerChange;
93 * Returns whether this event origins from marker changes. If
94 * <code>false</code> an annotation model change is the origin. In
95 * this case viewers not displaying working copies can ignore these
98 * @return if this event origins from a marker change.
100 public boolean isMarkerChange() {
101 return fMarkerChange;
106 private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
108 private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
110 private ImageDescriptorRegistry fRegistry;
112 private boolean fUseNewRegistry = false;
114 private IProblemChangedListener fProblemChangedListener;
116 private ListenerList fListeners;
119 * Creates a new <code>ProblemsLabelDecorator</code>.
121 // public ProblemsLabelDecorator() {
123 // fUseNewRegistry = true;
127 * Creates decorator with a shared image registry.
129 * @param registry The registry to use or <code>null</code> to use the
130 * Java plugin's image registry.
133 * Note: This constructor is for internal use only. Clients should not call
136 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
137 fRegistry = registry;
138 fProblemChangedListener = null;
141 private ImageDescriptorRegistry getRegistry() {
142 if (fRegistry == null) {
143 fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
144 : WebUI.getImageDescriptorRegistry();
152 * @see ILabelDecorator#decorateText(String, Object)
154 public String decorateText(String text, Object element) {
161 * @see ILabelDecorator#decorateImage(Image, Object)
163 public Image decorateImage(Image image, Object obj) {
164 int adornmentFlags = computeAdornmentFlags(obj);
165 if (adornmentFlags != 0) {
166 ImageDescriptor baseImage = new ImageImageDescriptor(image);
167 Rectangle bounds = image.getBounds();
168 return getRegistry().get(
169 new JavaElementImageDescriptor(baseImage, adornmentFlags,
170 new Point(bounds.width, bounds.height)));
176 * Note: This method is for internal use only. Clients should not call this
179 protected int computeAdornmentFlags(Object obj) {
181 if (obj instanceof IJavaElement) {
182 IJavaElement element = (IJavaElement) obj;
183 int type = element.getElementType();
185 case IJavaElement.JAVA_PROJECT:
186 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
187 return getErrorTicksFromMarkers(element.getResource(),
188 IResource.DEPTH_INFINITE, null);
189 case IJavaElement.PACKAGE_FRAGMENT:
190 case IJavaElement.CLASS_FILE:
191 return getErrorTicksFromMarkers(element.getResource(),
192 IResource.DEPTH_ONE, null);
193 case IJavaElement.COMPILATION_UNIT:
194 case IJavaElement.PACKAGE_DECLARATION:
195 case IJavaElement.IMPORT_DECLARATION:
196 case IJavaElement.IMPORT_CONTAINER:
197 case IJavaElement.TYPE:
198 case IJavaElement.INITIALIZER:
199 case IJavaElement.METHOD:
200 case IJavaElement.FIELD:
201 ICompilationUnit cu = (ICompilationUnit) element
202 .getAncestor(IJavaElement.COMPILATION_UNIT);
204 ISourceReference ref = (type == IJavaElement.COMPILATION_UNIT) ? null
205 : (ISourceReference) element;
206 // The assumption is that only source elements in
207 // compilation unit can have markers
208 if (cu.isWorkingCopy()) {
209 // working copy: look at annotation model
210 return getErrorTicksFromWorkingCopy(
211 (ICompilationUnit) cu.getOriginalElement(),
214 return getErrorTicksFromMarkers(cu.getResource(),
215 IResource.DEPTH_ONE, ref);
220 } else if (obj instanceof IResource) {
221 return getErrorTicksFromMarkers((IResource) obj,
222 IResource.DEPTH_INFINITE, null);
224 } catch (CoreException e) {
225 if (e instanceof JavaModelException) {
226 if (((JavaModelException) e).isDoesNotExist()) {
230 PHPeclipsePlugin.log(e);
235 private int getErrorTicksFromMarkers(IResource res, int depth,
236 ISourceReference sourceElement) throws CoreException {
237 if (res == null || !res.isAccessible()) {
242 IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true, depth);
243 if (markers != null) {
244 for (int i = 0; i < markers.length && (info != ERRORTICK_ERROR); i++) {
245 IMarker curr = markers[i];
246 if (sourceElement == null
247 || isMarkerInRange(curr, sourceElement)) {
248 int priority = curr.getAttribute(IMarker.SEVERITY, -1);
249 if (priority == IMarker.SEVERITY_WARNING) {
250 info = ERRORTICK_WARNING;
251 } else if (priority == IMarker.SEVERITY_ERROR) {
252 info = ERRORTICK_ERROR;
260 private boolean isMarkerInRange(IMarker marker,
261 ISourceReference sourceElement) throws CoreException {
262 if (marker.isSubtypeOf(IMarker.TEXT)) {
263 int pos = marker.getAttribute(IMarker.CHAR_START, -1);
264 return isInside(pos, sourceElement);
269 private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
270 ISourceReference sourceElement) throws CoreException {
272 FileEditorInput editorInput = new FileEditorInput((IFile) original
274 IAnnotationModel model = WebUI.getDefault()
275 .getCompilationUnitDocumentProvider().getAnnotationModel(
278 Iterator iter = model.getAnnotationIterator();
279 while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
280 Annotation curr = (Annotation) iter.next();
281 IMarker marker = isAnnotationInRange(model, curr, sourceElement);
282 if (marker != null) {
283 int priority = marker.getAttribute(IMarker.SEVERITY, -1);
284 if (priority == IMarker.SEVERITY_WARNING) {
285 info = ERRORTICK_WARNING;
286 } else if (priority == IMarker.SEVERITY_ERROR) {
287 info = ERRORTICK_ERROR;
295 private IMarker isAnnotationInRange(IAnnotationModel model,
296 Annotation annot, ISourceReference sourceElement)
297 throws CoreException {
298 if (annot instanceof MarkerAnnotation) {
299 IMarker marker = ((MarkerAnnotation) annot).getMarker();
300 if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) {
301 Position pos = model.getPosition(annot);
302 if (sourceElement == null
303 || isInside(pos.getOffset(), sourceElement)) {
312 * Tests if a position is inside the source range of an element.
315 * Position to be tested.
316 * @param sourceElement
317 * Source element (must be a IJavaElement)
318 * @return boolean Return <code>true</code> if position is located inside
319 * the source element.
320 * @throws CoreException
321 * Exception thrown if element range could not be accessed.
325 protected boolean isInside(int pos, ISourceReference sourceElement)
326 throws CoreException {
327 ISourceRange range = sourceElement.getSourceRange();
329 int rangeOffset = range.getOffset();
330 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
338 * @see IBaseLabelProvider#dispose()
340 public void dispose() {
341 if (fProblemChangedListener != null) {
342 WebUI.getDefault().getProblemMarkerManager()
343 .removeListener(fProblemChangedListener);
344 fProblemChangedListener = null;
346 if (fRegistry != null && fUseNewRegistry) {
354 * @see IBaseLabelProvider#isLabelProperty(Object, String)
356 public boolean isLabelProperty(Object element, String property) {
363 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
365 public void addListener(ILabelProviderListener listener) {
366 if (fListeners == null) {
367 fListeners = new ListenerList();
369 fListeners.add(listener);
370 if (fProblemChangedListener == null) {
371 fProblemChangedListener = new IProblemChangedListener() {
372 public void problemsChanged(IResource[] changedResources,
373 boolean isMarkerChange) {
374 fireProblemsChanged(changedResources, isMarkerChange);
377 WebUI.getDefault().getProblemMarkerManager()
378 .addListener(fProblemChangedListener);
385 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
387 public void removeListener(ILabelProviderListener listener) {
388 if (fListeners != null) {
389 fListeners.remove(listener);
390 if (fListeners.isEmpty() && fProblemChangedListener != null) {
391 WebUI.getDefault().getProblemMarkerManager()
392 .removeListener(fProblemChangedListener);
393 fProblemChangedListener = null;
398 private void fireProblemsChanged(IResource[] changedResources,
399 boolean isMarkerChange) {
400 if (fListeners != null && !fListeners.isEmpty()) {
401 LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(
402 this, changedResources, isMarkerChange);
403 Object[] listeners = fListeners.getListeners();
404 for (int i = 0; i < listeners.length; i++) {
405 ((ILabelProviderListener) listeners[i])
406 .labelProviderChanged(event);
414 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
415 * org.eclipse.jface.viewers.IDecoration)
417 public void decorate(Object element, IDecoration decoration) {
418 int adornmentFlags = computeAdornmentFlags(element);
419 if (adornmentFlags == ERRORTICK_ERROR) {
420 decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR);
421 } else if (adornmentFlags == ERRORTICK_WARNING) {
422 decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING);