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) {