1 11 package org.eclipse.jface.text.source; 12 13 14 import java.util.ArrayList ; 15 import java.util.Collections ; 16 import java.util.Comparator ; 17 import java.util.HashMap ; 18 import java.util.HashSet ; 19 import java.util.Iterator ; 20 import java.util.List ; 21 import java.util.Map ; 22 import java.util.Set ; 23 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.custom.StyledText; 26 import org.eclipse.swt.events.DisposeEvent; 27 import org.eclipse.swt.events.DisposeListener; 28 import org.eclipse.swt.events.MouseEvent; 29 import org.eclipse.swt.events.MouseListener; 30 import org.eclipse.swt.events.MouseMoveListener; 31 import org.eclipse.swt.events.PaintEvent; 32 import org.eclipse.swt.events.PaintListener; 33 import org.eclipse.swt.graphics.Cursor; 34 import org.eclipse.swt.graphics.Font; 35 import org.eclipse.swt.graphics.GC; 36 import org.eclipse.swt.graphics.Image; 37 import org.eclipse.swt.graphics.Point; 38 import org.eclipse.swt.graphics.Rectangle; 39 import org.eclipse.swt.widgets.Canvas; 40 import org.eclipse.swt.widgets.Composite; 41 import org.eclipse.swt.widgets.Control; 42 import org.eclipse.swt.widgets.Display; 43 44 45 import org.eclipse.jface.text.BadLocationException; 46 import org.eclipse.jface.text.IDocument; 47 import org.eclipse.jface.text.IRegion; 48 import org.eclipse.jface.text.ITextListener; 49 import org.eclipse.jface.text.ITextViewer; 50 import org.eclipse.jface.text.ITextViewerExtension5; 51 import org.eclipse.jface.text.IViewportListener; 52 import org.eclipse.jface.text.JFaceTextUtil; 53 import org.eclipse.jface.text.Position; 54 import org.eclipse.jface.text.TextEvent; 55 import org.eclipse.jface.text.TextViewer; 56 57 58 67 public class AnnotationRulerColumn implements IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension { 68 69 72 class InternalListener implements IViewportListener, IAnnotationModelListener, ITextListener { 73 74 77 public void viewportChanged(int verticalPosition) { 78 if (verticalPosition != fScrollPos) 79 redraw(); 80 } 81 82 85 public void modelChanged(IAnnotationModel model) { 86 postRedraw(); 87 } 88 89 92 public void textChanged(TextEvent e) { 93 if (e.getViewerRedrawState()) 94 postRedraw(); 95 } 96 } 97 98 102 private static class ReusableRegion extends Position implements IRegion {} 103 104 109 private static class Tuple { 110 Annotation annotation; 111 Position position; 112 113 Tuple(Annotation annotation, Position position) { 114 this.annotation= annotation; 115 this.position= position; 116 } 117 } 118 119 123 private static class TupleComparator implements Comparator { 124 127 public int compare(Object o1, Object o2) { 128 Position p1= ((Tuple) o1).position; 129 Position p2= ((Tuple) o2).position; 130 return p1.getOffset() - p2.getOffset(); 131 } 132 } 133 134 135 private CompositeRuler fParentRuler; 136 137 private ITextViewer fCachedTextViewer; 138 139 private StyledText fCachedTextWidget; 140 141 private Canvas fCanvas; 142 143 private IAnnotationModel fModel; 144 145 private int fScrollPos; 146 147 private Image fBuffer; 148 149 private InternalListener fInternalListener= new InternalListener(); 150 151 private int fWidth; 152 153 private boolean fAllowSetModel= true; 154 158 private Set fConfiguredAnnotationTypes= new HashSet (); 159 165 private Map fAllowedAnnotationTypes= new HashMap (); 166 170 private IAnnotationAccessExtension fAnnotationAccessExtension; 171 175 private IAnnotationHover fHover; 176 180 private List fCachedAnnotations= new ArrayList (); 181 185 private Comparator fTupleComparator= new TupleComparator(); 186 190 private Cursor fHitDetectionCursor; 191 195 private Cursor fLastCursor; 196 200 private MouseListener fMouseListener; 201 202 210 public AnnotationRulerColumn(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) { 211 this(width, annotationAccess); 212 fAllowSetModel= false; 213 fModel= model; 214 fModel.addAnnotationModelListener(fInternalListener); 215 } 216 217 224 public AnnotationRulerColumn(int width, IAnnotationAccess annotationAccess) { 225 fWidth= width; 226 if (annotationAccess instanceof IAnnotationAccessExtension) 227 fAnnotationAccessExtension= (IAnnotationAccessExtension) annotationAccess; 228 } 229 230 236 public AnnotationRulerColumn(IAnnotationModel model, int width) { 237 fWidth= width; 238 fAllowSetModel= false; 239 fModel= model; 240 fModel.addAnnotationModelListener(fInternalListener); 241 } 242 243 248 public AnnotationRulerColumn(int width) { 249 fWidth= width; 250 } 251 252 255 public Control getControl() { 256 return fCanvas; 257 } 258 259 262 public int getWidth() { 263 return fWidth; 264 } 265 266 269 public Control createControl(CompositeRuler parentRuler, Composite parentControl) { 270 271 fParentRuler= parentRuler; 272 fCachedTextViewer= parentRuler.getTextViewer(); 273 fCachedTextWidget= fCachedTextViewer.getTextWidget(); 274 275 fHitDetectionCursor= new Cursor(parentControl.getDisplay(), SWT.CURSOR_HAND); 276 277 fCanvas= createCanvas(parentControl); 278 279 fCanvas.addPaintListener(new PaintListener() { 280 public void paintControl(PaintEvent event) { 281 if (fCachedTextViewer != null) 282 doubleBufferPaint(event.gc); 283 } 284 }); 285 286 fCanvas.addDisposeListener(new DisposeListener() { 287 public void widgetDisposed(DisposeEvent e) { 288 handleDispose(); 289 fCachedTextViewer= null; 290 fCachedTextWidget= null; 291 } 292 }); 293 294 fMouseListener= new MouseListener() { 295 public void mouseUp(MouseEvent event) { 296 int lineNumber; 297 if (isPropagatingMouseListener()) { 298 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 299 lineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); 300 } else 301 lineNumber= fParentRuler.toDocumentLineNumber(event.y); 302 303 if (1 == event.button) 304 mouseClicked(lineNumber); 305 } 306 307 public void mouseDown(MouseEvent event) { 308 if (isPropagatingMouseListener()) 309 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 310 } 311 312 public void mouseDoubleClick(MouseEvent event) { 313 int lineNumber; 314 if (isPropagatingMouseListener()) { 315 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 316 lineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); 317 } else 318 lineNumber= fParentRuler.toDocumentLineNumber(event.y); 319 320 if (1 == event.button) 321 mouseDoubleClicked(lineNumber); 322 } 323 }; 324 fCanvas.addMouseListener(fMouseListener); 325 326 fCanvas.addMouseMoveListener(new MouseMoveListener() { 327 331 public void mouseMove(MouseEvent e) { 332 handleMouseMove(e); 333 } 334 }); 335 336 if (fCachedTextViewer != null) { 337 fCachedTextViewer.addViewportListener(fInternalListener); 338 fCachedTextViewer.addTextListener(fInternalListener); 339 } 340 341 return fCanvas; 342 } 343 344 350 private Canvas createCanvas(Composite parent) { 351 return new Canvas(parent, SWT.NO_BACKGROUND) { 352 356 public void addMouseListener(MouseListener listener) { 357 if (isPropagatingMouseListener() || listener == fMouseListener) 358 super.addMouseListener(listener); 359 } 360 }; 361 } 362 363 370 protected boolean isPropagatingMouseListener() { 371 return true; 372 } 373 374 379 protected void mouseDoubleClicked(int rulerLine) { 380 } 381 382 388 protected void mouseClicked(int rulerLine) { 389 } 390 391 396 private void handleMouseMove(MouseEvent event) { 397 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 398 if (fCachedTextViewer != null) { 399 int line= toDocumentLineNumber(event.y); 400 Cursor cursor= (hasAnnotation(line) ? fHitDetectionCursor : null); 401 if (cursor != fLastCursor) { 402 fCanvas.setCursor(cursor); 403 fLastCursor= cursor; 404 } 405 } 406 } 407 408 414 protected boolean hasAnnotation(int lineNumber) { 415 416 IAnnotationModel model= fModel; 417 if (fModel instanceof IAnnotationModelExtension) 418 model= ((IAnnotationModelExtension)fModel).getAnnotationModel(SourceViewer.MODEL_ANNOTATION_MODEL); 419 420 if (model == null) 421 return false; 422 423 IRegion line; 424 try { 425 IDocument d= fCachedTextViewer.getDocument(); 426 if (d == null) 427 return false; 428 429 line= d.getLineInformation(lineNumber); 430 } catch (BadLocationException ex) { 431 return false; 432 } 433 434 int lineStart= line.getOffset(); 435 int lineLength= line.getLength(); 436 437 Iterator e= model.getAnnotationIterator(); 438 while (e.hasNext()) { 439 Annotation a= (Annotation) e.next(); 440 441 if (a.isMarkedDeleted()) 442 continue; 443 444 if (skip(a)) 445 continue; 446 447 Position p= model.getPosition(a); 448 if (p == null || p.isDeleted()) 449 continue; 450 451 if (p.overlapsWith(lineStart, lineLength) || p.length == 0 && p.offset == lineStart + lineLength) 452 return true; 453 } 454 455 return false; 456 } 457 458 461 private void handleDispose() { 462 463 if (fCachedTextViewer != null) { 464 fCachedTextViewer.removeViewportListener(fInternalListener); 465 fCachedTextViewer.removeTextListener(fInternalListener); 466 } 467 468 if (fModel != null) 469 fModel.removeAnnotationModelListener(fInternalListener); 470 471 if (fBuffer != null) { 472 fBuffer.dispose(); 473 fBuffer= null; 474 } 475 476 if (fHitDetectionCursor != null) { 477 fHitDetectionCursor.dispose(); 478 fHitDetectionCursor= null; 479 } 480 481 fConfiguredAnnotationTypes.clear(); 482 fAllowedAnnotationTypes.clear(); 483 fAnnotationAccessExtension= null; 484 } 485 486 491 private void doubleBufferPaint(GC dest) { 492 493 Point size= fCanvas.getSize(); 494 495 if (size.x <= 0 || size.y <= 0) 496 return; 497 498 if (fBuffer != null) { 499 Rectangle r= fBuffer.getBounds(); 500 if (r.width != size.x || r.height != size.y) { 501 fBuffer.dispose(); 502 fBuffer= null; 503 } 504 } 505 if (fBuffer == null) 506 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); 507 508 GC gc= new GC(fBuffer); 509 gc.setFont(fCachedTextWidget.getFont()); 510 try { 511 gc.setBackground(fCanvas.getBackground()); 512 gc.fillRectangle(0, 0, size.x, size.y); 513 514 if (fCachedTextViewer instanceof ITextViewerExtension5) 515 doPaint1(gc); 516 else 517 doPaint(gc); 518 } finally { 519 gc.dispose(); 520 } 521 522 dest.drawImage(fBuffer, 0, 0); 523 } 524 525 531 protected int getInclusiveTopIndexStartOffset() { 532 if (fCachedTextWidget == null || fCachedTextWidget.isDisposed()) 533 return -1; 534 535 IDocument document= fCachedTextViewer.getDocument(); 536 if (document == null) 537 return -1; 538 539 int top= JFaceTextUtil.getPartialTopIndex(fCachedTextViewer); 540 try { 541 return document.getLineOffset(top); 542 } catch (BadLocationException x) { 543 return -1; 544 } 545 } 546 547 553 private int getExclusiveBottomIndexEndOffset() { 554 if (fCachedTextWidget == null || fCachedTextWidget.isDisposed()) 555 return -1; 556 557 IDocument document= fCachedTextViewer.getDocument(); 558 if (document == null) 559 return -1; 560 561 int bottom= JFaceTextUtil.getPartialBottomIndex(fCachedTextViewer); 562 try { 563 if (bottom >= document.getNumberOfLines()) 564 bottom= document.getNumberOfLines() - 1; 565 return document.getLineOffset(bottom) + document.getLineLength(bottom); 566 } catch (BadLocationException x) { 567 return -1; 568 } 569 } 570 571 576 protected void doPaint(GC gc) { 577 578 if (fModel == null || fCachedTextViewer == null) 579 return; 580 581 int topLeft= getInclusiveTopIndexStartOffset(); 582 int bottomRight; 583 584 IRegion coverage= null; 585 if (fCachedTextViewer instanceof ITextViewerExtension5) { 586 ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer; 587 coverage= extension.getModelCoverage(); 588 } else if (fCachedTextViewer instanceof TextViewer) { 589 TextViewer extension= (TextViewer) fCachedTextViewer; 591 coverage= extension.getModelCoverage(); 592 } 593 594 if (coverage != null) 595 bottomRight= coverage.getOffset() + coverage.getLength(); 596 else { 597 bottomRight= fCachedTextViewer.getBottomIndexEndOffset() + 1; 601 } 602 int viewPort= bottomRight - topLeft; 603 604 fScrollPos= fCachedTextWidget.getTopPixel(); 605 Point dimension= fCanvas.getSize(); 606 607 IDocument doc= fCachedTextViewer.getDocument(); 608 if (doc == null) 609 return; 610 611 int topLine= -1, bottomLine= -1; 612 try { 613 IRegion region= fCachedTextViewer.getVisibleRegion(); 614 topLine= doc.getLineOfOffset(region.getOffset()); 615 bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); 616 } catch (BadLocationException x) { 617 return; 618 } 619 620 Rectangle r= new Rectangle(0, 0, 0, 0); 622 int maxLayer= 1; 624 for (int layer= 0; layer < maxLayer; layer++) { 625 Iterator iter= fModel.getAnnotationIterator(); 626 while (iter.hasNext()) { 627 Annotation annotation= (Annotation) iter.next(); 628 629 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; 630 if (fAnnotationAccessExtension != null) 631 lay= fAnnotationAccessExtension.getLayer(annotation); 632 maxLayer= Math.max(maxLayer, lay+1); if (lay != layer) continue; 635 636 if (skip(annotation)) 637 continue; 638 639 Position position= fModel.getPosition(annotation); 640 if (position == null) 641 continue; 642 643 int viewPortSize= position.getLength() == 0 ? viewPort + 1 : viewPort; 647 if (!position.overlapsWith(topLeft, viewPortSize)) 648 continue; 649 650 try { 651 652 int offset= position.getOffset(); 653 int length= position.getLength(); 654 655 int startLine= doc.getLineOfOffset(offset); 656 if (startLine < topLine) 657 startLine= topLine; 658 659 int endLine= startLine; 660 if (length > 0) 661 endLine= doc.getLineOfOffset(offset + length - 1); 662 if (endLine > bottomLine) 663 endLine= bottomLine; 664 665 startLine -= topLine; 666 endLine -= topLine; 667 668 r.x= 0; 669 r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine) - fScrollPos; 670 671 r.width= dimension.x; 672 int lines= endLine - startLine; 673 674 r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1); 675 676 if (r.y < dimension.y && fAnnotationAccessExtension != null) fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r); 678 679 } catch (BadLocationException x) { 680 } 681 } 682 } 683 } 684 685 691 protected void doPaint1(GC gc) { 692 693 if (fModel == null || fCachedTextViewer == null) 694 return; 695 696 ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer; 697 698 fScrollPos= fCachedTextWidget.getTopPixel(); 699 Point dimension= fCanvas.getSize(); 700 701 int vOffset= getInclusiveTopIndexStartOffset(); 702 int vLength= getExclusiveBottomIndexEndOffset() - vOffset; 703 704 Rectangle r= new Rectangle(0, 0, 0, 0); 706 ReusableRegion range= new ReusableRegion(); 707 708 int minLayer= Integer.MAX_VALUE, maxLayer= Integer.MIN_VALUE; 709 fCachedAnnotations.clear(); 710 Iterator iter= fModel.getAnnotationIterator(); 711 while (iter.hasNext()) { 712 Annotation annotation= (Annotation) iter.next(); 713 714 if (skip(annotation)) 715 continue; 716 717 Position position= fModel.getPosition(annotation); 718 if (position == null) 719 continue; 720 721 if (!position.overlapsWith(vOffset, vLength)) 722 continue; 723 724 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; 725 if (fAnnotationAccessExtension != null) 726 lay= fAnnotationAccessExtension.getLayer(annotation); 727 728 minLayer= Math.min(minLayer, lay); 729 maxLayer= Math.max(maxLayer, lay); 730 fCachedAnnotations.add(new Tuple(annotation, position)); 731 } 732 Collections.sort(fCachedAnnotations, fTupleComparator); 733 734 for (int layer= minLayer; layer <= maxLayer; layer++) { 735 for (int i= 0, n= fCachedAnnotations.size(); i < n; i++) { 736 Tuple tuple= (Tuple) fCachedAnnotations.get(i); 737 Annotation annotation= tuple.annotation; 738 Position position= tuple.position; 739 740 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; 741 if (fAnnotationAccessExtension != null) 742 lay= fAnnotationAccessExtension.getLayer(annotation); 743 if (lay != layer) continue; 745 746 range.setOffset(position.getOffset()); 747 range.setLength(position.getLength()); 748 IRegion widgetRegion= extension.modelRange2WidgetRange(range); 749 if (widgetRegion == null) 750 continue; 751 752 int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset()); 753 if (startLine == -1) 754 continue; 755 756 int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0)); 757 if (endLine == -1) 758 continue; 759 760 r.x= 0; 761 r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine) - fScrollPos; 762 763 r.width= dimension.x; 764 int lines= endLine - startLine; 765 r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1); 766 767 if (r.y < dimension.y && fAnnotationAccessExtension != null) fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r); 769 } 770 } 771 772 fCachedAnnotations.clear(); 773 } 774 775 776 779 private void postRedraw() { 780 if (fCanvas != null && !fCanvas.isDisposed()) { 781 Display d= fCanvas.getDisplay(); 782 if (d != null) { 783 d.asyncExec(new Runnable () { 784 public void run() { 785 redraw(); 786 } 787 }); 788 } 789 } 790 } 791 792 795 public void redraw() { 796 if (fCanvas != null && !fCanvas.isDisposed()) { 797 GC gc= new GC(fCanvas); 798 doubleBufferPaint(gc); 799 gc.dispose(); 800 } 801 } 802 803 806 public void setModel(IAnnotationModel model) { 807 if (fAllowSetModel && model != fModel) { 808 809 if (fModel != null) 810 fModel.removeAnnotationModelListener(fInternalListener); 811 812 fModel= model; 813 814 if (fModel != null) 815 fModel.addAnnotationModelListener(fInternalListener); 816 817 postRedraw(); 818 } 819 } 820 821 824 public void setFont(Font font) { 825 } 826 827 832 protected ITextViewer getCachedTextViewer() { 833 return fCachedTextViewer; 834 } 835 836 839 public IAnnotationModel getModel() { 840 return fModel; 841 } 842 843 851 public void addAnnotationType(Object annotationType) { 852 fConfiguredAnnotationTypes.add(annotationType); 853 fAllowedAnnotationTypes.clear(); 854 } 855 856 860 public int getLineOfLastMouseButtonActivity() { 861 return fParentRuler.getLineOfLastMouseButtonActivity(); 862 } 863 864 868 public int toDocumentLineNumber(int y_coordinate) { 869 return fParentRuler.toDocumentLineNumber(y_coordinate); 870 } 871 872 880 public void removeAnnotationType(Object annotationType) { 881 fConfiguredAnnotationTypes.remove(annotationType); 882 fAllowedAnnotationTypes.clear(); 883 } 884 885 894 private boolean skip(Annotation annotation) { 895 Object annotationType= annotation.getType(); 896 Boolean allowed= (Boolean ) fAllowedAnnotationTypes.get(annotationType); 897 if (allowed != null) 898 return !allowed.booleanValue(); 899 900 boolean skip= skip(annotationType); 901 fAllowedAnnotationTypes.put(annotationType, !skip ? Boolean.TRUE : Boolean.FALSE); 902 return skip; 903 } 904 905 914 private boolean skip(Object annotationType) { 915 if (fAnnotationAccessExtension != null) { 916 Iterator e= fConfiguredAnnotationTypes.iterator(); 917 while (e.hasNext()) { 918 if (fAnnotationAccessExtension.isSubtype(annotationType, e.next())) 919 return false; 920 } 921 return true; 922 } 923 return !fConfiguredAnnotationTypes.contains(annotationType); 924 } 925 926 930 public IAnnotationHover getHover() { 931 return fHover; 932 } 933 934 938 public void setHover(IAnnotationHover hover) { 939 fHover= hover; 940 } 941 942 946 public void addVerticalRulerListener(IVerticalRulerListener listener) { 947 throw new UnsupportedOperationException (); 948 } 949 950 954 public void removeVerticalRulerListener(IVerticalRulerListener listener) { 955 throw new UnsupportedOperationException (); 956 } 957 } 958 | Popular Tags |