1 11 12 package org.eclipse.jface.text.source; 13 14 15 import java.util.Arrays ; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.custom.StyledText; 19 import org.eclipse.swt.events.DisposeEvent; 20 import org.eclipse.swt.events.DisposeListener; 21 import org.eclipse.swt.events.MouseEvent; 22 import org.eclipse.swt.events.MouseListener; 23 import org.eclipse.swt.events.MouseMoveListener; 24 import org.eclipse.swt.events.PaintEvent; 25 import org.eclipse.swt.events.PaintListener; 26 import org.eclipse.swt.graphics.Color; 27 import org.eclipse.swt.graphics.Font; 28 import org.eclipse.swt.graphics.FontMetrics; 29 import org.eclipse.swt.graphics.GC; 30 import org.eclipse.swt.graphics.Image; 31 import org.eclipse.swt.graphics.Point; 32 import org.eclipse.swt.graphics.Rectangle; 33 import org.eclipse.swt.widgets.Canvas; 34 import org.eclipse.swt.widgets.Composite; 35 import org.eclipse.swt.widgets.Control; 36 import org.eclipse.swt.widgets.Display; 37 38 import org.eclipse.jface.text.BadLocationException; 39 import org.eclipse.jface.text.IDocument; 40 import org.eclipse.jface.text.IRegion; 41 import org.eclipse.jface.text.ITextListener; 42 import org.eclipse.jface.text.ITextViewer; 43 import org.eclipse.jface.text.ITextViewerExtension; 44 import org.eclipse.jface.text.IViewportListener; 45 import org.eclipse.jface.text.JFaceTextUtil; 46 import org.eclipse.jface.text.TextEvent; 47 48 49 55 public class LineNumberRulerColumn implements IVerticalRulerColumn { 56 57 60 class InternalListener implements IViewportListener, ITextListener { 61 62 65 private boolean fCachedRedrawState= true; 66 67 70 public void viewportChanged(int verticalPosition) { 71 if (fCachedRedrawState && verticalPosition != fScrollPos) 72 redraw(); 73 } 74 75 78 public void textChanged(TextEvent event) { 79 80 fCachedRedrawState= event.getViewerRedrawState(); 81 if (!fCachedRedrawState) 82 return; 83 84 if (updateNumberOfDigits()) { 85 computeIndentations(); 86 layout(event.getViewerRedrawState()); 87 return; 88 } 89 90 boolean viewerCompletelyShown= isViewerCompletelyShown(); 91 if (viewerCompletelyShown || fSensitiveToTextChanges || event.getDocumentEvent() == null) 92 postRedraw(); 93 fSensitiveToTextChanges= viewerCompletelyShown; 94 } 95 } 96 97 100 class MouseHandler implements MouseListener, MouseMoveListener { 101 102 103 private int fCachedViewportSize; 104 105 private IRegion fStartLine; 106 107 private int fStartLineNumber; 108 109 private int fAutoScrollDirection; 110 111 private boolean fIsListeningForMove= false; 112 113 116 public void mouseUp(MouseEvent event) { 117 if (event.button == 1) { 119 stopSelecting(); 120 stopAutoScroll(); 121 } 122 } 123 124 127 public void mouseDown(MouseEvent event) { 128 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 129 if (event.button == 1) { 131 startSelecting(); 132 } 133 } 134 135 138 public void mouseDoubleClick(MouseEvent event) { 139 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 140 stopSelecting(); 141 stopAutoScroll(); 142 } 143 144 147 public void mouseMove(MouseEvent event) { 148 if (fIsListeningForMove && !autoScroll(event)) { 149 int newLine= fParentRuler.toDocumentLineNumber(event.y); 150 expandSelection(newLine); 151 } 152 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); 153 } 154 155 159 private void startSelecting() { 160 try { 161 162 IDocument document= fCachedTextViewer.getDocument(); 164 fStartLineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); 165 fStartLine= document.getLineInformation(fStartLineNumber); 166 fCachedTextViewer.setSelectedRange(fStartLine.getOffset(), fStartLine.getLength()); 167 fCachedViewportSize= getVisibleLinesInViewport(); 168 169 fIsListeningForMove= true; 171 172 } catch (BadLocationException x) { 173 } 174 } 175 176 180 private void stopSelecting() { 181 fIsListeningForMove= false; 183 } 184 185 191 private void expandSelection(int lineNumber) { 192 try { 193 194 IDocument document= fCachedTextViewer.getDocument(); 195 IRegion lineInfo= document.getLineInformation(lineNumber); 196 197 int start= Math.min(fStartLine.getOffset(), lineInfo.getOffset()); 198 int end= Math.max(fStartLine.getOffset() + fStartLine.getLength(), lineInfo.getOffset() + lineInfo.getLength()); 199 200 if (lineNumber < fStartLineNumber) 201 fCachedTextViewer.setSelectedRange(end, start - end); 202 else 203 fCachedTextViewer.setSelectedRange(start, end - start); 204 205 } catch (BadLocationException x) { 206 } 207 } 208 209 212 private void stopAutoScroll() { 213 fAutoScrollDirection= SWT.NULL; 214 } 215 216 222 private boolean autoScroll(MouseEvent event) { 223 Rectangle area= fCanvas.getClientArea(); 224 225 if (event.y > area.height) { 226 autoScroll(SWT.DOWN); 227 return true; 228 } 229 230 if (event.y < 0) { 231 autoScroll(SWT.UP); 232 return true; 233 } 234 235 stopAutoScroll(); 236 return false; 237 } 238 239 244 private void autoScroll(int direction) { 245 246 if (fAutoScrollDirection == direction) 247 return; 248 249 final int TIMER_INTERVAL= 5; 250 final Display display = fCanvas.getDisplay(); 251 Runnable timer= null; 252 switch (direction) { 253 case SWT.UP: 254 timer= new Runnable () { 255 public void run() { 256 if (fAutoScrollDirection == SWT.UP) { 257 int top= getInclusiveTopIndex(); 258 if (top > 0) { 259 fCachedTextViewer.setTopIndex(top -1); 260 expandSelection(top -1); 261 display.timerExec(TIMER_INTERVAL, this); 262 } 263 } 264 } 265 }; 266 break; 267 case SWT.DOWN: 268 timer = new Runnable () { 269 public void run() { 270 if (fAutoScrollDirection == SWT.DOWN) { 271 int top= getInclusiveTopIndex(); 272 fCachedTextViewer.setTopIndex(top +1); 273 expandSelection(top +1 + fCachedViewportSize); 274 display.timerExec(TIMER_INTERVAL, this); 275 } 276 } 277 }; 278 break; 279 } 280 281 if (timer != null) { 282 fAutoScrollDirection= direction; 283 display.timerExec(TIMER_INTERVAL, timer); 284 } 285 } 286 287 292 private int getInclusiveTopIndex() { 293 if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) { 294 return JFaceTextUtil.getPartialTopIndex(fCachedTextViewer); 295 } 296 return -1; 297 } 298 } 299 300 301 private CompositeRuler fParentRuler; 302 303 private ITextViewer fCachedTextViewer; 304 305 private StyledText fCachedTextWidget; 306 307 private Canvas fCanvas; 308 309 private int fScrollPos; 310 311 private Image fBuffer; 312 313 private InternalListener fInternalListener= new InternalListener(); 314 315 private Font fFont; 316 317 private int[] fIndentation; 318 319 private boolean fSensitiveToTextChanges= false; 320 321 private Color fForeground; 322 323 private Color fBackground; 324 325 private int fCachedNumberOfDigits= -1; 326 327 private boolean fRelayoutRequired= false; 328 332 private Object fRunnableLock= new Object (); 333 337 private boolean fIsRunnablePosted= false; 338 342 private Runnable fRunnable= new Runnable () { 343 public void run() { 344 synchronized (fRunnableLock) { 345 fIsRunnablePosted= false; 346 } 347 redraw(); 348 } 349 }; 350 351 private MouseHandler fMouseHandler; 352 353 354 357 public LineNumberRulerColumn() { 358 } 359 360 365 public void setForeground(Color foreground) { 366 fForeground= foreground; 367 } 368 369 375 protected Color getForeground() { 376 return fForeground; 377 } 378 379 384 public void setBackground(Color background) { 385 fBackground= background; 386 if (fCanvas != null && !fCanvas.isDisposed()) 387 fCanvas.setBackground(getBackground(fCanvas.getDisplay())); 388 } 389 390 396 protected Color getBackground(Display display) { 397 if (fBackground == null) 398 return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); 399 return fBackground; 400 } 401 402 405 public Control getControl() { 406 return fCanvas; 407 } 408 409 412 public int getWidth() { 413 return fIndentation[0]; 414 } 415 416 425 protected boolean updateNumberOfDigits() { 426 if (fCachedTextViewer == null) 427 return false; 428 429 int digits= computeNumberOfDigits(); 430 431 if (fCachedNumberOfDigits != digits) { 432 fCachedNumberOfDigits= digits; 433 return true; 434 } 435 436 return false; 437 } 438 439 445 protected int computeNumberOfDigits() { 446 IDocument document= fCachedTextViewer.getDocument(); 447 int lines= document == null ? 0 : document.getNumberOfLines(); 448 449 int digits= 2; 450 while (lines > Math.pow(10, digits) -1) { 451 ++digits; 452 } 453 return digits; 454 } 455 456 462 protected void layout(boolean redraw) { 463 if (!redraw) { 464 fRelayoutRequired= true; 465 return; 466 } 467 468 fRelayoutRequired= false; 469 if (fCachedTextViewer instanceof ITextViewerExtension) { 470 ITextViewerExtension extension= (ITextViewerExtension) fCachedTextViewer; 471 Control control= extension.getControl(); 472 if (control instanceof Composite && !control.isDisposed()) { 473 Composite composite= (Composite) control; 474 composite.layout(true); 475 } 476 } 477 } 478 479 483 protected void computeIndentations() { 484 if (fCanvas == null || fCanvas.isDisposed()) 485 return; 486 487 GC gc= new GC(fCanvas); 488 try { 489 490 gc.setFont(fCanvas.getFont()); 491 492 fIndentation= new int[fCachedNumberOfDigits + 1]; 493 494 char[] nines= new char[fCachedNumberOfDigits]; 495 Arrays.fill(nines, '9'); 496 String nineString= new String (nines); 497 Point p= gc.stringExtent(nineString); 498 fIndentation[0]= p.x; 499 500 for (int i= 1; i <= fCachedNumberOfDigits; i++) { 501 p= gc.stringExtent(nineString.substring(0, i)); 502 fIndentation[i]= fIndentation[0] - p.x; 503 } 504 505 } finally { 506 gc.dispose(); 507 } 508 } 509 510 513 public Control createControl(CompositeRuler parentRuler, Composite parentControl) { 514 515 fParentRuler= parentRuler; 516 fCachedTextViewer= parentRuler.getTextViewer(); 517 fCachedTextWidget= fCachedTextViewer.getTextWidget(); 518 519 fCanvas= new Canvas(parentControl, SWT.NONE); 520 fCanvas.setBackground(getBackground(fCanvas.getDisplay())); 521 fCanvas.setForeground(fForeground); 522 523 fCanvas.addPaintListener(new PaintListener() { 524 public void paintControl(PaintEvent event) { 525 if (fCachedTextViewer != null) 526 doubleBufferPaint(event.gc); 527 } 528 }); 529 530 fCanvas.addDisposeListener(new DisposeListener() { 531 public void widgetDisposed(DisposeEvent e) { 532 handleDispose(); 533 fCachedTextViewer= null; 534 fCachedTextWidget= null; 535 } 536 }); 537 538 fMouseHandler= new MouseHandler(); 539 fCanvas.addMouseListener(fMouseHandler); 540 fCanvas.addMouseMoveListener(fMouseHandler); 541 542 if (fCachedTextViewer != null) { 543 544 fCachedTextViewer.addViewportListener(fInternalListener); 545 fCachedTextViewer.addTextListener(fInternalListener); 546 547 if (fFont == null) { 548 if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) 549 fFont= fCachedTextWidget.getFont(); 550 } 551 } 552 553 if (fFont != null) 554 fCanvas.setFont(fFont); 555 556 updateNumberOfDigits(); 557 computeIndentations(); 558 return fCanvas; 559 } 560 561 564 protected void handleDispose() { 565 566 if (fCachedTextViewer != null) { 567 fCachedTextViewer.removeViewportListener(fInternalListener); 568 fCachedTextViewer.removeTextListener(fInternalListener); 569 } 570 571 if (fBuffer != null) { 572 fBuffer.dispose(); 573 fBuffer= null; 574 } 575 } 576 577 582 private void doubleBufferPaint(GC dest) { 583 584 Point size= fCanvas.getSize(); 585 586 if (size.x <= 0 || size.y <= 0) 587 return; 588 589 if (fBuffer != null) { 590 Rectangle r= fBuffer.getBounds(); 591 if (r.width != size.x || r.height != size.y) { 592 fBuffer.dispose(); 593 fBuffer= null; 594 } 595 } 596 if (fBuffer == null) 597 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); 598 599 GC gc= new GC(fBuffer); 600 gc.setFont(fCanvas.getFont()); 601 if (fForeground != null) 602 gc.setForeground(fForeground); 603 604 try { 605 gc.setBackground(getBackground(fCanvas.getDisplay())); 606 gc.fillRectangle(0, 0, size.x, size.y); 607 608 ILineRange visibleLines= JFaceTextUtil.getVisibleModelLines(fCachedTextViewer); 609 if (visibleLines == null) 610 return; 611 fScrollPos= fCachedTextWidget.getTopPixel(); 612 doPaint(gc, visibleLines); 613 } finally { 614 gc.dispose(); 615 } 616 617 dest.drawImage(fBuffer, 0, 0); 618 } 619 620 627 protected int getVisibleLinesInViewport() { 628 return getVisibleLinesInViewport(fCachedTextWidget); 629 } 630 631 632 639 protected final boolean isViewerCompletelyShown() { 640 return JFaceTextUtil.isShowingEntireContents(fCachedTextWidget); 641 } 642 643 650 void doPaint(GC gc, ILineRange visibleLines) { 651 Display display= fCachedTextWidget.getDisplay(); 652 653 int y= -JFaceTextUtil.getHiddenTopLinePixels(fCachedTextWidget); 655 656 int lastLine= end(visibleLines); 657 for (int line= visibleLines.getStartLine(); line < lastLine; line++) { 658 int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line); 659 if (widgetLine == -1) 660 continue; 661 662 int lineHeight= fCachedTextWidget.getLineHeight(fCachedTextWidget.getOffsetAtLine(widgetLine)); 663 paintLine(line, y, lineHeight, gc, display); 664 y += lineHeight; 665 } 666 } 667 668 669 private static int end(ILineRange range) { 670 return range.getStartLine() + range.getNumberOfLines(); 671 } 672 673 681 protected String createDisplayString(int line) { 682 return Integer.toString(line + 1); 683 } 684 685 697 private int getBaselineBias(GC gc, int widgetLine) { 698 704 int offset= fCachedTextWidget.getOffsetAtLine(widgetLine); 705 int widgetBaseline= fCachedTextWidget.getBaseline(offset); 706 707 FontMetrics fm = gc.getFontMetrics(); 708 int fontBaseline = fm.getAscent() + fm.getLeading(); 709 int baselineBias= widgetBaseline - fontBaseline; 710 return Math.max(0, baselineBias); 711 } 712 713 724 protected void paintLine(int line, int y, int lineheight, GC gc, Display display) { 725 int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line); 726 727 String s= createDisplayString(line); 728 int indentation= fIndentation[s.length()]; 729 int baselineBias= getBaselineBias(gc, widgetLine); 730 gc.drawString(s, indentation, y + baselineBias, true); 731 } 732 733 738 protected final void postRedraw() { 739 if (fCanvas != null && !fCanvas.isDisposed()) { 740 Display d= fCanvas.getDisplay(); 741 if (d != null) { 742 synchronized (fRunnableLock) { 743 if (fIsRunnablePosted) 744 return; 745 fIsRunnablePosted= true; 746 } 747 d.asyncExec(fRunnable); 748 } 749 } 750 } 751 752 755 public void redraw() { 756 757 if (fRelayoutRequired) { 758 layout(true); 759 return; 760 } 761 762 if (fCachedTextViewer != null && fCanvas != null && !fCanvas.isDisposed()) { 763 GC gc= new GC(fCanvas); 764 doubleBufferPaint(gc); 765 gc.dispose(); 766 } 767 } 768 769 772 public void setModel(IAnnotationModel model) { 773 } 774 775 778 public void setFont(Font font) { 779 fFont= font; 780 if (fCanvas != null && !fCanvas.isDisposed()) { 781 fCanvas.setFont(fFont); 782 updateNumberOfDigits(); 783 computeIndentations(); 784 } 785 } 786 787 793 protected CompositeRuler getParentRuler() { 794 return fParentRuler; 795 } 796 797 798 805 static int getVisibleLinesInViewport(StyledText textWidget) { 806 if (textWidget != null) { 807 Rectangle clArea= textWidget.getClientArea(); 808 if (!clArea.isEmpty()) { 809 int firstPixel= 0; 810 int lastPixel= clArea.height - 1; int first= JFaceTextUtil.getLineIndex(textWidget, firstPixel); 812 int last= JFaceTextUtil.getLineIndex(textWidget, lastPixel); 813 return last - first; 814 } 815 } 816 return -1; 817 } 818 819 } 820 | Popular Tags |