--- /dev/null
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package net.sourceforge.phpeclipse.phpeditor;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewerExtension3;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelListener;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Highlights the temporary problems.
+ */
+public class ProblemPainter implements IPainter, PaintListener, IAnnotationModelListener {
+
+ private static class ProblemPosition {
+ Position fPosition;
+ Color fColor;
+ boolean fMultiLine;
+ };
+
+ private boolean fIsActive= false;
+ private boolean fIsPainting= false;
+ private boolean fIsSettingModel= false;
+
+ private ITextEditor fTextEditor;
+ private ISourceViewer fSourceViewer;
+ private StyledText fTextWidget;
+ private IAnnotationModel fModel;
+ private List fProblemPositions= new ArrayList();
+
+ private Map fColorTable= new HashMap();
+ private Set fAnnotationSet= new HashSet();
+
+
+
+ public ProblemPainter(ITextEditor textEditor, ISourceViewer sourceViewer) {
+ fTextEditor= textEditor;
+ fSourceViewer= sourceViewer;
+ fTextWidget= sourceViewer.getTextWidget();
+ }
+
+ private boolean hasProblems() {
+ return !fProblemPositions.isEmpty();
+ }
+
+ private void enablePainting() {
+ if (!fIsPainting && hasProblems()) {
+ fIsPainting= true;
+ fTextWidget.addPaintListener(this);
+ handleDrawRequest(null);
+ }
+ }
+
+ private void disablePainting(boolean redraw) {
+ if (fIsPainting) {
+ fIsPainting= false;
+ fTextWidget.removePaintListener(this);
+ if (redraw && hasProblems())
+ handleDrawRequest(null);
+ }
+ }
+
+ private void setModel(IAnnotationModel model) {
+ if (fModel != model) {
+ if (fModel != null)
+ fModel.removeAnnotationModelListener(this);
+ fModel= model;
+ if (fModel != null) {
+ try {
+ fIsSettingModel= true;
+ fModel.addAnnotationModelListener(this);
+ } finally {
+ fIsSettingModel= false;
+ }
+ }
+ }
+ }
+
+ private void catchupWithModel() {
+ if (fProblemPositions != null) {
+ fProblemPositions.clear();
+ if (fModel != null) {
+
+ Iterator e= new ProblemAnnotationIterator(fModel, true);
+ while (e.hasNext()) {
+ IProblemAnnotation pa= (IProblemAnnotation) e.next();
+ Annotation a= (Annotation) pa;
+
+ Color color= null;
+ AnnotationType type= pa.getAnnotationType();
+ if (fAnnotationSet.contains(type))
+ color= (Color) fColorTable.get(type);
+
+ if (color != null) {
+ ProblemPosition pp= new ProblemPosition();
+ pp.fPosition= fModel.getPosition(a);
+ pp.fColor= color;
+ pp.fMultiLine= true;
+ fProblemPositions.add(pp);
+ }
+ }
+ }
+ }
+ }
+
+ private void updatePainting() {
+ disablePainting(true);
+ catchupWithModel();
+ enablePainting();
+ }
+
+ /*
+ * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
+ */
+ public void modelChanged(final IAnnotationModel model) {
+ if (fTextWidget != null && !fTextWidget.isDisposed()) {
+ if (fIsSettingModel) {
+ // inside the ui thread -> no need for posting
+ updatePainting();
+ } else {
+ Display d= fTextWidget.getDisplay();
+ if (d != null) {
+ d.asyncExec(new Runnable() {
+ public void run() {
+ if (fTextWidget != null && !fTextWidget.isDisposed())
+ updatePainting();
+ }
+ });
+ }
+ }
+ }
+ }
+
+ public void setColor(AnnotationType annotationType, Color color) {
+ if (color != null)
+ fColorTable.put(annotationType, color);
+ else
+ fColorTable.remove(annotationType);
+ }
+
+ public void paintAnnotations(AnnotationType annotationType, boolean paint) {
+ if (paint)
+ fAnnotationSet.add(annotationType);
+ else
+ fAnnotationSet.remove(annotationType);
+ }
+
+ public boolean isPaintingAnnotations() {
+ return !fAnnotationSet.isEmpty();
+ }
+
+ /*
+ * @see IPainter#dispose()
+ */
+ public void dispose() {
+
+ if (fColorTable != null)
+ fColorTable.clear();
+ fColorTable= null;
+
+ if (fAnnotationSet != null)
+ fAnnotationSet.clear();
+ fAnnotationSet= null;
+
+ fTextWidget= null;
+ fModel= null;
+ fProblemPositions= null;
+ }
+
+ /*
+ * Returns the document offset of the upper left corner of the widgets viewport,
+ * possibly including partially visible lines.
+ */
+ private int getInclusiveTopIndexStartOffset() {
+
+ if (fTextWidget != null && !fTextWidget.isDisposed()) {
+ int top= fSourceViewer.getTopIndex();
+ if ((fTextWidget.getTopPixel() % fTextWidget.getLineHeight()) != 0)
+ top--;
+ try {
+ IDocument document= fSourceViewer.getDocument();
+ return document.getLineOffset(top);
+ } catch (BadLocationException ex) {
+ }
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see PaintListener#paintControl(PaintEvent)
+ */
+ public void paintControl(PaintEvent event) {
+ if (fTextWidget != null)
+ handleDrawRequest(event.gc);
+ }
+
+ private void handleDrawRequest(GC gc) {
+
+ int vOffset= getInclusiveTopIndexStartOffset();
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
+ int vLength= fSourceViewer.getBottomIndexEndOffset() + 1;
+
+ for (Iterator e = fProblemPositions.iterator(); e.hasNext();) {
+ ProblemPosition pp = (ProblemPosition) e.next();
+ Position p= pp.fPosition;
+ if (p.overlapsWith(vOffset, vLength)) {
+
+ if (!pp.fMultiLine) {
+
+ IRegion widgetRange= getWidgetRange(p);
+ if (widgetRange != null)
+ draw(gc, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
+
+ } else {
+
+ IDocument document= fSourceViewer.getDocument();
+ try {
+
+ int startLine= document.getLineOfOffset(p.getOffset());
+ int lastInclusive= Math.max(p.getOffset(), p.getOffset() + p.getLength() - 1);
+ int endLine= document.getLineOfOffset(lastInclusive);
+
+ for (int i= startLine; i <= endLine; i++) {
+ IRegion line= document.getLineInformation(i);
+ int paintStart= Math.max(line.getOffset(), p.getOffset());
+ int paintEnd= Math.min(line.getOffset() + line.getLength(), p.getOffset() + p.getLength());
+ if (paintEnd > paintStart) {
+ // otherwise inside a line delimiter
+ IRegion widgetRange= getWidgetRange(new Position(paintStart, paintEnd - paintStart));
+ if (widgetRange != null)
+ draw(gc, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
+ }
+ }
+
+ } catch (BadLocationException x) {
+ }
+ }
+ }
+ }
+ }
+
+ private IRegion getWidgetRange(Position p) {
+ if (fSourceViewer instanceof ITextViewerExtension3) {
+
+ ITextViewerExtension3 extension= (ITextViewerExtension3) fSourceViewer;
+ return extension.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength()));
+
+ } else {
+
+ IRegion region= fSourceViewer.getVisibleRegion();
+ int offset= region.getOffset();
+ int length= region.getLength();
+
+ if (p.overlapsWith(offset , length)) {
+ int p1= Math.max(offset, p.getOffset());
+ int p2= Math.min(offset + length, p.getOffset() + p.getLength());
+ return new Region(p1 - offset, p2 - p1);
+ }
+ }
+
+ return null;
+ }
+
+ private int[] computePolyline(Point left, Point right, int height) {
+
+ final int WIDTH= 4; // must be even
+ final int HEIGHT= 2; // can be any number
+// final int MINPEEKS= 2; // minimal number of peeks
+
+ int peeks= (right.x - left.x) / WIDTH;
+// if (peeks < MINPEEKS) {
+// int missing= (MINPEEKS - peeks) * WIDTH;
+// left.x= Math.max(0, left.x - missing/2);
+// peeks= MINPEEKS;
+// }
+
+ int leftX= left.x;
+
+ // compute (number of point) * 2
+ int length= ((2 * peeks) + 1) * 2;
+ if (length < 0)
+ return new int[0];
+
+ int[] coordinates= new int[length];
+
+ // cache peeks' y-coordinates
+ int bottom= left.y + height - 1;
+ int top= bottom - HEIGHT;
+
+ // populate array with peek coordinates
+ for (int i= 0; i < peeks; i++) {
+ int index= 4 * i;
+ coordinates[index]= leftX + (WIDTH * i);
+ coordinates[index+1]= bottom;
+ coordinates[index+2]= coordinates[index] + WIDTH/2;
+ coordinates[index+3]= top;
+ }
+
+ // the last down flank is missing
+ coordinates[length-2]= left.x + (WIDTH * peeks);
+ coordinates[length-1]= bottom;
+
+ return coordinates;
+ }
+
+ private void draw(GC gc, int offset, int length, Color color) {
+ if (gc != null) {
+
+ Point left= fTextWidget.getLocationAtOffset(offset);
+ Point right= fTextWidget.getLocationAtOffset(offset + length);
+
+ gc.setForeground(color);
+ int[] polyline= computePolyline(left, right, gc.getFontMetrics().getHeight());
+ gc.drawPolyline(polyline);
+
+ } else {
+ fTextWidget.redrawRange(offset, length, true);
+ }
+ }
+
+ /*
+ * @see IPainter#deactivate(boolean)
+ */
+ public void deactivate(boolean redraw) {
+ if (fIsActive) {
+ fIsActive= false;
+ disablePainting(redraw);
+ setModel(null);
+ catchupWithModel();
+ }
+ }
+
+ /*
+ * @see IPainter#paint(int)
+ */
+ public void paint(int reason) {
+ if (!fIsActive) {
+ fIsActive= true;
+ IDocumentProvider provider= PHPeclipsePlugin.getDefault().getCompilationUnitDocumentProvider();
+ setModel(provider.getAnnotationModel(fTextEditor.getEditorInput()));
+ } else if (CONFIGURATION == reason || INTERNAL == reason)
+ updatePainting();
+ }
+
+ /*
+ * @see IPainter#setPositionManager(IPositionManager)
+ */
+ public void setPositionManager(IPositionManager manager) {
+ }
+}