1 11 package org.eclipse.jface.internal.text.revisions; 12 13 import java.util.ArrayList ; 14 import java.util.Arrays ; 15 import java.util.Collections ; 16 import java.util.HashMap ; 17 import java.util.Iterator ; 18 import java.util.List ; 19 import java.util.ListIterator ; 20 import java.util.Map ; 21 import java.util.Map.Entry; 22 23 import org.eclipse.swt.SWT; 24 import org.eclipse.swt.custom.StyledText; 25 import org.eclipse.swt.events.DisposeEvent; 26 import org.eclipse.swt.events.DisposeListener; 27 import org.eclipse.swt.events.MouseEvent; 28 import org.eclipse.swt.events.MouseListener; 29 import org.eclipse.swt.events.MouseMoveListener; 30 import org.eclipse.swt.events.MouseTrackListener; 31 import org.eclipse.swt.graphics.Color; 32 import org.eclipse.swt.graphics.FontMetrics; 33 import org.eclipse.swt.graphics.GC; 34 import org.eclipse.swt.graphics.Point; 35 import org.eclipse.swt.graphics.RGB; 36 import org.eclipse.swt.graphics.Rectangle; 37 import org.eclipse.swt.widgets.Canvas; 38 import org.eclipse.swt.widgets.Control; 39 import org.eclipse.swt.widgets.Display; 40 import org.eclipse.swt.widgets.Event; 41 import org.eclipse.swt.widgets.Listener; 42 import org.eclipse.swt.widgets.Shell; 43 44 import org.eclipse.core.runtime.Assert; 45 import org.eclipse.core.runtime.ListenerList; 46 import org.eclipse.core.runtime.Platform; 47 48 import org.eclipse.jface.internal.text.html.BrowserInformationControl; 49 import org.eclipse.jface.internal.text.html.HTMLPrinter; 50 import org.eclipse.jface.internal.text.html.HTMLTextPresenter; 51 52 import org.eclipse.jface.text.AbstractReusableInformationControlCreator; 53 import org.eclipse.jface.text.BadLocationException; 54 import org.eclipse.jface.text.DefaultInformationControl; 55 import org.eclipse.jface.text.IDocument; 56 import org.eclipse.jface.text.IInformationControl; 57 import org.eclipse.jface.text.IInformationControlCreator; 58 import org.eclipse.jface.text.IRegion; 59 import org.eclipse.jface.text.ITextViewer; 60 import org.eclipse.jface.text.ITextViewerExtension5; 61 import org.eclipse.jface.text.JFaceTextUtil; 62 import org.eclipse.jface.text.Position; 63 import org.eclipse.jface.text.Region; 64 import org.eclipse.jface.text.information.IInformationProviderExtension2; 65 import org.eclipse.jface.text.revisions.IRevisionListener; 66 import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension; 67 import org.eclipse.jface.text.revisions.Revision; 68 import org.eclipse.jface.text.revisions.RevisionEvent; 69 import org.eclipse.jface.text.revisions.RevisionInformation; 70 import org.eclipse.jface.text.revisions.RevisionRange; 71 import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode; 72 import org.eclipse.jface.text.source.Annotation; 73 import org.eclipse.jface.text.source.CompositeRuler; 74 import org.eclipse.jface.text.source.IAnnotationHover; 75 import org.eclipse.jface.text.source.IAnnotationHoverExtension; 76 import org.eclipse.jface.text.source.IAnnotationHoverExtension2; 77 import org.eclipse.jface.text.source.IAnnotationModel; 78 import org.eclipse.jface.text.source.IAnnotationModelExtension; 79 import org.eclipse.jface.text.source.IAnnotationModelListener; 80 import org.eclipse.jface.text.source.IChangeRulerColumn; 81 import org.eclipse.jface.text.source.ILineDiffer; 82 import org.eclipse.jface.text.source.ILineRange; 83 import org.eclipse.jface.text.source.ISharedTextColors; 84 import org.eclipse.jface.text.source.ISourceViewer; 85 import org.eclipse.jface.text.source.IVerticalRulerColumn; 86 import org.eclipse.jface.text.source.LineRange; 87 88 89 95 public final class RevisionPainter { 96 97 private static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text.source/debug/RevisionRulerColumn")); 99 private static final RGB BY_DATE_START_COLOR= new RGB(199, 134, 57); 101 private static final RGB BY_DATE_END_COLOR= new RGB(241, 225, 206); 102 103 104 107 private static final class RevisionAnnotation extends Annotation { 108 public RevisionAnnotation(String text) { 109 super("org.eclipse.ui.workbench.texteditor.revisionAnnotation", false, text); } 111 } 112 113 117 private final class ColorTool { 118 123 private static final float AVERAGE_INTENSITY= 0.5f; 124 128 private static final float MAX_SHADING= 0.7f; 129 132 private static final float MIN_SHADING= 0.2f; 133 136 private static final float FOCUS_COLOR_SHADING= 1f; 137 138 139 142 private List fRevisions; 143 146 private final Map fColors= new HashMap (); 147 150 private final Map fFocusColors= new HashMap (); 151 152 157 public void setInfo(RevisionInformation info) { 158 fRevisions= null; 159 fColors.clear(); 160 fFocusColors.clear(); 161 162 if (info == null) 163 return; 164 List revisions= new ArrayList (); 165 for (Iterator it= info.getRevisions().iterator(); it.hasNext();) { 166 Revision revision= (Revision) it.next(); 167 revisions.add(new Long (computeAge(revision))); 168 } 169 Collections.sort(revisions); 170 fRevisions= revisions; 171 } 172 173 private RGB adaptColor(Revision revision, boolean focus) { 174 RGB rgb; 175 float scale; 176 if (fRenderingMode == IRevisionRulerColumnExtension.AGE) { 177 int index= computeAgeIndex(revision); 178 if (index == -1 || fRevisions.size() == 0) { 179 rgb= getBackground().getRGB(); 180 } else { 181 RGB[] gradient= Colors.palette(BY_DATE_START_COLOR, BY_DATE_END_COLOR, fRevisions.size()); 183 rgb= gradient[gradient.length - index - 1]; 184 } 185 scale= 0.99f; 186 } else if (fRenderingMode == IRevisionRulerColumnExtension.AUTHOR) { 187 rgb= revision.getColor(); 188 rgb= Colors.adjustBrightness(rgb, AVERAGE_INTENSITY); 189 scale= 0.6f; 190 } else if (fRenderingMode == IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE) { 191 rgb= revision.getColor(); 192 rgb= Colors.adjustBrightness(rgb, AVERAGE_INTENSITY); 193 int index= computeAgeIndex(revision); 194 int size= fRevisions.size(); 195 if (index == -1 || size < 2) 198 scale= 0.5f; 199 else 200 scale= (float) index / (size - 1); 201 } else { 202 Assert.isTrue(false); 203 return null; } 205 rgb= getShadedColor(rgb, scale, focus); 206 return rgb; 207 } 208 209 private int computeAgeIndex(Revision revision) { 210 long age= computeAge(revision); 211 int index= fRevisions.indexOf(new Long (age)); 212 return index; 213 } 214 215 private RGB getShadedColor(RGB color, float scale, boolean focus) { 216 Assert.isLegal(scale >= 0.0); 217 Assert.isLegal(scale <= 1.0); 218 RGB background= getBackground().getRGB(); 219 220 boolean makeIntense= getWidth() <= 15; 223 float intensityShift= makeIntense ? 0.3f : 0f; 224 float max= MAX_SHADING + intensityShift; 225 float min= MIN_SHADING + intensityShift; 226 scale= (max - min) * scale + min; 227 228 if (focus) { 230 scale += FOCUS_COLOR_SHADING; 231 if (scale > 1) { 232 background= new RGB(255 - background.red, 255 - background.green, 255 - background.blue); 233 scale= 2 - scale; 234 } 235 } 236 237 return Colors.blend(background, color, scale); 238 } 239 240 private long computeAge(Revision revision) { 241 return revision.getDate().getTime(); 242 } 243 244 251 public RGB getColor(Revision revision, boolean focus) { 252 Map map= focus ? fFocusColors : fColors; 253 RGB color= (RGB) map.get(revision); 254 if (color != null) 255 return color; 256 257 color= adaptColor(revision, focus); 258 map.put(revision, color); 259 return color; 260 } 261 } 262 263 266 private class MouseHandler implements MouseListener, MouseMoveListener, MouseTrackListener, Listener { 267 268 private RevisionRange fMouseDownRegion; 269 270 273 public void mouseUp(MouseEvent e) { 274 if (e.button == 1) { 275 RevisionRange upRegion= fFocusRange; 276 RevisionRange downRegion= fMouseDownRegion; 277 fMouseDownRegion= null; 278 279 if (upRegion == downRegion) { 280 Revision revision= upRegion == null ? null : upRegion.getRevision(); 281 if (revision == fSelectedRevision) 282 revision= null; handleRevisionSelected(revision); 284 } 285 } 286 } 287 288 291 public void mouseDown(MouseEvent e) { 292 if (e.button == 3) 293 updateFocusRevision(null); if (e.button == 1) { 295 fMouseDownRegion= fFocusRange; 296 postRedraw(); 297 } 298 } 299 300 303 public void mouseDoubleClick(MouseEvent e) { 304 } 305 306 309 public void handleEvent(Event event) { 310 Assert.isTrue(event.type == SWT.MouseWheel); 311 handleMouseWheel(event); 312 } 313 314 317 public void mouseEnter(MouseEvent e) { 318 updateFocusLine(toDocumentLineNumber(e.y)); 319 } 320 321 324 public void mouseExit(MouseEvent e) { 325 updateFocusLine(-1); 326 } 327 328 331 public void mouseHover(MouseEvent e) { 332 } 333 334 337 public void mouseMove(MouseEvent e) { 338 updateFocusLine(toDocumentLineNumber(e.y)); 339 } 340 } 341 342 345 private class AnnotationListener implements IAnnotationModelListener { 346 349 public void modelChanged(IAnnotationModel model) { 350 clearRangeCache(); 351 postRedraw(); 352 } 353 354 } 355 356 359 private static final class HoverInformationControlCreator extends AbstractReusableInformationControlCreator { 360 private boolean fIsFocusable; 361 362 public HoverInformationControlCreator(boolean isFocusable) { 363 fIsFocusable= isFocusable; 364 } 365 366 369 protected IInformationControl doCreateInformationControl(Shell parent) { 370 int style= fIsFocusable ? SWT.V_SCROLL | SWT.H_SCROLL : SWT.NONE; 371 372 if (BrowserInformationControl.isAvailable(parent)) { 373 final int shellStyle= SWT.TOOL | (fIsFocusable ? SWT.RESIZE : SWT.NO_TRIM); 374 return new BrowserInformationControl(parent, shellStyle, style) { 375 379 public void setInformation(String content) { 380 content= addCSSToHTMLFragment(content); 381 super.setInformation(content); 382 } 383 384 391 private String addCSSToHTMLFragment(String html) { 392 int max= Math.min(100, html.length()); 393 if (html.substring(0, max).indexOf("<html>") != -1) return html; 396 397 StringBuffer info= new StringBuffer (512 + html.length()); 398 HTMLPrinter.insertPageProlog(info, 0, fgStyleSheet); 399 info.append(html); 400 HTMLPrinter.addPageEpilog(info); 401 return info.toString(); 402 } 403 404 }; 405 } 406 return new DefaultInformationControl(parent, style, new HTMLTextPresenter()); 407 } 408 } 409 410 private static final String fgStyleSheet= "/* Font definitions */\n" + "body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-family: sans-serif; font-size: 9pt }\n" + "pre { font-family: monospace; font-size: 9pt }\n" + "\n" + "/* Margins */\n" + "body { overflow: auto; margin-top: 0; margin-bottom: 4; margin-left: 3; margin-right: 0 }\n" + "h1 { margin-top: 5; margin-bottom: 1 } \n" + "h2 { margin-top: 25; margin-bottom: 3 }\n" + "h3 { margin-top: 20; margin-bottom: 3 }\n" + "h4 { margin-top: 20; margin-bottom: 3 }\n" + "h5 { margin-top: 0; margin-bottom: 0 }\n" + "p { margin-top: 10px; margin-bottom: 10px }\n" + "pre { margin-left: 6 }\n" + "ul { margin-top: 0; margin-bottom: 10 }\n" + "li { margin-top: 0; margin-bottom: 0 } \n" + "li p { margin-top: 0; margin-bottom: 0 } \n" + "ol { margin-top: 0; margin-bottom: 10 }\n" + "dl { margin-top: 0; margin-bottom: 10 }\n" + "dt { margin-top: 0; margin-bottom: 0; font-weight: bold }\n" + "dd { margin-top: 0; margin-bottom: 0 }\n" + "\n" + "/* Styles and colors */\n" + "a:link { color: #0000FF }\n" + "a:hover { color: #000080 }\n" + "a:visited { text-decoration: underline }\n" + "h4 { font-style: italic }\n" + "strong { font-weight: bold }\n" + "em { font-style: italic }\n" + "var { font-style: italic }\n" + "th { font-weight: bold }\n" + ""; 442 445 private final class RevisionHover implements IAnnotationHover, IAnnotationHoverExtension, IAnnotationHoverExtension2, IInformationProviderExtension2 { 446 447 451 public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) { 452 Object info= getHoverInfo(sourceViewer, getHoverLineRange(sourceViewer, lineNumber), 0); 453 return info == null ? null : info.toString(); 454 } 455 456 459 public IInformationControlCreator getHoverControlCreator() { 460 RevisionInformation revisionInfo= fRevisionInfo; 461 if (revisionInfo != null) { 462 IInformationControlCreator creator= revisionInfo.getHoverControlCreator(); 463 if (creator != null) 464 return creator; 465 } 466 return new HoverInformationControlCreator(false); 467 } 468 469 472 public boolean canHandleMouseCursor() { 473 return false; 474 } 475 476 479 public boolean canHandleMouseWheel() { 480 return true; 481 } 482 483 487 public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleNumberOfLines) { 488 RevisionRange range= getRange(lineRange.getStartLine()); 489 Object info= range == null ? null : range.getRevision().getHoverInfo(); 490 return info; 491 } 492 493 497 public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) { 498 RevisionRange range= getRange(lineNumber); 499 return range == null ? null : new LineRange(lineNumber, 1); 500 } 501 502 505 public IInformationControlCreator getInformationPresenterControlCreator() { 506 RevisionInformation revisionInfo= fRevisionInfo; 507 if (revisionInfo != null) { 508 IInformationControlCreator creator= revisionInfo.getInformationPresenterControlCreator(); 509 if (creator != null) 510 return creator; 511 } 512 return new HoverInformationControlCreator(true); 513 } 514 } 515 516 517 518 519 private final ISharedTextColors fSharedColors; 520 521 private final ColorTool fColorTool= new ColorTool(); 522 523 private final MouseHandler fMouseHandler= new MouseHandler(); 524 525 private final RevisionHover fHover= new RevisionHover(); 526 527 private final AnnotationListener fAnnotationListener= new AnnotationListener(); 528 529 private final RevisionSelectionProvider fRevisionSelectionProvider= new RevisionSelectionProvider(this); 530 534 private final ListenerList fRevisionListeners= new ListenerList(); 535 536 537 538 539 private final IVerticalRulerColumn fColumn; 540 541 private CompositeRuler fParentRuler; 542 543 private Control fControl; 544 545 private ITextViewer fViewer; 546 547 private StyledText fWidget; 548 549 550 551 552 private RevisionInformation fRevisionInfo; 553 554 private ILineDiffer fLineDiffer= null; 555 556 private IAnnotationModel fAnnotationModel= null; 557 558 private Color fBackground; 559 560 561 562 563 private List fRevisionRanges= null; 564 565 private List fAnnotations= new ArrayList (); 566 567 568 569 570 private int fFocusLine= -1; 571 572 private RevisionRange fFocusRange= null; 573 574 private Revision fFocusRevision= null; 575 583 private Revision fSelectedRevision= null; 584 585 private boolean fWheelHandlerInstalled= false; 586 589 private RenderingMode fRenderingMode= IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE; 590 594 private int fRequiredWidth= -1; 595 599 private int fRevisionIdChars= 0; 600 604 private boolean fShowRevision= false; 605 609 private boolean fShowAuthor= false; 610 614 private int fAuthorInset; 615 619 private int fLastWidth= -1; 620 621 628 public RevisionPainter(IVerticalRulerColumn column, ISharedTextColors sharedColors) { 629 Assert.isLegal(column != null); 630 Assert.isLegal(sharedColors != null); 631 fColumn= column; 632 fSharedColors= sharedColors; 633 } 634 635 640 public void setRevisionInformation(RevisionInformation info) { 641 if (fRevisionInfo != info) { 642 fRequiredWidth= -1; 643 fRevisionIdChars= 0; 644 fRevisionInfo= info; 645 clearRangeCache(); 646 updateFocusRange(null); 647 handleRevisionSelected((Revision) null); 648 fColorTool.setInfo(info); 649 postRedraw(); 650 informListeners(); 651 } 652 } 653 654 660 public void setRenderingMode(RenderingMode renderingMode) { 661 Assert.isLegal(renderingMode != null); 662 if (fRenderingMode != renderingMode) { 663 fRenderingMode= renderingMode; 664 fColorTool.setInfo(fRevisionInfo); 665 postRedraw(); 666 } 667 } 668 669 675 public void setBackground(Color background) { 676 fBackground= background; 677 } 678 679 685 public void setParentRuler(CompositeRuler parentRuler) { 686 fParentRuler= parentRuler; 687 } 688 689 698 public void paint(GC gc, ILineRange visibleLines) { 699 connectIfNeeded(); 700 if (!isConnected()) 701 return; 702 703 if (fShowAuthor && fShowRevision) { 706 char[] string= new char[fRevisionIdChars + 1]; 707 Arrays.fill(string, '9'); 708 if (string.length > 1) { 709 string[0]= '.'; 710 string[1]= ' '; 711 } 712 fAuthorInset= gc.stringExtent(new String (string)).x; 713 } 714 715 int width= getWidth(); 717 if (width != fLastWidth) { 718 fColorTool.setInfo(fRevisionInfo); 719 fLastWidth= width; 720 } 721 722 List ranges= getRanges(visibleLines); 724 for (Iterator it= ranges.iterator(); it.hasNext();) { 725 RevisionRange region= (RevisionRange) it.next(); 726 paintRange(region, gc); 727 } 728 } 729 730 734 private void connectIfNeeded() { 735 if (isConnected() || fParentRuler == null) 736 return; 737 738 fViewer= fParentRuler.getTextViewer(); 739 if (fViewer == null) 740 return; 741 742 fWidget= fViewer.getTextWidget(); 743 if (fWidget == null) 744 return; 745 746 fControl= fColumn.getControl(); 747 if (fControl == null) 748 return; 749 750 fControl.addMouseTrackListener(fMouseHandler); 751 fControl.addMouseMoveListener(fMouseHandler); 752 fControl.addMouseListener(fMouseHandler); 753 fControl.addDisposeListener(new DisposeListener() { 754 757 public void widgetDisposed(DisposeEvent e) { 758 handleDispose(); 759 } 760 }); 761 762 fRevisionSelectionProvider.install(fViewer); 763 } 764 765 770 private boolean isConnected() { 771 return fControl != null; 772 } 773 774 780 public void setModel(IAnnotationModel model) { 781 IAnnotationModel diffModel; 782 if (model instanceof IAnnotationModelExtension) 783 diffModel= ((IAnnotationModelExtension) model).getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID); 784 else 785 diffModel= model; 786 787 setDiffer(diffModel); 788 setAnnotationModel(model); 789 } 790 791 796 private void setAnnotationModel(IAnnotationModel model) { 797 if (fAnnotationModel != model) 798 fAnnotationModel= model; 799 } 800 801 806 private void setDiffer(IAnnotationModel differ) { 807 if (differ instanceof ILineDiffer || differ == null) { 808 if (fLineDiffer != differ) { 809 if (fLineDiffer != null) 810 ((IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); 811 fLineDiffer= (ILineDiffer) differ; 812 if (fLineDiffer != null) 813 ((IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener); 814 } 815 } 816 } 817 818 821 private void handleDispose() { 822 updateFocusLine(-1); 823 824 if (fLineDiffer != null) { 825 ((IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); 826 fLineDiffer= null; 827 } 828 829 fRevisionSelectionProvider.uninstall(); 830 } 831 832 838 private void paintRange(RevisionRange range, GC gc) { 839 ILineRange widgetRange= modelLinesToWidgetLines(range); 840 if (widgetRange == null) 841 return; 842 843 Revision revision= range.getRevision(); 844 boolean drawArmedFocus= range == fMouseHandler.fMouseDownRegion; 845 boolean drawSelection= !drawArmedFocus && revision == fSelectedRevision; 846 boolean drawFocus= !drawSelection && !drawArmedFocus && revision == fFocusRevision; 847 Rectangle box= computeBoxBounds(widgetRange); 848 849 gc.setBackground(lookupColor(revision, false)); 850 if (drawArmedFocus) { 851 Color foreground= gc.getForeground(); 852 Color focusColor= lookupColor(revision, true); 853 gc.setForeground(focusColor); 854 gc.fillRectangle(box); 855 gc.drawRectangle(box.x, box.y, box.width - 1, box.height - 1); gc.drawRectangle(box.x + 1, box.y + 1, box.width - 3, box.height - 3); gc.setForeground(foreground); 858 } else if (drawFocus || drawSelection) { 859 Color foreground= gc.getForeground(); 860 Color focusColor= lookupColor(revision, true); 861 gc.setForeground(focusColor); 862 gc.fillRectangle(box); 863 gc.drawRectangle(box.x, box.y, box.width - 1, box.height - 1); gc.setForeground(foreground); 865 } else { 866 gc.fillRectangle(box); 867 } 868 869 if ((fShowAuthor || fShowRevision)) { 870 int indentation= 1; 871 int baselineBias= getBaselineBias(gc, widgetRange.getStartLine()); 872 if (fShowAuthor && fShowRevision) { 873 gc.drawString(revision.getId(), indentation, box.y + baselineBias, true); 874 gc.drawString(revision.getAuthor(), fAuthorInset, box.y + baselineBias, true); 875 } else if (fShowAuthor) { 876 gc.drawString(revision.getAuthor(), indentation, box.y + baselineBias, true); 877 } else if (fShowRevision) { 878 gc.drawString(revision.getId(), indentation, box.y + baselineBias, true); 879 } 880 } 881 } 882 883 895 private int getBaselineBias(GC gc, int widgetLine) { 896 902 int offset= fWidget.getOffsetAtLine(widgetLine); 903 int widgetBaseline= fWidget.getBaseline(offset); 904 905 FontMetrics fm = gc.getFontMetrics(); 906 int fontBaseline = fm.getAscent() + fm.getLeading(); 907 int baselineBias= widgetBaseline - fontBaseline; 908 return Math.max(0, baselineBias); 909 } 910 911 918 private Color lookupColor(Revision revision, boolean focus) { 919 return fSharedColors.getColor(fColorTool.getColor(revision, focus)); 920 } 921 922 929 private RevisionRange getRange(int line) { 930 List ranges= getRangeCache(); 931 932 if (ranges.isEmpty() || line == -1) 933 return null; 934 935 for (Iterator it= ranges.iterator(); it.hasNext();) { 936 RevisionRange range= (RevisionRange) it.next(); 937 if (contains(range, line)) 938 return range; 939 } 940 941 RevisionRange lastRegion= (RevisionRange) ranges.get(ranges.size() - 1); 943 if (line == end(lastRegion)) 944 return lastRegion; 945 return null; 946 } 947 948 954 private List getRanges(ILineRange lines) { 955 List ranges= getRangeCache(); 956 957 int end= end(lines); 959 int first= -1, last= -1; 960 for (int i= 0; i < ranges.size(); i++) { 961 RevisionRange range= (RevisionRange) ranges.get(i); 962 int rangeEnd= end(range); 963 if (first == -1 && rangeEnd > lines.getStartLine()) 964 first= i; 965 if (first != -1 && rangeEnd > end) { 966 last= i; 967 break; 968 } 969 } 970 if (first == -1) 971 return Collections.EMPTY_LIST; 972 if (last == -1) 973 last= ranges.size() - 1; 975 return ranges.subList(first, last + 1); 976 } 977 978 984 private List getRangeCache() { 985 if (fRevisionRanges == null) { 986 if (fRevisionInfo == null) { 987 fRevisionRanges= Collections.EMPTY_LIST; 988 } else { 989 Hunk[] hunks= HunkComputer.computeHunks(fLineDiffer, fViewer.getDocument().getNumberOfLines()); 990 fRevisionInfo.applyDiff(hunks); 991 fRevisionRanges= fRevisionInfo.getRanges(); 992 updateOverviewAnnotations(); 993 informListeners(); 994 } 995 } 996 997 return fRevisionRanges; 998 } 999 1000 1005 private void clearRangeCache() { 1006 fRevisionRanges= null; 1007 } 1008 1009 1018 private static boolean contains(ILineRange range, int line) { 1019 return range.getStartLine() <= line && end(range) > line; 1020 } 1021 1022 1028 private static int end(ILineRange range) { 1029 return range.getStartLine() + range.getNumberOfLines(); 1030 } 1031 1032 1038 private ILineRange modelLinesToWidgetLines(ILineRange range) { 1039 int widgetStartLine= -1; 1040 int widgetEndLine= -1; 1041 if (fViewer instanceof ITextViewerExtension5) { 1042 ITextViewerExtension5 extension= (ITextViewerExtension5) fViewer; 1043 int modelEndLine= end(range); 1044 for (int modelLine= range.getStartLine(); modelLine < modelEndLine; modelLine++) { 1045 int widgetLine= extension.modelLine2WidgetLine(modelLine); 1046 if (widgetLine != -1) { 1047 if (widgetStartLine == -1) 1048 widgetStartLine= widgetLine; 1049 widgetEndLine= widgetLine; 1050 } 1051 } 1052 } else { 1053 IRegion region= fViewer.getVisibleRegion(); 1054 IDocument document= fViewer.getDocument(); 1055 try { 1056 int visibleStartLine= document.getLineOfOffset(region.getOffset()); 1057 int visibleEndLine= document.getLineOfOffset(region.getOffset() + region.getLength()); 1058 widgetStartLine= Math.max(0, range.getStartLine() - visibleStartLine); 1059 widgetEndLine= Math.min(visibleEndLine, end(range) - 1); 1060 } catch (BadLocationException x) { 1061 x.printStackTrace(); 1062 } 1064 } 1065 if (widgetStartLine == -1 || widgetEndLine == -1) 1066 return null; 1067 return new LineRange(widgetStartLine, widgetEndLine - widgetStartLine + 1); 1068 } 1069 1070 1075 public IAnnotationHover getHover() { 1076 return fHover; 1077 } 1078 1079 1087 private Rectangle computeBoxBounds(ILineRange range) { 1088 int y1= fWidget.getLinePixel(range.getStartLine()); 1089 int y2= fWidget.getLinePixel(range.getStartLine() + range.getNumberOfLines()); 1090 1091 return new Rectangle(0, y1, getWidth(), y2 - y1 - 1); 1092 } 1093 1094 1097 private void updateOverviewAnnotations() { 1098 if (fAnnotationModel == null) 1099 return; 1100 1101 Revision revision= fFocusRevision != null ? fFocusRevision : fSelectedRevision; 1102 1103 Map added= null; 1104 if (revision != null) { 1105 added= new HashMap (); 1106 for (Iterator it= revision.getRegions().iterator(); it.hasNext();) { 1107 RevisionRange range= (RevisionRange) it.next(); 1108 try { 1109 IRegion charRegion= toCharRegion(range); 1110 Position position= new Position(charRegion.getOffset(), charRegion.getLength()); 1111 Annotation annotation= new RevisionAnnotation(revision.getId()); 1112 added.put(annotation, position); 1113 } catch (BadLocationException x) { 1114 } 1116 } 1117 } 1118 1119 if (fAnnotationModel instanceof IAnnotationModelExtension) { 1120 IAnnotationModelExtension ext= (IAnnotationModelExtension) fAnnotationModel; 1121 ext.replaceAnnotations((Annotation[]) fAnnotations.toArray(new Annotation[fAnnotations.size()]), added); 1122 } else { 1123 for (Iterator it= fAnnotations.iterator(); it.hasNext();) { 1124 Annotation annotation= (Annotation) it.next(); 1125 fAnnotationModel.removeAnnotation(annotation); 1126 } 1127 if (added != null) { 1128 for (Iterator it= added.entrySet().iterator(); it.hasNext();) { 1129 Entry entry= (Entry) it.next(); 1130 fAnnotationModel.addAnnotation((Annotation) entry.getKey(), (Position) entry.getValue()); 1131 } 1132 } 1133 } 1134 fAnnotations.clear(); 1135 if (added != null) 1136 fAnnotations.addAll(added.keySet()); 1137 1138 } 1139 1140 1147 private IRegion toCharRegion(ILineRange lines) throws BadLocationException { 1148 IDocument document= fViewer.getDocument(); 1149 int offset= document.getLineOffset(lines.getStartLine()); 1150 int nextLine= end(lines); 1151 int endOffset; 1152 if (nextLine >= document.getNumberOfLines()) 1153 endOffset= document.getLength(); 1154 else 1155 endOffset= document.getLineOffset(nextLine); 1156 return new Region(offset, endOffset - offset); 1157 } 1158 1159 1164 void handleRevisionSelected(Revision revision) { 1165 fSelectedRevision= revision; 1166 fRevisionSelectionProvider.revisionSelected(revision); 1167 updateOverviewAnnotations(); 1168 postRedraw(); 1169 } 1170 1171 1176 void handleRevisionSelected(String id) { 1177 Assert.isLegal(id != null); 1178 if (fRevisionInfo == null) 1179 return; 1180 1181 for (Iterator it= fRevisionInfo.getRevisions().iterator(); it.hasNext();) { 1182 Revision revision= (Revision) it.next(); 1183 if (id.equals(revision.getId())) { 1184 handleRevisionSelected(revision); 1185 return; 1186 } 1187 } 1188 1189 handleRevisionSelected((Revision) null); 1191 } 1192 1193 1198 public RevisionSelectionProvider getRevisionSelectionProvider() { 1199 return fRevisionSelectionProvider; 1200 } 1201 1202 1207 private void updateFocusLine(int line) { 1208 if (fFocusLine != line) 1209 onFocusLineChanged(fFocusLine, line); 1210 } 1211 1212 1218 private void onFocusLineChanged(int previousLine, int nextLine) { 1219 if (DEBUG) 1220 System.out.println("line: " + previousLine + " > " + nextLine); fFocusLine= nextLine; 1222 RevisionRange region= getRange(nextLine); 1223 updateFocusRange(region); 1224 } 1225 1226 1231 private void updateFocusRange(RevisionRange range) { 1232 if (range != fFocusRange) 1233 onFocusRangeChanged(fFocusRange, range); 1234 } 1235 1236 1242 private void onFocusRangeChanged(RevisionRange previousRange, RevisionRange nextRange) { 1243 if (DEBUG) 1244 System.out.println("range: " + previousRange + " > " + nextRange); fFocusRange= nextRange; 1246 Revision revision= nextRange == null ? null : nextRange.getRevision(); 1247 updateFocusRevision(revision); 1248 } 1249 1250 private void updateFocusRevision(Revision revision) { 1251 if (fFocusRevision != revision) 1252 onFocusRevisionChanged(fFocusRevision, revision); 1253 } 1254 1255 1261 private void onFocusRevisionChanged(Revision previousRevision, Revision nextRevision) { 1262 if (DEBUG) 1263 System.out.println("revision: " + previousRevision + " > " + nextRevision); fFocusRevision= nextRevision; 1265 uninstallWheelHandler(); 1266 installWheelHandler(); 1267 updateOverviewAnnotations(); 1268 redraw(); } 1270 1271 1274 private void uninstallWheelHandler() { 1275 fControl.removeListener(SWT.MouseWheel, fMouseHandler); 1276 fWheelHandlerInstalled= false; 1277 } 1278 1279 1282 private void installWheelHandler() { 1283 if (fFocusRevision != null && !fWheelHandlerInstalled) { 1284 fControl.addListener(SWT.MouseWheel, fMouseHandler); 1285 fWheelHandlerInstalled= true; 1286 } 1287 } 1288 1289 1294 private void handleMouseWheel(Event event) { 1295 boolean up= event.count > 0; 1296 int documentHoverLine= fFocusLine; 1297 1298 ILineRange nextWidgetRange= null; 1299 ILineRange last= null; 1300 List ranges= fFocusRevision.getRegions(); 1301 if (up) { 1302 for (Iterator it= ranges.iterator(); it.hasNext();) { 1303 RevisionRange range= (RevisionRange) it.next(); 1304 ILineRange widgetRange= modelLinesToWidgetLines(range); 1305 if (contains(range, documentHoverLine)) { 1306 nextWidgetRange= last; 1307 break; 1308 } 1309 if (widgetRange != null) 1310 last= widgetRange; 1311 } 1312 } else { 1313 for (ListIterator it= ranges.listIterator(ranges.size()); it.hasPrevious();) { 1314 RevisionRange range= (RevisionRange) it.previous(); 1315 ILineRange widgetRange= modelLinesToWidgetLines(range); 1316 if (contains(range, documentHoverLine)) { 1317 nextWidgetRange= last; 1318 break; 1319 } 1320 if (widgetRange != null) 1321 last= widgetRange; 1322 } 1323 } 1324 1325 if (nextWidgetRange == null) 1326 return; 1327 1328 int widgetCurrentFocusLine= modelLinesToWidgetLines(new LineRange(documentHoverLine, 1)).getStartLine(); 1329 int widgetNextFocusLine= nextWidgetRange.getStartLine(); 1330 int newTopPixel= fWidget.getTopPixel() + JFaceTextUtil.computeLineHeight(fWidget, widgetCurrentFocusLine, widgetNextFocusLine, widgetNextFocusLine - widgetCurrentFocusLine); 1331 fWidget.setTopPixel(newTopPixel); 1332 if (newTopPixel < 0) { 1333 Point cursorLocation= fWidget.getDisplay().getCursorLocation(); 1334 cursorLocation.y+= newTopPixel; 1335 fWidget.getDisplay().setCursorLocation(cursorLocation); 1336 } else { 1337 int topPixel= fWidget.getTopPixel(); 1338 if (topPixel < newTopPixel) { 1339 Point cursorLocation= fWidget.getDisplay().getCursorLocation(); 1340 cursorLocation.y+= newTopPixel - topPixel; 1341 fWidget.getDisplay().setCursorLocation(cursorLocation); 1342 } 1343 } 1344 updateFocusLine(toDocumentLineNumber(fWidget.toControl(fWidget.getDisplay().getCursorLocation()).y)); 1345 immediateUpdate(); 1346 } 1347 1348 1351 private final void postRedraw() { 1352 if (isConnected() && !fControl.isDisposed()) { 1353 Display d= fControl.getDisplay(); 1354 if (d != null) { 1355 d.asyncExec(new Runnable () { 1356 public void run() { 1357 redraw(); 1358 } 1359 }); 1360 } 1361 } 1362 } 1363 1364 1372 private int toDocumentLineNumber(int y) { 1373 return fParentRuler.toDocumentLineNumber(y); 1374 } 1375 1376 1379 private void redraw() { 1380 fColumn.redraw(); 1381 } 1382 1383 1386 private void immediateUpdate() { 1387 fParentRuler.immediateUpdate(); 1388 } 1389 1390 1395 private int getWidth() { 1396 return fColumn.getWidth(); 1397 } 1398 1399 1404 private Color getBackground() { 1405 if (fBackground == null) 1406 return fWidget.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); 1407 return fBackground; 1408 } 1409 1410 1415 public void setHover(IAnnotationHover hover) { 1416 } 1418 1419 1425 public boolean hasHover(int activeLine) { 1426 return fViewer instanceof ISourceViewer && fHover.getHoverLineRange((ISourceViewer) fViewer, activeLine) != null; 1427 } 1428 1429 1435 Revision getRevision(int offset) { 1436 IDocument document= fViewer.getDocument(); 1437 int line; 1438 try { 1439 line= document.getLineOfOffset(offset); 1440 } catch (BadLocationException x) { 1441 return null; 1442 } 1443 if (line != -1) { 1444 RevisionRange range= getRange(line); 1445 if (range != null) 1446 return range.getRevision(); 1447 } 1448 return null; 1449 } 1450 1451 1456 public boolean hasInformation() { 1457 return fRevisionInfo != null; 1458 } 1459 1460 1466 public int getRequiredWidth() { 1467 if (fRequiredWidth == -1) { 1468 if (hasInformation() && (fShowRevision || fShowAuthor)) { 1469 int revisionWidth= 0; 1470 int authorWidth= 0; 1471 for (Iterator it= fRevisionInfo.getRevisions().iterator(); it.hasNext();) { 1472 Revision revision= (Revision) it.next(); 1473 revisionWidth= Math.max(revisionWidth, revision.getId().length()); 1474 authorWidth= Math.max(authorWidth, revision.getAuthor().length()); 1475 } 1476 fRevisionIdChars= revisionWidth + 1; 1477 if (fShowAuthor && fShowRevision) 1478 fRequiredWidth= revisionWidth + authorWidth + 2; 1479 else if (fShowAuthor) 1480 fRequiredWidth= authorWidth + 1; 1481 else 1482 fRequiredWidth= revisionWidth + 1; 1483 } else { 1484 fRequiredWidth= 0; 1485 } 1486 } 1487 return fRequiredWidth; 1488 } 1489 1490 1495 public void showRevisionId(boolean show) { 1496 if (fShowRevision != show) { 1497 fRequiredWidth= -1; 1498 fRevisionIdChars= 0; 1499 fShowRevision= show; 1500 postRedraw(); 1501 } 1502 } 1503 1504 1509 public void showRevisionAuthor(boolean show) { 1510 if (fShowAuthor != show) { 1511 fRequiredWidth= -1; 1512 fRevisionIdChars= 0; 1513 fShowAuthor= show; 1514 postRedraw(); 1515 } 1516 } 1517 1518 1524 public void addRevisionListener(IRevisionListener listener) { 1525 fRevisionListeners.add(listener); 1526 } 1527 1528 1534 public void removeRevisionListener(IRevisionListener listener) { 1535 fRevisionListeners.remove(listener); 1536 } 1537 1538 1543 private void informListeners() { 1544 if (fRevisionInfo == null || fRevisionListeners.isEmpty()) 1545 return; 1546 1547 RevisionEvent event= new RevisionEvent(fRevisionInfo); 1548 Object [] listeners= fRevisionListeners.getListeners(); 1549 for (int i= 0; i < listeners.length; i++) { 1550 IRevisionListener listener= (IRevisionListener) listeners[i]; 1551 listener.revisionInformationChanged(event); 1552 } 1553 } 1554} | Popular Tags |