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;
24 import org.eclipse.jface.text.BadLocationException;
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.jface.text.IRegion;
27 import org.eclipse.jface.text.ITextViewerExtension5;
28 import org.eclipse.jface.text.Position;
29 import org.eclipse.jface.text.Region;
30 import org.eclipse.jface.text.source.Annotation;
31 import org.eclipse.jface.text.source.IAnnotationModel;
32 import org.eclipse.jface.text.source.IAnnotationModelListener;
33 import org.eclipse.jface.text.source.ISourceViewer;
34 import org.eclipse.swt.custom.StyledText;
35 import org.eclipse.swt.events.PaintEvent;
36 import org.eclipse.swt.events.PaintListener;
37 import org.eclipse.swt.graphics.Color;
38 import org.eclipse.swt.graphics.GC;
39 import org.eclipse.swt.graphics.Point;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.ui.texteditor.IDocumentProvider;
42 import org.eclipse.ui.texteditor.ITextEditor;
45 * Highlights the temporary problems.
47 public class ProblemPainter implements IPainter, PaintListener,
48 IAnnotationModelListener {
50 private static class ProblemPosition {
58 private boolean fIsActive = false;
60 private boolean fIsPainting = false;
62 private boolean fIsSettingModel = false;
64 private ITextEditor fTextEditor;
66 private ISourceViewer fSourceViewer;
68 private StyledText fTextWidget;
70 private IAnnotationModel fModel;
72 private List fProblemPositions = new ArrayList();
74 private Map fColorTable = new HashMap();
76 private Set fAnnotationSet = new HashSet();
78 public ProblemPainter(ITextEditor textEditor, ISourceViewer sourceViewer) {
79 fTextEditor = textEditor;
80 fSourceViewer = sourceViewer;
81 fTextWidget = sourceViewer.getTextWidget();
84 private boolean hasProblems() {
85 return !fProblemPositions.isEmpty();
88 private void enablePainting() {
89 if (!fIsPainting && hasProblems()) {
91 fTextWidget.addPaintListener(this);
92 handleDrawRequest(null);
96 private void disablePainting(boolean redraw) {
99 fTextWidget.removePaintListener(this);
100 if (redraw && hasProblems())
101 handleDrawRequest(null);
105 private void setModel(IAnnotationModel model) {
106 if (fModel != model) {
108 fModel.removeAnnotationModelListener(this);
110 if (fModel != null) {
112 fIsSettingModel = true;
113 fModel.addAnnotationModelListener(this);
115 fIsSettingModel = false;
121 private void catchupWithModel() {
122 if (fProblemPositions != null) {
123 fProblemPositions.clear();
124 if (fModel != null) {
126 Iterator e = new ProblemAnnotationIterator(fModel, true);
127 while (e.hasNext()) {
128 IProblemAnnotation pa = (IProblemAnnotation) e.next();
129 Annotation a = (Annotation) pa;
132 AnnotationType type = pa.getAnnotationType();
133 if (fAnnotationSet.contains(type))
134 color = (Color) fColorTable.get(type);
137 ProblemPosition pp = new ProblemPosition();
138 pp.fPosition = fModel.getPosition(a);
140 pp.fMultiLine = true;
141 fProblemPositions.add(pp);
148 private void updatePainting() {
149 disablePainting(true);
155 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
157 public void modelChanged(final IAnnotationModel model) {
158 if (fTextWidget != null && !fTextWidget.isDisposed()) {
159 if (fIsSettingModel) {
160 // inside the ui thread -> no need for posting
163 Display d = fTextWidget.getDisplay();
165 d.asyncExec(new Runnable() {
167 if (fTextWidget != null
168 && !fTextWidget.isDisposed())
177 public void setColor(AnnotationType annotationType, Color color) {
179 fColorTable.put(annotationType, color);
181 fColorTable.remove(annotationType);
184 public void paintAnnotations(AnnotationType annotationType, boolean paint) {
186 fAnnotationSet.add(annotationType);
188 fAnnotationSet.remove(annotationType);
191 public boolean isPaintingAnnotations() {
192 return !fAnnotationSet.isEmpty();
196 * @see IPainter#dispose()
198 public void dispose() {
200 if (fColorTable != null)
204 if (fAnnotationSet != null)
205 fAnnotationSet.clear();
206 fAnnotationSet = null;
210 fProblemPositions = null;
214 * Returns the document offset of the upper left corner of the widgets
215 * viewport, possibly including partially visible lines.
217 private int getInclusiveTopIndexStartOffset() {
219 if (fTextWidget != null && !fTextWidget.isDisposed()) {
220 int top = fSourceViewer.getTopIndex();
221 if ((fTextWidget.getTopPixel() % fTextWidget.getLineHeight()) != 0)
224 IDocument document = fSourceViewer.getDocument();
225 return document.getLineOffset(top);
226 } catch (BadLocationException ex) {
234 * @see PaintListener#paintControl(PaintEvent)
236 public void paintControl(PaintEvent event) {
237 if (fTextWidget != null)
238 handleDrawRequest(event.gc);
241 private void handleDrawRequest(GC gc) {
243 int vOffset = getInclusiveTopIndexStartOffset();
244 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
245 int vLength = fSourceViewer.getBottomIndexEndOffset() + 1;
247 for (Iterator e = fProblemPositions.iterator(); e.hasNext();) {
248 ProblemPosition pp = (ProblemPosition) e.next();
249 Position p = pp.fPosition;
250 if (p.overlapsWith(vOffset, vLength)) {
252 if (!pp.fMultiLine) {
254 IRegion widgetRange = getWidgetRange(p);
255 if (widgetRange != null)
256 draw(gc, widgetRange.getOffset(), widgetRange
257 .getLength(), pp.fColor);
261 IDocument document = fSourceViewer.getDocument();
264 int startLine = document.getLineOfOffset(p.getOffset());
265 int lastInclusive = Math.max(p.getOffset(), p
267 + p.getLength() - 1);
268 int endLine = document.getLineOfOffset(lastInclusive);
270 for (int i = startLine; i <= endLine; i++) {
271 IRegion line = document.getLineInformation(i);
272 int paintStart = Math.max(line.getOffset(), p
274 int paintEnd = Math.min(line.getOffset()
275 + line.getLength(), p.getOffset()
277 if (paintEnd > paintStart) {
278 // otherwise inside a line delimiter
279 IRegion widgetRange = getWidgetRange(new Position(
280 paintStart, paintEnd - paintStart));
281 if (widgetRange != null)
282 draw(gc, widgetRange.getOffset(),
283 widgetRange.getLength(), pp.fColor);
287 } catch (BadLocationException x) {
294 private IRegion getWidgetRange(Position p) {
295 if (fSourceViewer instanceof ITextViewerExtension5) {
296 ITextViewerExtension5 extension = (ITextViewerExtension5) fSourceViewer;
297 return extension.modelRange2WidgetRange(new Region(p.getOffset(), p
302 IRegion region = fSourceViewer.getVisibleRegion();
303 int offset = region.getOffset();
304 int length = region.getLength();
306 if (p.overlapsWith(offset, length)) {
307 int p1 = Math.max(offset, p.getOffset());
308 int p2 = Math.min(offset + length, p.getOffset()
310 return new Region(p1 - offset, p2 - p1);
317 private int[] computePolyline(Point left, Point right, int height) {
319 final int WIDTH = 4; // must be even
320 final int HEIGHT = 2; // can be any number
321 // final int MINPEEKS= 2; // minimal number of peeks
323 int peeks = (right.x - left.x) / WIDTH;
324 // if (peeks < MINPEEKS) {
325 // int missing= (MINPEEKS - peeks) * WIDTH;
326 // left.x= Math.max(0, left.x - missing/2);
332 // compute (number of point) * 2
333 int length = ((2 * peeks) + 1) * 2;
337 int[] coordinates = new int[length];
339 // cache peeks' y-coordinates
340 int bottom = left.y + height - 1;
341 int top = bottom - HEIGHT;
343 // populate array with peek coordinates
344 for (int i = 0; i < peeks; i++) {
346 coordinates[index] = leftX + (WIDTH * i);
347 coordinates[index + 1] = bottom;
348 coordinates[index + 2] = coordinates[index] + WIDTH / 2;
349 coordinates[index + 3] = top;
352 // the last down flank is missing
353 coordinates[length - 2] = left.x + (WIDTH * peeks);
354 coordinates[length - 1] = bottom;
359 private void draw(GC gc, int offset, int length, Color color) {
362 Point left = fTextWidget.getLocationAtOffset(offset);
363 Point right = fTextWidget.getLocationAtOffset(offset + length);
365 gc.setForeground(color);
366 int[] polyline = computePolyline(left, right, gc.getFontMetrics()
368 gc.drawPolyline(polyline);
371 fTextWidget.redrawRange(offset, length, true);
376 * @see IPainter#deactivate(boolean)
378 public void deactivate(boolean redraw) {
381 disablePainting(redraw);
388 * @see IPainter#paint(int)
390 public void paint(int reason) {
393 IDocumentProvider provider = PHPeclipsePlugin.getDefault()
394 .getCompilationUnitDocumentProvider();
395 setModel(provider.getAnnotationModel(fTextEditor.getEditorInput()));
396 } else if (CONFIGURATION == reason || INTERNAL == reason)
401 * @see IPainter#setPositionManager(IPositionManager)
403 public void setPositionManager(IPositionManager manager) {