1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. 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 implementation
10 **********************************************************************/
12 package net.sourceforge.phpeclipse.phpeditor;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
22 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
23 import net.sourceforge.phpeclipse.ui.WebUI;
25 import org.eclipse.jface.text.BadLocationException;
26 import org.eclipse.jface.text.IDocument;
27 import org.eclipse.jface.text.IRegion;
28 import org.eclipse.jface.text.ITextViewerExtension5;
29 import org.eclipse.jface.text.Position;
30 import org.eclipse.jface.text.Region;
31 import org.eclipse.jface.text.source.Annotation;
32 import org.eclipse.jface.text.source.IAnnotationModel;
33 import org.eclipse.jface.text.source.IAnnotationModelListener;
34 import org.eclipse.jface.text.source.ISourceViewer;
35 import org.eclipse.swt.custom.StyledText;
36 import org.eclipse.swt.events.PaintEvent;
37 import org.eclipse.swt.events.PaintListener;
38 import org.eclipse.swt.graphics.Color;
39 import org.eclipse.swt.graphics.GC;
40 import org.eclipse.swt.graphics.Point;
41 import org.eclipse.swt.widgets.Display;
42 import org.eclipse.ui.texteditor.IDocumentProvider;
43 import org.eclipse.ui.texteditor.ITextEditor;
46 * Highlights the temporary problems.
48 public class ProblemPainter implements IPainter, PaintListener,
49 IAnnotationModelListener {
51 private static class ProblemPosition {
59 private boolean fIsActive = false;
61 private boolean fIsPainting = false;
63 private boolean fIsSettingModel = false;
65 private ITextEditor fTextEditor;
67 private ISourceViewer fSourceViewer;
69 private StyledText fTextWidget;
71 private IAnnotationModel fModel;
73 private List fProblemPositions = new ArrayList();
75 private Map fColorTable = new HashMap();
77 private Set fAnnotationSet = new HashSet();
79 public ProblemPainter(ITextEditor textEditor, ISourceViewer sourceViewer) {
80 fTextEditor = textEditor;
81 fSourceViewer = sourceViewer;
82 fTextWidget = sourceViewer.getTextWidget();
85 private boolean hasProblems() {
86 return !fProblemPositions.isEmpty();
89 private void enablePainting() {
90 if (!fIsPainting && hasProblems()) {
92 fTextWidget.addPaintListener(this);
93 handleDrawRequest(null);
97 private void disablePainting(boolean redraw) {
100 fTextWidget.removePaintListener(this);
101 if (redraw && hasProblems())
102 handleDrawRequest(null);
106 private void setModel(IAnnotationModel model) {
107 if (fModel != model) {
109 fModel.removeAnnotationModelListener(this);
111 if (fModel != null) {
113 fIsSettingModel = true;
114 fModel.addAnnotationModelListener(this);
116 fIsSettingModel = false;
122 private void catchupWithModel() {
123 if (fProblemPositions != null) {
124 fProblemPositions.clear();
125 if (fModel != null) {
127 Iterator e = new ProblemAnnotationIterator(fModel, true);
128 while (e.hasNext()) {
129 IProblemAnnotation pa = (IProblemAnnotation) e.next();
130 Annotation a = (Annotation) pa;
133 AnnotationType type = pa.getAnnotationType();
134 if (fAnnotationSet.contains(type))
135 color = (Color) fColorTable.get(type);
138 ProblemPosition pp = new ProblemPosition();
139 pp.fPosition = fModel.getPosition(a);
141 pp.fMultiLine = true;
142 fProblemPositions.add(pp);
149 private void updatePainting() {
150 disablePainting(true);
156 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
158 public void modelChanged(final IAnnotationModel model) {
159 if (fTextWidget != null && !fTextWidget.isDisposed()) {
160 if (fIsSettingModel) {
161 // inside the ui thread -> no need for posting
164 Display d = fTextWidget.getDisplay();
166 d.asyncExec(new Runnable() {
168 if (fTextWidget != null
169 && !fTextWidget.isDisposed())
178 public void setColor(AnnotationType annotationType, Color color) {
180 fColorTable.put(annotationType, color);
182 fColorTable.remove(annotationType);
185 public void paintAnnotations(AnnotationType annotationType, boolean paint) {
187 fAnnotationSet.add(annotationType);
189 fAnnotationSet.remove(annotationType);
192 public boolean isPaintingAnnotations() {
193 return !fAnnotationSet.isEmpty();
197 * @see IPainter#dispose()
199 public void dispose() {
201 if (fColorTable != null)
205 if (fAnnotationSet != null)
206 fAnnotationSet.clear();
207 fAnnotationSet = null;
211 fProblemPositions = null;
215 * Returns the document offset of the upper left corner of the widgets
216 * viewport, possibly including partially visible lines.
218 private int getInclusiveTopIndexStartOffset() {
220 if (fTextWidget != null && !fTextWidget.isDisposed()) {
221 int top = fSourceViewer.getTopIndex();
222 if ((fTextWidget.getTopPixel() % fTextWidget.getLineHeight()) != 0)
225 IDocument document = fSourceViewer.getDocument();
226 return document.getLineOffset(top);
227 } catch (BadLocationException ex) {
235 * @see PaintListener#paintControl(PaintEvent)
237 public void paintControl(PaintEvent event) {
238 if (fTextWidget != null)
239 handleDrawRequest(event.gc);
242 private void handleDrawRequest(GC gc) {
244 int vOffset = getInclusiveTopIndexStartOffset();
245 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
246 int vLength = fSourceViewer.getBottomIndexEndOffset() + 1;
248 for (Iterator e = fProblemPositions.iterator(); e.hasNext();) {
249 ProblemPosition pp = (ProblemPosition) e.next();
250 Position p = pp.fPosition;
251 if (p.overlapsWith(vOffset, vLength)) {
253 if (!pp.fMultiLine) {
255 IRegion widgetRange = getWidgetRange(p);
256 if (widgetRange != null)
257 draw(gc, widgetRange.getOffset(), widgetRange
258 .getLength(), pp.fColor);
262 IDocument document = fSourceViewer.getDocument();
265 int startLine = document.getLineOfOffset(p.getOffset());
266 int lastInclusive = Math.max(p.getOffset(), p
268 + p.getLength() - 1);
269 int endLine = document.getLineOfOffset(lastInclusive);
271 for (int i = startLine; i <= endLine; i++) {
272 IRegion line = document.getLineInformation(i);
273 int paintStart = Math.max(line.getOffset(), p
275 int paintEnd = Math.min(line.getOffset()
276 + line.getLength(), p.getOffset()
278 if (paintEnd > paintStart) {
279 // otherwise inside a line delimiter
280 IRegion widgetRange = getWidgetRange(new Position(
281 paintStart, paintEnd - paintStart));
282 if (widgetRange != null)
283 draw(gc, widgetRange.getOffset(),
284 widgetRange.getLength(), pp.fColor);
288 } catch (BadLocationException x) {
295 private IRegion getWidgetRange(Position p) {
296 if (fSourceViewer instanceof ITextViewerExtension5) {
297 ITextViewerExtension5 extension = (ITextViewerExtension5) fSourceViewer;
298 return extension.modelRange2WidgetRange(new Region(p.getOffset(), p
303 IRegion region = fSourceViewer.getVisibleRegion();
304 int offset = region.getOffset();
305 int length = region.getLength();
307 if (p.overlapsWith(offset, length)) {
308 int p1 = Math.max(offset, p.getOffset());
309 int p2 = Math.min(offset + length, p.getOffset()
311 return new Region(p1 - offset, p2 - p1);
318 private int[] computePolyline(Point left, Point right, int height) {
320 final int WIDTH = 4; // must be even
321 final int HEIGHT = 2; // can be any number
322 // final int MINPEEKS= 2; // minimal number of peeks
324 int peeks = (right.x - left.x) / WIDTH;
325 // if (peeks < MINPEEKS) {
326 // int missing= (MINPEEKS - peeks) * WIDTH;
327 // left.x= Math.max(0, left.x - missing/2);
333 // compute (number of point) * 2
334 int length = ((2 * peeks) + 1) * 2;
338 int[] coordinates = new int[length];
340 // cache peeks' y-coordinates
341 int bottom = left.y + height - 1;
342 int top = bottom - HEIGHT;
344 // populate array with peek coordinates
345 for (int i = 0; i < peeks; i++) {
347 coordinates[index] = leftX + (WIDTH * i);
348 coordinates[index + 1] = bottom;
349 coordinates[index + 2] = coordinates[index] + WIDTH / 2;
350 coordinates[index + 3] = top;
353 // the last down flank is missing
354 coordinates[length - 2] = left.x + (WIDTH * peeks);
355 coordinates[length - 1] = bottom;
360 private void draw(GC gc, int offset, int length, Color color) {
363 Point left = fTextWidget.getLocationAtOffset(offset);
364 Point right = fTextWidget.getLocationAtOffset(offset + length);
366 gc.setForeground(color);
367 int[] polyline = computePolyline(left, right, gc.getFontMetrics()
369 gc.drawPolyline(polyline);
372 fTextWidget.redrawRange(offset, length, true);
377 * @see IPainter#deactivate(boolean)
379 public void deactivate(boolean redraw) {
382 disablePainting(redraw);
389 * @see IPainter#paint(int)
391 public void paint(int reason) {
394 IDocumentProvider provider = WebUI.getDefault()
395 .getCompilationUnitDocumentProvider();
396 setModel(provider.getAnnotationModel(fTextEditor.getEditorInput()));
397 } else if (CONFIGURATION == reason || INTERNAL == reason)
402 * @see IPainter#setPositionManager(IPositionManager)
404 public void setPositionManager(IPositionManager manager) {