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;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
23 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
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.ITextViewerExtension3;
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, IAnnotationModelListener {
50 private static class ProblemPosition {
56 private boolean fIsActive= false;
57 private boolean fIsPainting= false;
58 private boolean fIsSettingModel= false;
60 private ITextEditor fTextEditor;
61 private ISourceViewer fSourceViewer;
62 private StyledText fTextWidget;
63 private IAnnotationModel fModel;
64 private List fProblemPositions= new ArrayList();
66 private Map fColorTable= new HashMap();
67 private Set fAnnotationSet= new HashSet();
71 public ProblemPainter(ITextEditor textEditor, ISourceViewer sourceViewer) {
72 fTextEditor= textEditor;
73 fSourceViewer= sourceViewer;
74 fTextWidget= sourceViewer.getTextWidget();
77 private boolean hasProblems() {
78 return !fProblemPositions.isEmpty();
81 private void enablePainting() {
82 if (!fIsPainting && hasProblems()) {
84 fTextWidget.addPaintListener(this);
85 handleDrawRequest(null);
89 private void disablePainting(boolean redraw) {
92 fTextWidget.removePaintListener(this);
93 if (redraw && hasProblems())
94 handleDrawRequest(null);
98 private void setModel(IAnnotationModel model) {
99 if (fModel != model) {
101 fModel.removeAnnotationModelListener(this);
103 if (fModel != null) {
105 fIsSettingModel= true;
106 fModel.addAnnotationModelListener(this);
108 fIsSettingModel= false;
114 private void catchupWithModel() {
115 if (fProblemPositions != null) {
116 fProblemPositions.clear();
117 if (fModel != null) {
119 Iterator e= new ProblemAnnotationIterator(fModel, true);
120 while (e.hasNext()) {
121 IProblemAnnotation pa= (IProblemAnnotation) e.next();
122 Annotation a= (Annotation) pa;
125 AnnotationType type= pa.getAnnotationType();
126 if (fAnnotationSet.contains(type))
127 color= (Color) fColorTable.get(type);
130 ProblemPosition pp= new ProblemPosition();
131 pp.fPosition= fModel.getPosition(a);
134 fProblemPositions.add(pp);
141 private void updatePainting() {
142 disablePainting(true);
148 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
150 public void modelChanged(final IAnnotationModel model) {
151 if (fTextWidget != null && !fTextWidget.isDisposed()) {
152 if (fIsSettingModel) {
153 // inside the ui thread -> no need for posting
156 Display d= fTextWidget.getDisplay();
158 d.asyncExec(new Runnable() {
160 if (fTextWidget != null && !fTextWidget.isDisposed())
169 public void setColor(AnnotationType annotationType, Color color) {
171 fColorTable.put(annotationType, color);
173 fColorTable.remove(annotationType);
176 public void paintAnnotations(AnnotationType annotationType, boolean paint) {
178 fAnnotationSet.add(annotationType);
180 fAnnotationSet.remove(annotationType);
183 public boolean isPaintingAnnotations() {
184 return !fAnnotationSet.isEmpty();
188 * @see IPainter#dispose()
190 public void dispose() {
192 if (fColorTable != null)
196 if (fAnnotationSet != null)
197 fAnnotationSet.clear();
198 fAnnotationSet= null;
202 fProblemPositions= null;
206 * Returns the document offset of the upper left corner of the widgets viewport,
207 * possibly including partially visible lines.
209 private int getInclusiveTopIndexStartOffset() {
211 if (fTextWidget != null && !fTextWidget.isDisposed()) {
212 int top= fSourceViewer.getTopIndex();
213 if ((fTextWidget.getTopPixel() % fTextWidget.getLineHeight()) != 0)
216 IDocument document= fSourceViewer.getDocument();
217 return document.getLineOffset(top);
218 } catch (BadLocationException ex) {
226 * @see PaintListener#paintControl(PaintEvent)
228 public void paintControl(PaintEvent event) {
229 if (fTextWidget != null)
230 handleDrawRequest(event.gc);
233 private void handleDrawRequest(GC gc) {
235 int vOffset= getInclusiveTopIndexStartOffset();
236 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
237 int vLength= fSourceViewer.getBottomIndexEndOffset() + 1;
239 for (Iterator e = fProblemPositions.iterator(); e.hasNext();) {
240 ProblemPosition pp = (ProblemPosition) e.next();
241 Position p= pp.fPosition;
242 if (p.overlapsWith(vOffset, vLength)) {
244 if (!pp.fMultiLine) {
246 IRegion widgetRange= getWidgetRange(p);
247 if (widgetRange != null)
248 draw(gc, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
252 IDocument document= fSourceViewer.getDocument();
255 int startLine= document.getLineOfOffset(p.getOffset());
256 int lastInclusive= Math.max(p.getOffset(), p.getOffset() + p.getLength() - 1);
257 int endLine= document.getLineOfOffset(lastInclusive);
259 for (int i= startLine; i <= endLine; i++) {
260 IRegion line= document.getLineInformation(i);
261 int paintStart= Math.max(line.getOffset(), p.getOffset());
262 int paintEnd= Math.min(line.getOffset() + line.getLength(), p.getOffset() + p.getLength());
263 if (paintEnd > paintStart) {
264 // otherwise inside a line delimiter
265 IRegion widgetRange= getWidgetRange(new Position(paintStart, paintEnd - paintStart));
266 if (widgetRange != null)
267 draw(gc, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
271 } catch (BadLocationException x) {
278 private IRegion getWidgetRange(Position p) {
279 if (fSourceViewer instanceof ITextViewerExtension3) {
281 ITextViewerExtension3 extension= (ITextViewerExtension3) fSourceViewer;
282 return extension.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength()));
286 IRegion region= fSourceViewer.getVisibleRegion();
287 int offset= region.getOffset();
288 int length= region.getLength();
290 if (p.overlapsWith(offset , length)) {
291 int p1= Math.max(offset, p.getOffset());
292 int p2= Math.min(offset + length, p.getOffset() + p.getLength());
293 return new Region(p1 - offset, p2 - p1);
300 private int[] computePolyline(Point left, Point right, int height) {
302 final int WIDTH= 4; // must be even
303 final int HEIGHT= 2; // can be any number
304 // final int MINPEEKS= 2; // minimal number of peeks
306 int peeks= (right.x - left.x) / WIDTH;
307 // if (peeks < MINPEEKS) {
308 // int missing= (MINPEEKS - peeks) * WIDTH;
309 // left.x= Math.max(0, left.x - missing/2);
315 // compute (number of point) * 2
316 int length= ((2 * peeks) + 1) * 2;
320 int[] coordinates= new int[length];
322 // cache peeks' y-coordinates
323 int bottom= left.y + height - 1;
324 int top= bottom - HEIGHT;
326 // populate array with peek coordinates
327 for (int i= 0; i < peeks; i++) {
329 coordinates[index]= leftX + (WIDTH * i);
330 coordinates[index+1]= bottom;
331 coordinates[index+2]= coordinates[index] + WIDTH/2;
332 coordinates[index+3]= top;
335 // the last down flank is missing
336 coordinates[length-2]= left.x + (WIDTH * peeks);
337 coordinates[length-1]= bottom;
342 private void draw(GC gc, int offset, int length, Color color) {
345 Point left= fTextWidget.getLocationAtOffset(offset);
346 Point right= fTextWidget.getLocationAtOffset(offset + length);
348 gc.setForeground(color);
349 int[] polyline= computePolyline(left, right, gc.getFontMetrics().getHeight());
350 gc.drawPolyline(polyline);
353 fTextWidget.redrawRange(offset, length, true);
358 * @see IPainter#deactivate(boolean)
360 public void deactivate(boolean redraw) {
363 disablePainting(redraw);
370 * @see IPainter#paint(int)
372 public void paint(int reason) {
375 IDocumentProvider provider= PHPeclipsePlugin.getDefault().getCompilationUnitDocumentProvider();
376 setModel(provider.getAnnotationModel(fTextEditor.getEditorInput()));
377 } else if (CONFIGURATION == reason || INTERNAL == reason)
382 * @see IPainter#setPositionManager(IPositionManager)
384 public void setPositionManager(IPositionManager manager) {