1 19 20 package org.netbeans.editor; 21 22 import java.awt.Graphics ; 23 import java.awt.Graphics2D ; 24 import java.awt.Font ; 25 import java.awt.FontMetrics ; 26 import java.awt.Color ; 27 import java.awt.Component ; 28 import java.awt.Rectangle ; 29 import java.awt.Shape ; 30 import java.awt.Insets ; 31 import java.text.AttributedCharacterIterator ; 32 import java.util.ArrayList ; 33 import java.util.List ; 34 import java.util.Map ; 35 import javax.swing.text.BadLocationException ; 36 import javax.swing.text.Document ; 37 import javax.swing.text.JTextComponent ; 38 import javax.swing.text.Segment ; 39 import javax.swing.text.View ; 40 41 47 class DrawEngine { 48 49 50 private static final boolean debug 51 = Boolean.getBoolean("netbeans.debug.editor.draw"); 53 private static final boolean debugFragment 54 = Boolean.getBoolean("netbeans.debug.editor.draw.fragment"); 56 57 private static DrawEngine drawEngine; 58 59 private static final char[] SPACE = new char[] { ' ' }; 60 61 62 private DrawEngine() { 63 } 64 65 66 public static DrawEngine getDrawEngine() { 67 if (drawEngine == null) { 68 drawEngine = new DrawEngine(); 69 } 70 return drawEngine; 71 } 72 73 public PreinitializedDrawEngine getDrawEngine(View view, 74 DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset, 75 int startX, int startY, int targetOffset) throws BadLocationException 76 { 77 78 if (startOffset < 0 || endOffset < 0 || startOffset > endOffset 80 || startX < 0 || startY < 0 81 ) { 82 return null; 83 } 84 85 BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument()); 86 PreinitializedDrawEngine preinitializedDrawEngine = new PreinitializedDrawEngine(drawGraphics); 87 preinitializedDrawEngine.preinitialize(doc, editorUI, startOffset, endOffset, startX, startY, targetOffset); 88 return preinitializedDrawEngine; 89 } 90 91 92 private void initLineNumbering(DrawInfo ctx) { 93 ctx.lineNumbering = ctx.editorUI.lineNumberVisible 95 && ctx.drawGraphics.supportsLineNumbers(); 96 97 if (ctx.lineNumbering) { 99 try { 100 ctx.startLineNumber = Utilities.getLineOffset(ctx.doc, ctx.startOffset) + 1; 101 } catch (BadLocationException e) { 102 Utilities.annotateLoggable(e); 103 } 104 105 ctx.lineNumberColoring = ctx.editorUI.getColoring(SettingsNames.LINE_NUMBER_COLORING); 106 if (ctx.lineNumberColoring == null) { 107 ctx.lineNumberColoring = ctx.defaultColoring; 109 } else { ctx.lineNumberColoring = ctx.lineNumberColoring.apply(ctx.defaultColoring); 111 } 112 113 Font lnFont = ctx.lineNumberColoring.getFont(); 114 if (lnFont == null) { 115 lnFont = ctx.defaultColoring.getFont(); 116 } 117 118 Color lnBackColor = ctx.lineNumberColoring.getBackColor(); 119 if (lnBackColor == null) { 120 lnBackColor = ctx.defaultColoring.getBackColor(); 121 } 122 123 Color lnForeColor = ctx.lineNumberColoring.getForeColor(); 124 if (lnForeColor == null) { 125 lnForeColor = ctx.defaultColoring.getForeColor(); 126 } 127 128 ctx.lineNumberChars = new char[Math.max(ctx.editorUI.lineNumberMaxDigitCount, 1)]; 129 if (ctx.graphics == null) { 130 ctx.syncedLineNumbering = true; 131 132 } else { try { 134 int endLineNumber = Utilities.getLineOffset(ctx.doc, ctx.endOffset) + 1; 135 ctx.lineStartOffsets = new int[endLineNumber - ctx.startLineNumber + 2]; } catch (BadLocationException e) { 137 Utilities.annotateLoggable(e); 138 } 139 } 140 } 141 } 142 143 private void initInfo(DrawInfo ctx) throws BadLocationException { 144 ctx.x = ctx.startX; 145 ctx.y = ctx.startY; 146 ctx.lineHeight = ctx.editorUI.getLineHeight(); 147 ctx.defaultColoring = ctx.editorUI.getDefaultColoring(); 148 ctx.tabSize = ctx.doc.getTabSize(); 149 ctx.fragmentOffset = ctx.startOffset; ctx.graphics = ctx.drawGraphics.getGraphics(); 151 152 if (ctx.graphics != null) { 153 if (ctx.editorUI.renderingHints != null) { 154 ((Graphics2D )ctx.graphics).setRenderingHints(ctx.editorUI.renderingHints); 155 } 156 } 157 158 initLineNumbering(ctx); 159 160 ctx.foreColor = ctx.defaultColoring.getForeColor(); 162 ctx.backColor = ctx.defaultColoring.getBackColor(); 163 ctx.font = ctx.defaultColoring.getFont(); 164 ctx.bol = true; 166 ctx.drawGraphics.init(ctx); 168 ctx.drawGraphics.setDefaultBackColor(ctx.defaultColoring.getBackColor()); 169 ctx.drawGraphics.setLineHeight(ctx.lineHeight); 170 ctx.drawGraphics.setLineAscent(ctx.editorUI.getLineAscent()); 171 ctx.drawGraphics.setX(ctx.x); 172 ctx.drawGraphics.setY(ctx.y); 173 174 ctx.layers = ctx.editorUI.getDrawLayerList().currentLayers(); 176 int layersLength = ctx.layers.length; 177 ctx.layerActives = new boolean[layersLength]; 178 ctx.layerActivityChangeOffsets = new int[layersLength]; 179 180 for (int i = 0; i < layersLength; i++) { 181 ctx.layers[i].init(ctx); } 183 184 ctx.drawMarkList = new ArrayList (); 185 Map docMarks = ctx.doc.marks; 186 MarkVector docMarksStorage = ctx.doc.marksStorage; 187 synchronized (docMarks) { 188 int offset = ctx.startOffset; 189 int low = 0; 190 int markCount = docMarksStorage.getMarkCount(); 191 int high = markCount - 1; 192 193 while (low <= high) { 194 int mid = (low + high) >> 1; 195 int cmp = docMarksStorage.getMarkOffsetInternal(mid) - offset; 196 197 if (cmp < 0) 198 low = mid + 1; 199 else if (cmp > 0) 200 high = mid - 1; 201 else { while (--mid >= 0 203 && docMarksStorage.getMarkOffsetInternal(mid) == offset 204 ) { } 205 low = mid + 1; 206 break; 207 } 208 209 } 210 211 offset = ctx.endOffset; 213 while (low < markCount) { 214 MultiMark m = (MultiMark)docMarksStorage.getMark(low); 215 if (m.isValid()) { 216 if (m.getOffset() > offset) { 217 break; 218 } 219 220 Mark mark = (Mark)docMarks.get(m); 221 if (mark == null) { 222 throw new IllegalStateException ("No mark for m=" + m); } 224 if (mark instanceof MarkFactory.DrawMark) { 225 ctx.drawMarkList.add(mark); 226 } 227 } 228 229 low++; 230 } 231 } 232 233 234 ctx.drawMarkIndex = 0; 236 ctx.drawMarkOffset = Integer.MAX_VALUE; 237 if (ctx.drawMarkList.size() > 0) { 238 ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++); 239 try { 240 ctx.drawMarkOffset = ctx.drawMark.getOffset(); 241 } catch (InvalidMarkException e) { 242 throw new IllegalStateException (e.toString()); 243 } 244 if (ctx.drawMarkOffset < ctx.updateOffset) { 245 ctx.updateOffset = ctx.drawMarkOffset; 246 ctx.drawMarkUpdate = true; 247 } 248 } 249 250 int endTokenSafeOffset = ctx.doc.getTokenSafeOffset(ctx.endOffset); 252 ctx.doc.prepareSyntax(ctx.text, ctx.syntax, 253 ctx.startOffset, endTokenSafeOffset - ctx.startOffset, 254 false, false 255 ); 256 257 ctx.textArray = ctx.text.array; 258 ctx.buffer = ctx.textArray; 259 ctx.bufferStartOffset = ctx.startOffset - ctx.syntax.getOffset(); 260 ctx.drawGraphics.setBuffer(ctx.textArray); 261 if (ctx.drawGraphics instanceof DrawGraphics.GraphicsDG) { 262 ((DrawGraphics.GraphicsDG)ctx.drawGraphics).setBufferStartOffset(ctx.bufferStartOffset); 263 } 264 265 ctx.continueDraw = true; 266 } 267 268 private void handleBOL(DrawInfo ctx) { 269 if (ctx.lineNumbering) { 270 if (ctx.syncedLineNumbering) { 271 273 ctx.foreColor = ctx.lineNumberColoring.getForeColor(); 275 ctx.backColor = ctx.lineNumberColoring.getBackColor(); 276 ctx.font = ctx.lineNumberColoring.getFont(); 277 ctx.strikeThroughColor = null; 278 ctx.underlineColor = null; 279 ctx.waveUnderlineColor = null; 280 281 int lineNumber = ctx.startLineNumber + ctx.lineIndex; 282 int layersLength = ctx.layers.length; 284 for (int i = 0; i < layersLength; i++) { 285 lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx); 286 } 287 288 int i = Math.max(ctx.lineNumberChars.length - 1, 0); 290 do { 291 ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10)); 292 lineNumber /= 10; 293 } while (lineNumber != 0 && i >= 0); 294 295 while (i >= 0) { 297 ctx.lineNumberChars[i--] = ' '; 298 } 299 300 int numX = ctx.x - ctx.editorUI.lineNumberWidth; 302 if (ctx.editorUI.getLineNumberMargin() != null) { 303 numX += ctx.editorUI.getLineNumberMargin().left; 304 } 305 ctx.drawGraphics.setX(numX); 306 307 ctx.drawGraphics.setBuffer(ctx.lineNumberChars); 308 ctx.drawGraphics.setForeColor(ctx.foreColor); 309 ctx.drawGraphics.setBackColor(ctx.backColor); 310 ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor); 311 ctx.drawGraphics.setUnderlineColor(ctx.underlineColor); 312 ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor); 313 ctx.drawGraphics.setFont(ctx.font); 314 ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length, 315 ctx.editorUI.lineNumberWidth); 316 317 if (ctx.drawGraphics.getGraphics() == null) { 320 ctx.drawGraphics.setBuffer(SPACE); 321 ctx.drawGraphics.drawChars(0, 1, 322 ctx.editorUI.lineNumberDigitWidth); 323 } 324 325 ctx.drawGraphics.setX(ctx.x); 326 ctx.drawGraphics.setBuffer(ctx.textArray); 327 328 } else { ctx.lineStartOffsets[ctx.lineIndex] = ctx.fragmentOffset; } 331 } 332 333 ctx.lineIndex++; 334 } 335 336 337 338 private void handleEOL(DrawInfo ctx) { 339 ctx.drawGraphics.setX(ctx.x); 340 ctx.drawGraphics.setY(ctx.y); 341 342 ctx.drawGraphics.eol(); ctx.widestWidth = Math.max(ctx.widestWidth, ctx.x); ctx.visualColumn = 0; 345 ctx.x = ctx.startX; 346 ctx.y += ctx.lineHeight; 347 348 ctx.drawGraphics.setX(ctx.x); 349 ctx.drawGraphics.setY(ctx.y); 350 351 } 352 353 354 357 private void updateOffsetReached(DrawInfo ctx) { 358 if (ctx.drawMarkUpdate) { int layersLength = ctx.layers.length; 361 for (int i = 0; i < layersLength; i++) { 362 DrawLayer l = ctx.layers[i]; 363 if (l.getName().equals(ctx.drawMark.layerName) 364 && (ctx.drawMark.isDocumentMark() 365 || ctx.editorUI == ctx.drawMark.getEditorUI()) 366 ) { 367 ctx.layerActives[i] = l.isActive(ctx, ctx.drawMark); 368 int naco = l.getNextActivityChangeOffset(ctx); 369 ctx.layerActivityChangeOffsets[i] = naco; 370 if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) { 371 ctx.layerUpdateOffset = naco; 372 } 373 } 374 } 375 376 if (ctx.drawMarkIndex < ctx.drawMarkList.size()) { 378 ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++); 379 try { 380 ctx.drawMarkOffset = ctx.drawMark.getOffset(); 381 } catch (InvalidMarkException e) { 382 throw new IllegalStateException (e.toString()); 383 } 384 385 } else { ctx.drawMark = null; 387 ctx.drawMarkOffset = Integer.MAX_VALUE; 388 } 389 390 } else { ctx.layerUpdateOffset = Integer.MAX_VALUE; 392 int layersLength = ctx.layers.length; 393 for (int i = 0; i < layersLength; i++) { 394 int naco = ctx.layerActivityChangeOffsets[i]; 396 if (naco == ctx.fragmentOffset) { 397 DrawLayer l = ctx.layers[i]; 398 ctx.layerActives[i] = l.isActive(ctx, null); 399 naco = l.getNextActivityChangeOffset(ctx); 400 ctx.layerActivityChangeOffsets[i] = naco; 401 } 402 403 if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) { 404 ctx.layerUpdateOffset = naco; 405 } 406 } 407 } 408 409 if (ctx.drawMarkOffset < ctx.layerUpdateOffset) { 411 ctx.drawMarkUpdate = true; 412 ctx.updateOffset = ctx.drawMarkOffset; 413 414 } else { 415 ctx.drawMarkUpdate = false; 416 ctx.updateOffset = ctx.layerUpdateOffset; 417 } 418 419 } 420 421 422 private void computeFragmentLength(DrawInfo ctx) { 423 ctx.fragmentStartIndex = ctx.fragmentOffset - ctx.bufferStartOffset; 425 ctx.fragmentLength = Math.min(ctx.updateOffset - ctx.fragmentOffset, 426 ctx.tokenLength - ctx.drawnLength); 427 428 int stopIndex = Analyzer.findFirstTabOrLF(ctx.textArray, 430 ctx.fragmentStartIndex, ctx.fragmentLength); 431 432 ctx.eol = (ctx.fragmentOffset == ctx.docLen); 434 ctx.tabsFragment = false; 435 436 if (stopIndex >= 0) { if (stopIndex == ctx.fragmentStartIndex) { if (ctx.textArray[stopIndex] == '\t') { ctx.tabsFragment = true; 442 int ntInd = Analyzer.findFirstNonTab(ctx.textArray, ctx.fragmentStartIndex, 444 ctx.fragmentLength); 445 446 if (ntInd != -1) { ctx.fragmentLength = ntInd - ctx.fragmentStartIndex; 448 } 449 450 451 } else { ctx.eol = true; 453 ctx.fragmentLength = 1; } 455 456 } else { ctx.fragmentLength = stopIndex - ctx.fragmentStartIndex; } 459 } 460 } 461 462 463 private void computeFragmentDisplayWidth(DrawInfo ctx) { 464 if (!ctx.eol) { int layersLength = ctx.layers.length; 468 for (int i = 0; i < layersLength; i++) { 469 if (ctx.layerActives[i]) { 470 ctx.layers[i].updateContext(ctx); } 472 } 473 } 474 475 FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(ctx.font); 477 ctx.spaceWidth = (ctx.component != null) 478 ? fmcInfo.getSpaceWidth(ctx.component) : ctx.editorUI.defaultSpaceWidth; 479 480 ctx.fragmentCharCount = ctx.fragmentLength; 482 if (ctx.tabsFragment) { ctx.fragmentCharCount = Analyzer.getColumn(ctx.textArray, 484 ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn) - ctx.visualColumn; 485 ctx.fragmentWidth = ctx.fragmentCharCount * ctx.spaceWidth; 486 487 } else if (ctx.eol) { ctx.fragmentWidth = ctx.spaceWidth; 489 490 } else { if (ctx.fragmentLength > 0) { 492 if (ctx.component != null) { 493 ctx.fragmentWidth = FontMetricsCache.getFontMetrics(ctx.font, ctx.component).charsWidth( 494 ctx.textArray, ctx.fragmentStartIndex, ctx.fragmentLength); 495 496 } else { ctx.fragmentWidth = ctx.fragmentLength * ctx.spaceWidth; 498 } 499 500 } else { 501 ctx.fragmentWidth = 0; } 503 } 504 505 } 506 507 508 511 private void drawFragment(DrawInfo ctx) { 512 if (ctx.eol) { int layersLength = ctx.layers.length; 514 boolean emptyLine = false; 515 int blankWidth = ctx.fragmentWidth; 516 517 527 do { 528 blankWidth = 0; 529 if (ctx.bol) { if (!emptyLine) { for (int i = 0; i < layersLength; i++) { 532 if (ctx.layerActives[i]) { 533 DrawLayer l = ctx.layers[i]; 534 if (l.extendsEmptyLine()) { 535 emptyLine = true; l.updateContext(ctx); 537 } 538 } 539 } 540 541 if (emptyLine) { blankWidth = ctx.spaceWidth / 2; } 544 } else { emptyLine = false; 546 } 547 } 548 549 if (!emptyLine) { boolean extendEOL = false; 551 for (int i = 0; i < layersLength; i++) { 552 if (ctx.layerActives[i]) { 553 DrawLayer l = ctx.layers[i]; 554 if (l.extendsEOL()) { 555 extendEOL = true; l.updateContext(ctx); 557 } 558 } 559 } 560 if (extendEOL && ctx.component != null) { 561 ctx.drawGraphics.setStrikeThroughColor(null); 563 ctx.drawGraphics.setUnderlineColor(null); 564 ctx.drawGraphics.setWaveUnderlineColor(null); 565 blankWidth = ctx.component.getWidth(); 566 } 567 } 568 if (blankWidth > 0) { 569 ctx.drawGraphics.setBackColor(ctx.backColor); 570 ctx.drawGraphics.fillRect(blankWidth); 571 if (emptyLine && 572 ctx.x <= ctx.editorUI.getTextMargin().left) { ctx.x += blankWidth; 574 } 575 } 576 } while (emptyLine); 577 578 } else { 580 ctx.drawGraphics.setBackColor(ctx.backColor); 581 ctx.drawGraphics.setForeColor(ctx.foreColor); 582 ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor); 583 ctx.drawGraphics.setUnderlineColor(ctx.underlineColor); 584 ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor); 585 ctx.drawGraphics.setFont(ctx.font); 586 587 if (ctx.tabsFragment) { 588 ctx.drawGraphics.drawTabs(ctx.fragmentStartIndex, 589 ctx.fragmentLength, ctx.fragmentCharCount, ctx.fragmentWidth); 590 591 } else { ctx.drawGraphics.drawChars(ctx.fragmentStartIndex, 593 ctx.fragmentLength, ctx.fragmentWidth); 594 } 595 } 596 } 597 598 599 600 private void checkTargetOffsetReached(DrawInfo ctx) { 601 ctx.continueDraw = true; 602 603 if (ctx.eol 605 && (ctx.targetOffset == ctx.fragmentOffset || (ctx.targetOffset == -1)) 606 ) { 607 610 char ch = '\n'; 611 ctx.continueDraw = ctx.drawGraphics.targetOffsetReached( 612 ctx.fragmentOffset, ch, ctx.x, ctx.spaceWidth, ctx); 613 614 } else if (ctx.targetOffset == -1 616 && ctx.fragmentLength > 0 ) { FontMetrics fm = FontMetricsCache.getFontMetrics(ctx.font, ctx.component); 619 620 int low = -1; 622 int high = ctx.fragmentLength - 1; 623 624 int lastMid = high; 626 int lastWidth; if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 629 ctx.fragmentStartIndex, high, ctx.tabSize, ctx.visualColumn) 630 - ctx.visualColumn; 631 lastWidth = spaceCount * ctx.spaceWidth; 632 633 } else { lastWidth = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, high); 635 } 636 637 int lastWidthP1; if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 640 ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn) 641 - ctx.visualColumn; 642 lastWidthP1 = spaceCount * ctx.spaceWidth; 643 644 } else { lastWidthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, ctx.fragmentLength); 646 } 647 648 ctx.continueDraw = ctx.drawGraphics.targetOffsetReached( 650 ctx.fragmentOffset + high, ctx.textArray[ctx.fragmentStartIndex + high], 651 ctx.x + lastWidth, lastWidthP1 - lastWidth, ctx 652 ); 653 654 if (!ctx.continueDraw) { 655 while (low <= high) { 657 int mid =(low + high) / 2; 658 659 int width = 0; 661 if (mid == lastMid + 1) { width = lastWidthP1; 663 664 } else { 665 if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 667 ctx.fragmentStartIndex, mid, ctx.tabSize, ctx.visualColumn) 668 - ctx.visualColumn; 669 width = spaceCount * ctx.spaceWidth; 670 671 } else { width = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, mid); 673 } 674 } 675 676 int widthP1 = 0; 679 if (mid == lastMid - 1) { widthP1 = lastWidth; 681 682 } else { 683 if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 685 ctx.fragmentStartIndex, mid + 1, ctx.tabSize, ctx.visualColumn) 686 - ctx.visualColumn; 687 widthP1 = spaceCount * ctx.spaceWidth; 688 689 } else { widthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, mid + 1); 691 } 692 } 693 694 lastWidth = width; 695 lastWidthP1 = widthP1; 696 lastMid = mid; 697 698 ctx.continueDraw = ctx.drawGraphics.targetOffsetReached( 699 ctx.fragmentOffset + mid, ctx.textArray[ctx.fragmentStartIndex + mid], 700 ctx.x + width, widthP1 - width, ctx 701 ); 702 703 if (ctx.continueDraw) { 704 low = mid + 1; 705 } else { 706 if (mid > low && mid != high) { 708 high = mid; } else { 710 break; 711 } 712 } 713 } 714 } 715 716 717 718 } else if (ctx.targetOffset < ctx.fragmentOffset + ctx.fragmentLength 720 && ctx.fragmentOffset <= ctx.targetOffset 721 ) { 722 int curWidth; 723 int prevWidth = 0; 724 int i = (ctx.targetOffset - ctx.fragmentOffset); 725 726 if (i > 0) { 727 if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 729 ctx.fragmentStartIndex, i, ctx.tabSize, ctx.visualColumn) 730 - ctx.visualColumn; 731 prevWidth = spaceCount * ctx.spaceWidth; 732 733 } else { prevWidth = FontMetricsCache.getFontMetrics(ctx.font, 735 ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i); 736 } 737 } 738 739 if (ctx.tabsFragment) { int spaceCount = Analyzer.getColumn(ctx.textArray, 741 ctx.fragmentStartIndex, i + 1, ctx.tabSize, ctx.visualColumn) 742 - ctx.visualColumn; 743 curWidth = spaceCount * ctx.spaceWidth; 744 745 } else { curWidth = FontMetricsCache.getFontMetrics(ctx.font, 747 ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i + 1); 748 } 749 750 ctx.continueDraw = ctx.drawGraphics.targetOffsetReached( 751 ctx.fragmentOffset + i, ctx.textArray[ctx.fragmentStartIndex + i], 752 ctx.x + prevWidth, curWidth - prevWidth, ctx); 753 } 754 } 755 756 757 private void drawCurrentTokenFragment(DrawInfo ctx) { 758 ctx.foreColor = ctx.defaultColoring.getForeColor(); 760 ctx.backColor = ctx.defaultColoring.getBackColor(); 761 ctx.font = ctx.defaultColoring.getFont(); 762 ctx.strikeThroughColor = null; 763 ctx.underlineColor = null; 764 ctx.waveUnderlineColor = null; 765 766 if (ctx.bol) { Color fg = ctx.foreColor; 768 Color bg = ctx.backColor; 769 handleBOL(ctx); 770 ctx.foreColor = fg; 771 ctx.backColor = bg; 772 } 773 774 while (ctx.fragmentOffset == ctx.updateOffset) { 776 updateOffsetReached(ctx); } 778 779 computeFragmentLength(ctx); 781 782 computeFragmentDisplayWidth(ctx); 784 785 drawFragment(ctx); 787 788 if (debugFragment) { 789 System.err.println("DrawEngine: FRAGMENT='" + EditorDebug.debugChars(ctx.buffer, ctx.fragmentStartIndex, ctx.fragmentLength) 791 + "', at pos=" + ctx.fragmentOffset + ", bol=" + ctx.bol + ", eol=" + ctx.eol ); 794 } 795 796 if (ctx.component != null) { 798 checkTargetOffsetReached(ctx); 799 } 800 801 ctx.fragmentOffset += ctx.fragmentLength; 803 ctx.drawnLength += ctx.fragmentLength; 804 ctx.visualColumn += ctx.fragmentCharCount; 805 ctx.x += ctx.fragmentWidth; 806 ctx.bol = false; 807 808 if (ctx.eol) { 810 handleEOL(ctx); 811 ctx.bol = true; } 813 814 if (ctx.fragmentOffset >= ctx.endOffset && ctx.endOffset < ctx.docLen) { 815 ctx.continueDraw = false; 816 } 817 } 818 819 820 825 private void drawCurrentToken(DrawInfo ctx) { 826 if (ctx.tokenID != null) { 828 ctx.tokenContextPath = ctx.syntax.getTokenContextPath(); 829 ctx.tokenOffset = ctx.syntax.getTokenOffset() + ctx.bufferStartOffset; 830 ctx.tokenLength = ctx.syntax.getTokenLength(); 831 832 837 if (ctx.tokenOffset + ctx.tokenLength <= ctx.startOffset) { 838 return; 839 } 840 841 } else { ctx.tokenContextPath = null; 843 ctx.tokenOffset = ctx.fragmentOffset; 844 ctx.tokenLength = 0; 845 } 846 847 if (ctx.tokenOffset <= ctx.startOffset) { 850 ctx.layerUpdateOffset = Integer.MAX_VALUE; 851 int layersLength = ctx.layers.length; 852 for (int i = 0; i < layersLength; i++) { DrawLayer l = ctx.layers[i]; 854 ctx.layerActives[i] = l.isActive(ctx, null); 855 856 int naco = l.getNextActivityChangeOffset(ctx); 857 ctx.layerActivityChangeOffsets[i] = naco; 858 if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) { 859 ctx.layerUpdateOffset = naco; 860 } 861 } 862 ctx.updateOffset = Math.min(ctx.layerUpdateOffset, ctx.drawMarkOffset); 863 } 864 865 ctx.drawnLength = ctx.fragmentOffset - ctx.tokenOffset; 866 ctx.fragmentLength = 0; 868 if (debug) { 869 System.err.println("DrawEngine: TOKEN='" + EditorDebug.debugChars(ctx.getBuffer(), ctx.getTokenOffset() 871 - ctx.getBufferStartOffset(), ctx.getTokenLength()) 872 + "', tokenID=<" + (ctx.getTokenID() == null ? "null" : ctx.tokenID.getName()) + ">, tcp=" + ctx.getTokenContextPath() + ", pos=" + ctx.getTokenOffset() ); 876 } 877 878 do { 880 drawCurrentTokenFragment(ctx); 881 } while (ctx.continueDraw && ctx.drawnLength < ctx.tokenLength); 882 883 } 884 885 private void graphicsSpecificUpdates(DrawInfo ctx) { 886 Rectangle bounds = ctx.editorUI.getExtentBounds(); 887 Rectangle clip = ctx.graphics.getClipBounds(); 888 Insets textMargin = ctx.editorUI.getTextMargin(); 889 int leftMarginWidth = textMargin.left - ctx.editorUI.lineNumberWidth 890 - ctx.editorUI.textLeftMarginWidth; 891 892 if (ctx.lineNumbering && !ctx.syncedLineNumbering) { 894 Color lnBackColor = ctx.lineNumberColoring.getBackColor(); 895 int numY = ctx.startY; 896 int lnBarX = bounds.x + leftMarginWidth; 897 if (!lnBackColor.equals(ctx.defaultColoring.getBackColor()) || bounds.x > 0) { 898 ctx.graphics.setColor(lnBackColor); 899 ctx.graphics.fillRect(lnBarX, numY, ctx.editorUI.lineNumberWidth, 900 ctx.lineIndex * ctx.lineHeight); } 902 903 ctx.drawGraphics.setDefaultBackColor(lnBackColor); 905 int lastDigitInd = Math.max(ctx.lineNumberChars.length - 1, 0); 906 int numX = lnBarX; 907 if (ctx.editorUI.getLineNumberMargin() != null) { 908 numX += ctx.editorUI.getLineNumberMargin().left; 909 } 910 911 ctx.bol = true; for (int j = 0; j < ctx.lineIndex; j++) { 914 ctx.fragmentOffset = ctx.lineStartOffsets[j]; 916 ctx.foreColor = ctx.lineNumberColoring.getForeColor(); 917 ctx.backColor = lnBackColor; 918 ctx.font = ctx.lineNumberColoring.getFont(); 919 ctx.strikeThroughColor = null; 920 ctx.underlineColor = null; 921 ctx.waveUnderlineColor = null; 922 923 int lineNumber = ctx.startLineNumber + j; 924 int layersLength = ctx.layers.length; 926 for (int i = 0; i < layersLength; i++) { 927 lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx); 928 } 929 930 int i = lastDigitInd; 931 do { 933 ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10)); 934 lineNumber /= 10; 935 } while (lineNumber != 0 && i >= 0); 936 while (i >= 0) { 938 ctx.lineNumberChars[i--] = ' '; 939 } 940 941 ctx.drawGraphics.setY(numY); 942 ctx.drawGraphics.setBuffer(ctx.lineNumberChars); 943 ctx.drawGraphics.setForeColor(ctx.foreColor); 944 ctx.drawGraphics.setBackColor(ctx.backColor); 945 ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor); 946 ctx.drawGraphics.setUnderlineColor(ctx.underlineColor); 947 ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor); 948 ctx.drawGraphics.setFont(ctx.font); 949 950 ctx.drawGraphics.setX(lnBarX); 951 ctx.drawGraphics.fillRect(ctx.editorUI.lineNumberWidth); 952 ctx.drawGraphics.setX(numX); 953 954 ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length, 955 ctx.lineNumberChars.length * ctx.editorUI.lineNumberDigitWidth); 956 957 ctx.drawGraphics.setBuffer(null); 959 numY += ctx.lineHeight; 960 } 961 } 962 963 ctx.graphics.setColor(ctx.defaultColoring.getBackColor()); 965 } 966 967 983 void draw(DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset, 984 int startX, int startY, int targetOffset) throws BadLocationException { 985 986 draw(null, drawGraphics, editorUI, startOffset, endOffset, 987 startX, startY, targetOffset); 988 } 989 990 void draw(View view, DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset, 991 int startX, int startY, int targetOffset) throws BadLocationException { 992 if (startOffset < 0 || endOffset < 0 || startOffset > endOffset 994 || startX < 0 || startY < 0 995 ) { 996 return; 997 } 998 999 BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument()); 1000 if (debug) { 1001 javax.swing.text.Element lineRoot = doc.getParagraphElement(0).getParentElement(); 1002 int startLine = lineRoot.getElementIndex(startOffset); 1003 int startLineOffset = lineRoot.getElement(startLine).getStartOffset(); 1004 int endLine = lineRoot.getElementIndex(endOffset); 1005 int endLineOffset = lineRoot.getElement(endLine).getStartOffset(); 1006 Graphics g = drawGraphics.getGraphics(); 1007 1008 System.err.println("DrawEngine:---------- DRAWING startOffset=" 1009 + startOffset + ", startLine=" + startLine + "(o=" + startLineOffset + "), endOffset=" + endOffset + ", endLine=" + endLine + "(o=" + endLineOffset + "), clip=" + ((g != null) ? g.getClipBounds().toString() : "null") + " ------------------"); } 1015 1016 DrawInfo ctx = new DrawInfo(); 1018 ctx.drawGraphics = drawGraphics; 1019 ctx.drawGraphics.setView(view); 1020 ctx.view = view; 1021 ctx.editorUI = editorUI; 1022 ctx.startOffset = startOffset; 1023 ctx.endOffset = endOffset; 1024 ctx.startX = startX; 1025 ctx.startY = startY; 1026 ctx.targetOffset = targetOffset; 1027 1028 synchronized (editorUI) { ctx.doc = doc; 1030 if (ctx.doc == null) { return; 1032 } 1033 1034 ctx.text = DocumentUtilities.SEGMENT_CACHE.getSegment(); 1035 ctx.syntax = ctx.doc.getFreeSyntax(); 1036 ctx.doc.readLock(); 1037 1038 try { 1039 ctx.component = editorUI.getComponent(); 1040 ctx.docLen = ctx.doc.getLength(); 1041 1042 if (ctx.startOffset > ctx.docLen || ctx.endOffset > ctx.docLen) { 1043 return; 1044 } 1045 1046 1050 if (ctx.endOffset < ctx.docLen) { 1051 ctx.endOffset++; 1052 } 1053 1054 initInfo(ctx); 1056 1057 do { 1059 ctx.tokenID = ctx.syntax.nextToken(); 1060 1061 drawCurrentToken(ctx); 1063 1064 } while (ctx.continueDraw && ctx.tokenID != null); 1065 1066 if (ctx.endOffset == ctx.docLen) { 1067 handleEOL(ctx); 1068 1070 if (ctx.editorUI.textLimitLineVisible) { int lineX = ctx.startX + ctx.editorUI.textLimitWidth * ctx.editorUI.defaultSpaceWidth; 1072 if (ctx.graphics !=null){ 1073 ctx.graphics.setColor(ctx.editorUI.textLimitLineColor); 1074 Rectangle clip = ctx.graphics.getClipBounds(); 1075 if (clip.height>ctx.editorUI.getLineHeight()){ 1076 ctx.graphics.drawLine(lineX, ctx.y, lineX, ctx.y+clip.height); 1077 } 1078 } 1079 } 1080 1081 } 1082 1083 if (ctx.graphics != null) { 1085 graphicsSpecificUpdates(ctx); 1086 } 1087 1088 ctx.drawGraphics.setBuffer(null); 1089 } finally { 1090 ctx.drawGraphics.finish(); 1091 1092 if (ctx.syntax != null) { 1093 ctx.doc.releaseSyntax(ctx.syntax); 1094 } 1095 DocumentUtilities.SEGMENT_CACHE.releaseSegment(ctx.text); 1096 ctx.doc.readUnlock(); 1097 1098 } 1099 } } 1101 1102 1103 static class DrawInfo implements DrawContext { 1104 1105 1107 Color foreColor; 1108 1109 1110 Color backColor; 1111 1112 1113 Color underlineColor; 1114 1115 1116 Color waveUnderlineColor; 1117 1118 1119 Color strikeThroughColor; 1120 1121 1122 Font font; 1123 1124 1125 int startOffset; 1126 1127 1128 int endOffset; 1129 1130 1131 boolean bol; 1132 1133 1134 boolean eol; 1135 1136 1137 EditorUI editorUI; 1138 1139 1140 char[] buffer; 1141 1142 1143 int bufferStartOffset; 1144 1145 1146 TokenID tokenID; 1147 1148 1149 TokenContextPath tokenContextPath; 1150 1151 1152 int tokenOffset; 1153 1154 1155 int tokenLength; 1156 1157 1158 int fragmentOffset; 1159 1160 1161 int fragmentLength; 1162 1163 1165 1166 DrawGraphics drawGraphics; 1167 1168 1171 int targetOffset; 1172 1173 1174 Segment text; 1175 1176 1177 char[] textArray; 1178 1179 1180 Syntax syntax; 1181 1182 1183 View view; 1184 1185 1186 JTextComponent component; 1187 1188 1189 BaseDocument doc; 1190 1191 1192 int docLen; 1193 1194 1195 int visualColumn; 1196 1197 1198 int x; 1199 1200 1201 int y; 1202 1203 1204 int startX; 1205 1206 1207 int startY; 1208 1209 1210 int lineHeight; 1211 1212 1213 Coloring defaultColoring; 1214 1215 1216 int tabSize; 1217 1218 1219 int widestWidth; 1220 1221 1222 boolean continueDraw; 1223 1224 1225 int startLineNumber; 1226 1227 1230 int lineIndex; 1231 1232 1233 int[] lineStartOffsets; 1234 1235 1238 char[] lineNumberChars; 1239 1240 1241 Coloring lineNumberColoring; 1242 1243 1244 Graphics graphics; 1245 1246 1247 boolean lineNumbering; 1248 1249 1253 boolean syncedLineNumbering; 1254 1255 1256 DrawLayer[] layers; 1257 1258 1259 boolean[] layerActives; 1260 1261 1264 int[] layerActivityChangeOffsets; 1265 1266 1269 int updateOffset; 1270 1271 1272 int layerUpdateOffset; 1273 1274 1278 boolean drawMarkUpdate; 1279 1280 1281 List drawMarkList; 1282 1283 1284 int drawMarkIndex; 1285 1286 1287 MarkFactory.DrawMark drawMark; 1288 1289 1290 int drawMarkOffset; 1291 1292 1293 int drawnLength; 1294 1295 1296 int fragmentStartIndex; 1297 1298 1299 boolean tabsFragment; 1300 1301 1302 int spaceWidth; 1303 1304 1305 int fragmentWidth; 1306 1307 1310 int fragmentCharCount; 1311 1312 public Color getForeColor() { 1313 return foreColor; 1314 } 1315 1316 public void setForeColor(Color foreColor) { 1317 this.foreColor = foreColor; 1318 } 1319 1320 public Color getBackColor() { 1321 return backColor; 1322 } 1323 1324 public void setBackColor(Color backColor) { 1325 this.backColor = backColor; 1326 } 1327 1328 public Color getUnderlineColor() { 1329 return underlineColor; 1330 } 1331 1332 public void setUnderlineColor(Color underlineColor) { 1333 this.underlineColor = underlineColor; 1334 } 1335 1336 public Color getWaveUnderlineColor() { 1337 return waveUnderlineColor; 1338 } 1339 1340 public void setWaveUnderlineColor(Color waveUnderlineColor) { 1341 this.waveUnderlineColor = waveUnderlineColor; 1342 } 1343 1344 public Color getStrikeThroughColor() { 1345 return strikeThroughColor; 1346 } 1347 1348 public void setStrikeThroughColor(Color strikeThroughColor) { 1349 this.strikeThroughColor = strikeThroughColor; 1350 } 1351 1352 public Font getFont() { 1353 return font; 1354 } 1355 1356 public void setFont(Font font) { 1357 this.font = font; 1358 } 1359 1360 public int getStartOffset() { 1361 return startOffset; 1362 } 1363 1364 public int getEndOffset() { 1365 return endOffset; 1366 } 1367 1368 public boolean isBOL() { 1369 return bol; 1370 } 1371 1372 public boolean isEOL() { 1373 return eol; 1374 } 1375 1376 public EditorUI getEditorUI() { 1377 return editorUI; 1378 } 1379 1380 public char[] getBuffer() { 1381 return buffer; 1382 } 1383 1384 public int getBufferStartOffset() { 1385 return bufferStartOffset; 1386 } 1387 1388 public TokenID getTokenID() { 1389 return tokenID; 1390 } 1391 1392 public TokenContextPath getTokenContextPath() { 1393 return tokenContextPath; 1394 } 1395 1396 public int getTokenOffset() { 1397 return tokenOffset; 1398 } 1399 1400 public int getTokenLength() { 1401 return tokenLength; 1402 } 1403 1404 public int getFragmentOffset() { 1405 return fragmentOffset; 1406 } 1407 1408 public int getFragmentLength() { 1409 return fragmentLength; 1410 } 1411 1412 } 1413 1414 class PreinitializedDrawEngine extends DrawEngine{ 1415 1416 DrawGraphics drawGraphics; 1417 DrawInfo drawInfo; 1418 1419 public PreinitializedDrawEngine(DrawGraphics drawGraphics){ 1420 super(); 1421 this.drawGraphics = drawGraphics; 1422 } 1423 1424 public void release(){ 1425 drawInfo.drawGraphics.setBuffer(null); 1426 drawInfo.drawGraphics.finish(); 1427 1428 if (drawInfo.syntax != null) { 1429 drawInfo.doc.releaseSyntax(drawInfo.syntax); 1430 } 1431 DocumentUtilities.SEGMENT_CACHE.releaseSegment(drawInfo.text); 1432 } 1433 1434 public void preinitialize(BaseDocument doc, EditorUI editorUI, int startOffset, int endOffset, 1435 int startX, int startY, int targetOffset) throws BadLocationException { 1436 drawInfo = new DrawInfo(); 1438 drawInfo.drawGraphics = drawGraphics; 1439 drawInfo.editorUI = editorUI; 1440 drawInfo.startOffset = startOffset; 1441 drawInfo.endOffset = endOffset; 1442 drawInfo.startX = startX; 1443 drawInfo.startY = startY; 1444 drawInfo.targetOffset = targetOffset; 1445 1446 synchronized (editorUI) { drawInfo.doc = doc; 1448 if (drawInfo.doc == null) { return; 1450 } 1451 1452 try{ 1453 drawInfo.text = DocumentUtilities.SEGMENT_CACHE.getSegment(); 1454 drawInfo.syntax = drawInfo.doc.getFreeSyntax(); 1455 drawInfo.doc.readLock(); 1456 1457 drawInfo.component = editorUI.getComponent(); 1458 drawInfo.docLen = drawInfo.doc.getLength(); 1459 1460 if (drawInfo.startOffset > drawInfo.docLen || drawInfo.endOffset > drawInfo.docLen) { 1461 return; 1462 } 1463 1464 1468 if (drawInfo.endOffset < drawInfo.docLen) { 1469 drawInfo.endOffset++; 1470 } 1471 1472 initInfo(drawInfo); 1474 } finally{ 1475 drawInfo.doc.readUnlock(); 1476 } 1477 } 1478 } 1479 1480 void draw(DrawGraphics dg, EditorUI editorUI, int startOffset, int endOffset, 1481 int startX, int startY, int targetOffset) throws BadLocationException { 1482 if (startOffset < 0 || endOffset < 0 || startOffset > endOffset 1484 || startX < 0 || startY < 0 1485 ) { 1486 return; 1487 } 1488 if (dg == null){ 1489 dg = drawGraphics; 1490 }else{ 1491 super.draw(dg, editorUI, startOffset, endOffset, startX, startY, targetOffset); 1492 return; 1493 } 1494 1495 synchronized (editorUI){ 1496 DrawInfo ctx = drawInfo; 1497 ctx.doc.readLock(); 1498 try{ 1499 do { 1500 ctx.tokenID = ctx.syntax.nextToken(); 1501 1502 drawCurrentToken(ctx); 1504 1505 } while (ctx.continueDraw && ctx.tokenID != null); 1506 1507 if (ctx.endOffset == ctx.docLen) { 1508 handleEOL(ctx); 1509 } 1510 1511 ctx.editorUI.updateVirtualWidth(ctx.widestWidth 1512 + ctx.editorUI.lineNumberWidth + 2 * ctx.editorUI.defaultSpaceWidth); 1513 1514 if (ctx.graphics != null) { 1516 graphicsSpecificUpdates(ctx); 1517 } 1518 }finally{ 1519 ctx.doc.readUnlock(); 1520 } 1521 } 1522 } 1523 1524 } 1525 1526 1527} 1528 | Popular Tags |