1 23 24 package org.gjt.sp.jedit.textarea; 25 26 import java.util.*; 28 import org.gjt.sp.jedit.buffer.JEditBuffer; 29 import org.gjt.sp.jedit.Debug; 30 import org.gjt.sp.jedit.syntax.*; 31 import org.gjt.sp.util.Log; 32 34 42 class ChunkCache 43 { 44 ChunkCache(TextArea textArea) 46 { 47 this.textArea = textArea; 48 out = new ArrayList<Chunk>(); 49 tokenHandler = new DisplayTokenHandler(); 50 } 52 59 int getMaxHorizontalScrollWidth() 60 { 61 int max = 0; 62 for(int i = 0; i < firstInvalidLine; i++) 63 { 64 LineInfo info = lineInfo[i]; 65 if(info.width > max) 66 max = info.width; 67 } 68 return max; 69 } 71 78 int getScreenLineOfOffset(int line, int offset) 79 { 80 if(lineInfo.length == 0) 81 return -1; 82 if(line < textArea.getFirstPhysicalLine()) 83 return -1; 84 if(line == textArea.getFirstPhysicalLine() 85 && offset < getLineInfo(0).offset) 86 return -1; 87 if(line > textArea.getLastPhysicalLine()) 88 return -1; 89 90 if(line == lastScreenLineP) 91 { 92 LineInfo last = getLineInfo(lastScreenLine); 93 94 if(offset >= last.offset 95 && offset < last.offset + last.length) { 96 return lastScreenLine; 97 } 98 } 99 100 int screenLine = -1; 101 102 for(int i = 0; i < textArea.getVisibleLines(); i++) 104 { 105 LineInfo info = getLineInfo(i); 106 if(info.physicalLine > line) 107 { 108 return i - 1; 110 } 112 if(info.physicalLine == line) 113 { 114 if(offset >= info.offset 115 && offset < info.offset + info.length) 116 { 117 screenLine = i; 118 break; 119 } 120 } 121 } 122 123 if(screenLine == -1) 124 return -1; 125 126 127 lastScreenLineP = line; 128 lastScreenLine = screenLine; 129 130 return screenLine; 131 } 133 138 void recalculateVisibleLines() 139 { 140 LineInfo[] newLineInfo = new LineInfo[textArea.getVisibleLines()]; 141 142 int start; 143 if(lineInfo == null) 144 start = 0; 145 else 146 { 147 start = Math.min(lineInfo.length,newLineInfo.length); 148 System.arraycopy(lineInfo,0,newLineInfo,0,start); 149 } 150 151 for(int i = start; i < newLineInfo.length; i++) 152 newLineInfo[i] = new LineInfo(); 153 154 lineInfo = newLineInfo; 155 156 lastScreenLine = lastScreenLineP = -1; 157 } 159 void setBuffer(JEditBuffer buffer) 161 { 162 this.buffer = buffer; 163 lastScreenLine = lastScreenLineP = -1; 164 } 166 void scrollDown(int amount) 168 { 169 int visibleLines = textArea.getVisibleLines(); 170 171 System.arraycopy(lineInfo,amount,lineInfo,0,visibleLines - amount); 172 173 for(int i = visibleLines - amount; i < visibleLines; i++) 174 { 175 lineInfo[i] = new LineInfo(); 176 } 177 178 firstInvalidLine -= amount; 179 if(firstInvalidLine < 0) 180 firstInvalidLine = 0; 181 182 if(Debug.CHUNK_CACHE_DEBUG) 183 { 184 System.err.println("f > t.f: only " + amount 185 + " need updates"); 186 } 187 188 lastScreenLine = lastScreenLineP = -1; 189 } 191 void scrollUp(int amount) 193 { 194 System.arraycopy(lineInfo,0,lineInfo,amount, 195 textArea.getVisibleLines() - amount); 196 197 for(int i = 0; i < amount; i++) 198 { 199 lineInfo[i] = new LineInfo(); 200 } 201 202 int oldFirstInvalidLine = firstInvalidLine; 204 firstInvalidLine = 0; 205 updateChunksUpTo(amount); 206 firstInvalidLine = oldFirstInvalidLine + amount; 207 if(firstInvalidLine > textArea.getVisibleLines()) 208 firstInvalidLine = textArea.getVisibleLines(); 209 210 if(Debug.CHUNK_CACHE_DEBUG) 211 { 212 Log.log(Log.DEBUG,this,"f > t.f: only " + amount 213 + " need updates"); 214 } 215 216 lastScreenLine = lastScreenLineP = -1; 217 } 219 void invalidateAll() 221 { 222 firstInvalidLine = 0; 223 lastScreenLine = lastScreenLineP = -1; 224 } 226 void invalidateChunksFrom(int screenLine) 228 { 229 if(Debug.CHUNK_CACHE_DEBUG) 230 Log.log(Log.DEBUG,this,"Invalidate from " + screenLine); 231 firstInvalidLine = Math.min(screenLine,firstInvalidLine); 232 233 if(screenLine <= lastScreenLine) 234 lastScreenLine = lastScreenLineP = -1; 235 } 237 void invalidateChunksFromPhys(int physicalLine) 239 { 240 for(int i = 0; i < firstInvalidLine; i++) 241 { 242 LineInfo info = lineInfo[i]; 243 if(info.physicalLine == -1 || info.physicalLine >= physicalLine) 244 { 245 firstInvalidLine = i; 246 if(i <= lastScreenLine) 247 lastScreenLine = lastScreenLineP = -1; 248 break; 249 } 250 } 251 } 253 LineInfo getLineInfo(int screenLine) 255 { 256 updateChunksUpTo(screenLine); 257 return lineInfo[screenLine]; 258 } 260 int getLineSubregionCount(int physicalLine) 262 { 263 if(!textArea.softWrap) 264 return 1; 265 266 out.clear(); 267 lineToChunkList(physicalLine,out); 268 269 int size = out.size(); 270 if(size == 0) 271 return 1; 272 else 273 return size; 274 } 276 289 static int getSubregionOfOffset(int offset, LineInfo[] lineInfos) 290 { 291 for(int i = 0; i < lineInfos.length; i++) 292 { 293 LineInfo info = lineInfos[i]; 294 if(offset >= info.offset && offset < info.offset + info.length) 295 return i; 296 } 297 298 return -1; 299 } 301 312 int xToSubregionOffset(int physicalLine, int subregion, int x, 313 boolean round) 314 { 315 LineInfo[] infos = getLineInfosForPhysicalLine(physicalLine); 316 if(subregion == -1) 317 subregion += infos.length; 318 return xToSubregionOffset(infos[subregion],x,round); 319 } 321 330 static int xToSubregionOffset(LineInfo info, int x, 331 boolean round) 332 { 333 int offset = Chunk.xToOffset(info.chunks,x,round); 334 if(offset == -1 || offset == info.offset + info.length) 335 offset = info.offset + info.length - 1; 336 337 return offset; 338 } 340 346 int subregionOffsetToX(int physicalLine, int offset) 347 { 348 LineInfo[] infos = getLineInfosForPhysicalLine(physicalLine); 349 LineInfo info = infos[getSubregionOfOffset(offset,infos)]; 350 return subregionOffsetToX(info,offset); 351 } 353 359 static int subregionOffsetToX(LineInfo info, int offset) 360 { 361 return (int)Chunk.offsetToX(info.chunks,offset); 362 } 364 371 int getSubregionStartOffset(int line, int offset) 372 { 373 LineInfo[] lineInfos = getLineInfosForPhysicalLine(line); 374 LineInfo info = lineInfos[getSubregionOfOffset(offset,lineInfos)]; 375 return textArea.getLineStartOffset(info.physicalLine) 376 + info.offset; 377 } 379 386 int getSubregionEndOffset(int line, int offset) 387 { 388 LineInfo[] lineInfos = getLineInfosForPhysicalLine(line); 389 LineInfo info = lineInfos[getSubregionOfOffset(offset,lineInfos)]; 390 return textArea.getLineStartOffset(info.physicalLine) 391 + info.offset + info.length; 392 } 394 402 int getBelowPosition(int physicalLine, int offset, int x, 403 boolean ignoreWrap) 404 { 405 LineInfo[] lineInfos = getLineInfosForPhysicalLine(physicalLine); 406 407 int subregion = getSubregionOfOffset(offset,lineInfos); 408 409 if(subregion != lineInfos.length - 1 && !ignoreWrap) 410 { 411 return textArea.getLineStartOffset(physicalLine) 412 + xToSubregionOffset(lineInfos[subregion + 1], 413 x,true); 414 } 415 else 416 { 417 int nextLine = textArea.displayManager 418 .getNextVisibleLine(physicalLine); 419 420 if(nextLine == -1) 421 return -1; 422 else 423 { 424 return textArea.getLineStartOffset(nextLine) 425 + xToSubregionOffset(nextLine,0, 426 x,true); 427 } 428 } 429 } 431 439 int getAbovePosition(int physicalLine, int offset, int x, 440 boolean ignoreWrap) 441 { 442 LineInfo[] lineInfos = getLineInfosForPhysicalLine(physicalLine); 443 444 int subregion = getSubregionOfOffset(offset,lineInfos); 445 446 if(subregion != 0 && !ignoreWrap) 447 { 448 return textArea.getLineStartOffset(physicalLine) 449 + xToSubregionOffset(lineInfos[subregion - 1], 450 x,true); 451 } 452 else 453 { 454 int prevLine = textArea.displayManager 455 .getPrevVisibleLine(physicalLine); 456 457 if(prevLine == -1) 458 return -1; 459 else 460 { 461 return textArea.getLineStartOffset(prevLine) 462 + xToSubregionOffset(prevLine,-1, 463 x,true); 464 } 465 } 466 } 468 473 boolean needFullRepaint() 474 { 475 boolean retVal = needFullRepaint; 476 needFullRepaint = false; 477 return retVal; 478 } 480 LineInfo[] getLineInfosForPhysicalLine(int physicalLine) 482 { 483 out.clear(); 484 485 if(!buffer.isLoading()) 486 lineToChunkList(physicalLine,out); 487 488 if(out.isEmpty()) 489 out.add(null); 490 491 List<LineInfo> returnValue = new ArrayList<LineInfo>(out.size()); 492 getLineInfosForPhysicalLine(physicalLine,returnValue); 493 return returnValue.toArray(new LineInfo[out.size()]); 494 } 496 498 private final TextArea textArea; 500 private JEditBuffer buffer; 501 506 private LineInfo[] lineInfo; 507 private final List<Chunk> out; 508 509 510 private int firstInvalidLine; 511 private int lastScreenLineP; 512 private int lastScreenLine; 513 514 private boolean needFullRepaint; 515 516 private final DisplayTokenHandler tokenHandler; 517 519 private void getLineInfosForPhysicalLine(int physicalLine, List<LineInfo> list) 521 { 522 for(int i = 0; i < out.size(); i++) 523 { 524 Chunk chunks = out.get(i); 525 LineInfo info = new LineInfo(); 526 info.physicalLine = physicalLine; 527 if(i == 0) 528 { 529 info.firstSubregion = true; 530 info.offset = 0; 531 } 532 else 533 info.offset = chunks.offset; 534 535 if(i == out.size() - 1) 536 { 537 info.lastSubregion = true; 538 info.length = textArea.getLineLength(physicalLine) 539 - info.offset + 1; 540 } 541 else 542 { 543 info.length = out.get(i + 1).offset 544 - info.offset; 545 } 546 547 info.chunks = chunks; 548 549 list.add(info); 550 } 551 } 553 557 private int getFirstScreenLine() 558 { 559 for(int i = firstInvalidLine - 1; i >= 0; i--) 560 { 561 if(lineInfo[i].lastSubregion) 562 return i + 1; 563 } 564 565 return 0; 566 } 568 572 private int getUpdateStartLine(int firstScreenLine) 573 { 574 if(firstScreenLine == 0) 577 { 578 return textArea.getFirstPhysicalLine(); 579 } 580 else 582 { 583 int prevPhysLine = lineInfo[ 584 firstScreenLine - 1] 585 .physicalLine; 586 if(prevPhysLine == -1) 589 return -1; 590 else 591 { 592 return textArea.displayManager 593 .getNextVisibleLine(prevPhysLine); 594 } 595 } 596 } 598 private void updateChunksUpTo(int lastScreenLine) 600 { 601 if(lastScreenLine >= lineInfo.length) 603 throw new ArrayIndexOutOfBoundsException (lastScreenLine); 604 605 if(lastScreenLine < firstInvalidLine) 608 return; 609 610 int firstScreenLine = getFirstScreenLine(); 611 int physicalLine = getUpdateStartLine(firstScreenLine); 612 613 if(Debug.CHUNK_CACHE_DEBUG) 614 { 615 Log.log(Log.DEBUG,this,"Updating chunks from " + firstScreenLine 616 + " to " + lastScreenLine); 617 } 618 619 624 out.clear(); 625 626 int offset = 0; 627 int length = 0; 628 629 for(int i = firstScreenLine; i <= lastScreenLine; i++) 630 { 631 LineInfo info = lineInfo[i]; 632 633 Chunk chunks; 634 635 if(out.isEmpty()) 637 { 638 if(physicalLine != -1 && i != firstScreenLine) 641 { 642 physicalLine = textArea.displayManager 643 .getNextVisibleLine(physicalLine); 644 } 645 646 if(physicalLine == -1) 648 { 649 info.chunks = null; 650 info.physicalLine = -1; 651 info.width = 0; 655 continue; 656 } 657 658 lineToChunkList(physicalLine,out); 660 661 info.firstSubregion = true; 662 663 int screenLines; 664 665 if(out.isEmpty()) 667 { 668 screenLines = 1; 669 670 if(i == 0) 671 { 672 if(textArea.displayManager.firstLine.skew > 0) 673 { 674 Log.log(Log.ERROR,this,"BUG: skew=" + textArea.displayManager.firstLine.skew + ",out.size()=" + out.size()); 675 textArea.displayManager.firstLine.skew = 0; 676 needFullRepaint = true; 677 lastScreenLine = lineInfo.length - 1; 678 } 679 } 680 chunks = null; 681 offset = 0; 682 length = 1; 683 } 684 else 686 { 687 screenLines = out.size(); 688 689 if(i == 0) 690 { 691 int skew = textArea.displayManager.firstLine.skew; 692 if(skew >= out.size()) 693 { 694 Log.log(Log.ERROR,this,"BUG: skew=" + skew + ",out.size()=" + out.size()); 695 skew = 0; 696 needFullRepaint = true; 697 lastScreenLine = lineInfo.length - 1; 698 } 699 else if(skew > 0) 700 { 701 info.firstSubregion = false; 702 for(int j = 0; j < skew; j++) 703 out.remove(0); 704 } 705 } 706 chunks = out.remove(0); 707 offset = chunks.offset; 708 if (!out.isEmpty()) 709 length = out.get(0).offset - offset; 710 else 711 length = textArea.getLineLength(physicalLine) - offset + 1; 712 } 713 } 714 else 715 { 716 info.firstSubregion = false; 717 718 chunks = out.remove(0); 719 offset = chunks.offset; 720 if (!out.isEmpty()) 721 length = out.get(0).offset - offset; 722 else 723 length = textArea.getLineLength(physicalLine) - offset + 1; 724 } 725 726 boolean lastSubregion = out.isEmpty(); 727 728 if(i == lastScreenLine 729 && lastScreenLine != lineInfo.length - 1) 730 { 731 733 if(tokenHandler.getLineContext() != 734 info.lineContext) 735 { 736 lastScreenLine++; 737 needFullRepaint = true; 738 } 739 748 else if(info.physicalLine != physicalLine 749 || info.lastSubregion != lastSubregion) 750 { 751 lastScreenLine++; 752 needFullRepaint = true; 753 } 754 757 else if (!out.isEmpty()) 758 lastScreenLine++; 759 } 760 761 info.physicalLine = physicalLine; 762 info.lastSubregion = lastSubregion; 763 info.offset = offset; 764 info.length = length; 765 info.chunks = chunks; 766 info.lineContext = tokenHandler.getLineContext(); 767 } 768 769 firstInvalidLine = Math.max(lastScreenLine + 1,firstInvalidLine); 770 } 772 private void lineToChunkList(int physicalLine, List<Chunk> out) 774 { 775 TextAreaPainter painter = textArea.getPainter(); 776 777 tokenHandler.init(painter.getStyles(), 778 painter.getFontRenderContext(), 779 painter,out, 780 textArea.softWrap 781 ? textArea.wrapMargin : 0.0f); 782 buffer.markTokens(physicalLine,tokenHandler); 783 } 785 787 789 static class LineInfo 790 { 791 int physicalLine; 792 int offset; 793 int length; 794 boolean firstSubregion; 795 boolean lastSubregion; 796 Chunk chunks; 797 798 int width; 799 TokenMarker.LineContext lineContext; 800 } } 802 | Popular Tags |