--- /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.ITextViewerExtension5;
+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 ITextViewerExtension5) {
+ ITextViewerExtension5 extension = (ITextViewerExtension5) 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) {
+ }
+}