1 package org.jedit.syntax; 2 3 11 12 import javax.swing.border.*; 13 import javax.swing.event.*; 14 import javax.swing.text.*; 15 import javax.swing.undo.*; 16 import javax.swing.*; 17 import java.awt.datatransfer.*; 18 import java.awt.event.*; 19 import java.awt.*; 20 import java.util.Enumeration ; 21 import java.util.Vector ; 22 23 53 public class JEditTextArea extends JComponent { 54 59 public static String LEFT_OF_SCROLLBAR = "los"; 60 61 64 public JEditTextArea() { 65 this(TextAreaDefaults.getDefaults()); 66 } 67 68 72 public JEditTextArea(TextAreaDefaults defaults) { 73 enableEvents(AWTEvent.KEY_EVENT_MASK); 75 76 painter = new TextAreaPainter(this, defaults); 78 documentHandler = new DocumentHandler(); 79 listenerList = new EventListenerList(); 80 caretEvent = new MutableCaretEvent(); 81 lineSegment = new Segment(); 82 bracketLine = bracketPosition = -1; 83 blink = true; 84 85 setLayout(new ScrollLayout()); 87 add(CENTER, painter); 88 add(RIGHT, vertical = new JScrollBar(JScrollBar.VERTICAL)); 89 add(BOTTOM, horizontal = new JScrollBar(JScrollBar.HORIZONTAL)); 90 91 vertical.putClientProperty("JScrollBar.isFreeStanding", Boolean.FALSE); 92 horizontal.putClientProperty("JScrollBar.isFreeStanding", Boolean.FALSE); 93 94 vertical.addAdjustmentListener(new AdjustHandler()); 96 horizontal.addAdjustmentListener(new AdjustHandler()); 97 painter.addComponentListener(new ComponentHandler()); 98 painter.addMouseListener(new MouseHandler()); 99 painter.addMouseMotionListener(new DragHandler()); 100 addFocusListener(new FocusHandler()); 101 painter.addMouseWheelListener(new ScrollWheelHandler()); 102 103 setInputHandler(defaults.inputHandler); 105 setDocument(defaults.document); 106 editable = defaults.editable; 107 caretVisible = defaults.caretVisible; 108 caretBlinks = defaults.caretBlinks; 109 electricScroll = defaults.electricScroll; 110 111 popup = defaults.popup; 112 113 focusedComponent = this; 115 } 116 117 121 public final boolean isManagingFocus() { 122 return true; 123 } 124 125 128 public final TextAreaPainter getPainter() { 129 return painter; 130 } 131 132 135 public final InputHandler getInputHandler() { 136 return inputHandler; 137 } 138 139 143 public void setInputHandler(InputHandler inputHandler) { 144 this.inputHandler = inputHandler; 145 } 146 147 150 public final boolean isCaretBlinkEnabled() { 151 return caretBlinks; 152 } 153 154 158 public void setCaretBlinkEnabled(boolean caretBlinks) { 159 this.caretBlinks = caretBlinks; 160 if (!caretBlinks) 161 blink = false; 162 163 painter.invalidateSelectedLines(); 164 } 165 166 169 public final boolean isCaretVisible() { 170 return (!caretBlinks || blink) && caretVisible; 171 } 172 173 178 public void setCaretVisible(boolean caretVisible) { 179 this.caretVisible = caretVisible; 180 blink = true; 181 182 painter.invalidateSelectedLines(); 183 } 184 185 188 public final void blinkCaret() { 189 if (caretBlinks) { 190 blink = !blink; 191 painter.invalidateSelectedLines(); 192 } else 193 blink = true; 194 } 195 196 200 public final int getElectricScroll() { 201 return electricScroll; 202 } 203 204 210 public final void setElectricScroll(int electricScroll) { 211 this.electricScroll = electricScroll; 212 } 213 214 219 public void updateScrollBars() { 220 if (vertical != null && visibleLines != 0) { 221 vertical.setValues(firstLine, visibleLines, 0, getLineCount()); 222 vertical.setUnitIncrement(2); 223 vertical.setBlockIncrement(visibleLines); 224 } 225 226 int width = painter.getWidth(); 227 if (horizontal != null && width != 0) { 228 horizontal.setValues(-horizontalOffset, width, 0, width * 5); 229 horizontal.setUnitIncrement(painter.getFontMetrics().charWidth('w')); 230 horizontal.setBlockIncrement(width / 2); 231 } 232 } 233 234 237 public final int getFirstLine() { 238 return firstLine; 239 } 240 241 245 public void setFirstLine(int firstLine) { 246 if (firstLine == this.firstLine) 247 return; 248 int oldFirstLine = this.firstLine; 249 this.firstLine = firstLine; 250 if (firstLine != vertical.getValue()) 251 updateScrollBars(); 252 painter.repaint(); 253 } 254 255 258 public final int getVisibleLines() { 259 return visibleLines; 260 } 261 262 266 public final void recalculateVisibleLines() { 267 if (painter == null) 268 return; 269 int height = painter.getHeight(); 270 int lineHeight = painter.getFontMetrics().getHeight(); 271 int oldVisibleLines = visibleLines; 272 visibleLines = height / lineHeight; 273 updateScrollBars(); 274 } 275 276 279 public final int getHorizontalOffset() { 280 return horizontalOffset; 281 } 282 283 288 public void setHorizontalOffset(int horizontalOffset) { 289 if (horizontalOffset == this.horizontalOffset) 290 return; 291 this.horizontalOffset = horizontalOffset; 292 if (horizontalOffset != horizontal.getValue()) 293 updateScrollBars(); 294 painter.repaint(); 295 } 296 297 304 public boolean setOrigin(int firstLine, int horizontalOffset) { 305 boolean changed = false; 306 int oldFirstLine = this.firstLine; 307 308 if (horizontalOffset != this.horizontalOffset) { 309 this.horizontalOffset = horizontalOffset; 310 changed = true; 311 } 312 313 if (firstLine != this.firstLine) { 314 this.firstLine = firstLine; 315 changed = true; 316 } 317 318 if (changed) { 319 updateScrollBars(); 320 painter.repaint(); 321 } 322 323 return changed; 324 } 325 326 332 public boolean scrollToCaret() { 333 int line = getCaretLine(); 334 int lineStart = getLineStartOffset(line); 335 int offset = Math.max(0, Math.min(getLineLength(line) - 1, getCaretPosition() - lineStart)); 336 337 return scrollTo(line, offset); 338 } 339 340 348 public boolean scrollTo(int line, int offset) { 349 if (visibleLines == 0) { 353 setFirstLine(Math.max(0, line - electricScroll)); 354 return true; 355 } 356 357 int newFirstLine = firstLine; 358 int newHorizontalOffset = horizontalOffset; 359 360 if (line < firstLine + electricScroll) { 361 newFirstLine = Math.max(0, line - electricScroll); 362 } else if (line + electricScroll >= firstLine + visibleLines) { 363 newFirstLine = (line - visibleLines) + electricScroll + 1; 364 if (newFirstLine + visibleLines >= getLineCount()) 365 newFirstLine = getLineCount() - visibleLines; 366 if (newFirstLine < 0) 367 newFirstLine = 0; 368 } 369 370 int x = _offsetToX(line, offset); 371 int width = painter.getFontMetrics().charWidth('w'); 372 373 if (x < 0) { 374 newHorizontalOffset = Math.min(0, horizontalOffset - x + width + 5); 375 } else if (x + width >= painter.getWidth()) { 376 newHorizontalOffset = horizontalOffset + (painter.getWidth() - x) - width - 5; 377 } 378 379 return setOrigin(newFirstLine, newHorizontalOffset); 380 } 381 382 386 public int lineToY(int line) { 387 FontMetrics fm = painter.getFontMetrics(); 388 return (line - firstLine) * fm.getHeight() - (fm.getLeading() + fm.getMaxDescent()); 389 } 390 391 395 public int yToLine(int y) { 396 FontMetrics fm = painter.getFontMetrics(); 397 int height = fm.getHeight(); 398 return Math.max(0, Math.min(getLineCount() - 1, y / height + firstLine)); 399 } 400 401 407 public final int offsetToX(int line, int offset) { 408 painter.currentLineTokens = null; 410 return _offsetToX(line, offset); 411 } 412 413 420 public int _offsetToX(int line, int offset) { 421 TokenMarker tokenMarker = getTokenMarker(); 422 423 424 FontMetrics fm = painter.getFontMetrics(); 425 426 getLineText(line, lineSegment); 427 428 int segmentOffset = lineSegment.offset; 429 int x = horizontalOffset; 430 431 432 if (tokenMarker == null) { 433 lineSegment.count = offset; 434 return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0); 435 } 436 438 else { 439 Token tokens; 440 if (painter.currentLineIndex == line && painter.currentLineTokens != null) 441 tokens = painter.currentLineTokens; 442 else { 443 painter.currentLineIndex = line; 444 tokens = painter.currentLineTokens = tokenMarker.markTokens(lineSegment, line); 445 } 446 447 Toolkit toolkit = painter.getToolkit(); 448 Font defaultFont = painter.getFont(); 449 SyntaxStyle[] styles = painter.getStyles(); 450 451 for (;;) { 452 byte id = tokens.id; 453 if (id == Token.END) { 454 return x; 455 } 456 457 if (id == Token.NULL) 458 fm = painter.getFontMetrics(); 459 else 460 fm = styles[id].getFontMetrics(defaultFont); 461 462 int length = tokens.length; 463 464 if (offset + segmentOffset < lineSegment.offset + length) { 465 lineSegment.count = offset - (lineSegment.offset - segmentOffset); 466 return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0); 467 } else { 468 lineSegment.count = length; 469 x += Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0); 470 lineSegment.offset += length; 471 } 472 tokens = tokens.next; 473 } 474 } 475 } 476 477 482 public int xToOffset(int line, int x) { 483 TokenMarker tokenMarker = getTokenMarker(); 484 485 486 FontMetrics fm = painter.getFontMetrics(); 487 488 getLineText(line, lineSegment); 489 490 char[] segmentArray = lineSegment.array; 491 int segmentOffset = lineSegment.offset; 492 int segmentCount = lineSegment.count; 493 494 int width = horizontalOffset; 495 496 if (tokenMarker == null) { 497 for (int i = 0; i < segmentCount; i++) { 498 char c = segmentArray[i + segmentOffset]; 499 int charWidth; 500 if (c == '\t') 501 charWidth = (int) painter.nextTabStop(width, i) - width; 502 else 503 charWidth = fm.charWidth(c); 504 505 if (painter.isBlockCaretEnabled()) { 506 if (x - charWidth <= width) 507 return i; 508 } else { 509 if (x - charWidth / 2 <= width) 510 return i; 511 } 512 513 width += charWidth; 514 } 515 516 return segmentCount; 517 } else { 518 Token tokens; 519 if (painter.currentLineIndex == line && painter.currentLineTokens != null) 520 tokens = painter.currentLineTokens; 521 else { 522 painter.currentLineIndex = line; 523 tokens = painter.currentLineTokens = tokenMarker.markTokens(lineSegment, line); 524 } 525 526 int offset = 0; 527 Toolkit toolkit = painter.getToolkit(); 528 Font defaultFont = painter.getFont(); 529 SyntaxStyle[] styles = painter.getStyles(); 530 531 for (;;) { 532 byte id = tokens.id; 533 if (id == Token.END) 534 return offset; 535 536 if (id == Token.NULL) 537 fm = painter.getFontMetrics(); 538 else 539 fm = styles[id].getFontMetrics(defaultFont); 540 541 int length = tokens.length; 542 543 for (int i = 0; i < length; i++) { 544 char c = segmentArray[segmentOffset + offset + i]; 545 int charWidth; 546 if (c == '\t') 547 charWidth = (int) painter.nextTabStop(width, offset + i) - width; 548 else 549 charWidth = fm.charWidth(c); 550 551 if (painter.isBlockCaretEnabled()) { 552 if (x - charWidth <= width) 553 return offset + i; 554 } else { 555 if (x - charWidth / 2 <= width) 556 return offset + i; 557 } 558 559 width += charWidth; 560 } 561 562 offset += length; 563 tokens = tokens.next; 564 } 565 } 566 } 567 568 573 public int xyToOffset(int x, int y) { 574 int line = yToLine(y); 575 int start = getLineStartOffset(line); 576 return start + xToOffset(line, x); 577 } 578 579 582 public final SyntaxDocument getDocument() { 583 return document; 584 } 585 586 590 public void setDocument(SyntaxDocument document) { 591 if (this.document == document) 592 return; 593 if (this.document != null) 594 this.document.removeDocumentListener(documentHandler); 595 this.document = document; 596 597 document.addDocumentListener(documentHandler); 598 599 select(0, 0); 600 updateScrollBars(); 601 painter.repaint(); 602 } 603 604 608 public final TokenMarker getTokenMarker() { 609 return document.getTokenMarker(); 610 } 611 612 617 public final void setTokenMarker(TokenMarker tokenMarker) { 618 document.setTokenMarker(tokenMarker); 619 } 620 621 625 public final int getDocumentLength() { 626 return document.getLength(); 627 } 628 629 632 public final int getLineCount() { 633 return document.getDefaultRootElement().getElementCount(); 634 } 635 636 640 public final int getLineOfOffset(int offset) { 641 return document.getDefaultRootElement().getElementIndex(offset); 642 } 643 644 650 public int getLineStartOffset(int line) { 651 Element lineElement = document.getDefaultRootElement().getElement(line); 652 if (lineElement == null) 653 return -1; 654 else 655 return lineElement.getStartOffset(); 656 } 657 658 664 public int getLineEndOffset(int line) { 665 Element lineElement = document.getDefaultRootElement().getElement(line); 666 if (lineElement == null) 667 return -1; 668 else 669 return lineElement.getEndOffset(); 670 } 671 672 676 public int getLineLength(int line) { 677 Element lineElement = document.getDefaultRootElement().getElement(line); 678 if (lineElement == null) 679 return -1; 680 else 681 return lineElement.getEndOffset() - lineElement.getStartOffset() - 1; 682 } 683 684 687 public String getText() { 688 try { 689 return document.getText(0, document.getLength()); 690 } catch (BadLocationException bl) { 691 bl.printStackTrace(); 692 return null; 693 } 694 } 695 696 699 public void setText(String text) { 700 try { 701 document.beginCompoundEdit(); 702 document.remove(0, document.getLength()); 703 document.insertString(0, text, null); 704 } catch (BadLocationException bl) { 705 bl.printStackTrace(); 706 } finally { 707 document.endCompoundEdit(); 708 } 709 } 710 711 717 public final String getText(int start, int len) { 718 try { 719 return document.getText(start, len); 720 } catch (BadLocationException bl) { 721 bl.printStackTrace(); 722 return null; 723 } 724 } 725 726 733 public final void getText(int start, int len, Segment segment) { 734 try { 735 document.getText(start, len, segment); 736 } catch (BadLocationException bl) { 737 bl.printStackTrace(); 738 segment.offset = segment.count = 0; 739 } 740 } 741 742 747 public final String getLineText(int lineIndex) { 748 int start = getLineStartOffset(lineIndex); 749 return getText(start, getLineEndOffset(lineIndex) - start - 1); 750 } 751 752 757 public final void getLineText(int lineIndex, Segment segment) { 758 int start = getLineStartOffset(lineIndex); 759 getText(start, getLineEndOffset(lineIndex) - start - 1, segment); 760 } 761 762 765 public final int getSelectionStart() { 766 return selectionStart; 767 } 768 769 773 public int getSelectionStart(int line) { 774 if (line == selectionStartLine) 775 return selectionStart; 776 else if (rectSelect) { 777 Element map = document.getDefaultRootElement(); 778 int start = selectionStart - map.getElement(selectionStartLine).getStartOffset(); 779 780 Element lineElement = map.getElement(line); 781 int lineStart = lineElement.getStartOffset(); 782 int lineEnd = lineElement.getEndOffset() - 1; 783 return Math.min(lineEnd, lineStart + start); 784 } else 785 return getLineStartOffset(line); 786 } 787 788 791 public final int getSelectionStartLine() { 792 return selectionStartLine; 793 } 794 795 801 public final void setSelectionStart(int selectionStart) { 802 select(selectionStart, selectionEnd); 803 } 804 805 808 public final int getSelectionEnd() { 809 return selectionEnd; 810 } 811 812 816 public int getSelectionEnd(int line) { 817 if (line == selectionEndLine) 818 return selectionEnd; 819 else if (rectSelect) { 820 Element map = document.getDefaultRootElement(); 821 int end = selectionEnd - map.getElement(selectionEndLine).getStartOffset(); 822 823 Element lineElement = map.getElement(line); 824 int lineStart = lineElement.getStartOffset(); 825 int lineEnd = lineElement.getEndOffset() - 1; 826 return Math.min(lineEnd, lineStart + end); 827 } else 828 return getLineEndOffset(line) - 1; 829 } 830 831 834 public final int getSelectionEndLine() { 835 return selectionEndLine; 836 } 837 838 844 public final void setSelectionEnd(int selectionEnd) { 845 select(selectionStart, selectionEnd); 846 } 847 848 853 public final int getCaretPosition() { 854 return (biasLeft ? selectionStart : selectionEnd); 855 } 856 857 860 public final int getCaretLine() { 861 return (biasLeft ? selectionStartLine : selectionEndLine); 862 } 863 864 869 public final int getMarkPosition() { 870 return (biasLeft ? selectionEnd : selectionStart); 871 } 872 873 876 public final int getMarkLine() { 877 return (biasLeft ? selectionEndLine : selectionStartLine); 878 } 879 880 886 public final void setCaretPosition(int caret) { 887 select(caret, caret); 888 } 889 890 893 public final void selectAll() { 894 select(0, getDocumentLength()); 895 } 896 897 900 public final void selectNone() { 901 select(getCaretPosition(), getCaretPosition()); 902 } 903 904 912 public void select(int start, int end) { 913 int newStart, newEnd; 914 boolean newBias; 915 if (start <= end) { 916 newStart = start; 917 newEnd = end; 918 newBias = false; 919 } else { 920 newStart = end; 921 newEnd = start; 922 newBias = true; 923 } 924 925 if (newStart < 0 || newEnd > getDocumentLength()) { 926 throw new IllegalArgumentException ( 927 "Bounds out of" + " range: " + newStart + "," + newEnd); 928 } 929 930 if (newStart != selectionStart || newEnd != selectionEnd || newBias != biasLeft) { 934 int newStartLine = getLineOfOffset(newStart); 935 int newEndLine = getLineOfOffset(newEnd); 936 937 if (painter.isBracketHighlightEnabled()) { 938 if (bracketLine != -1) 939 painter.invalidateLine(bracketLine); 940 updateBracketHighlight(end); 941 if (bracketLine != -1) 942 painter.invalidateLine(bracketLine); 943 } 944 945 painter.invalidateLineRange(selectionStartLine, selectionEndLine); 946 painter.invalidateLineRange(newStartLine, newEndLine); 947 948 document.addUndoableEdit(new CaretUndo(selectionStart, selectionEnd)); 949 950 selectionStart = newStart; 951 selectionEnd = newEnd; 952 selectionStartLine = newStartLine; 953 selectionEndLine = newEndLine; 954 biasLeft = newBias; 955 956 fireCaretEvent(); 957 } 958 959 blink = true; 962 caretTimer.restart(); 963 964 if (selectionStart == selectionEnd) 966 rectSelect = false; 967 968 magicCaret = -1; 970 971 scrollToCaret(); 972 } 973 974 977 public final String getSelectedText() { 978 if (selectionStart == selectionEnd) 979 return null; 980 981 if (rectSelect) { 982 984 Element map = document.getDefaultRootElement(); 985 986 int start = selectionStart - map.getElement(selectionStartLine).getStartOffset(); 987 int end = selectionEnd - map.getElement(selectionEndLine).getStartOffset(); 988 989 if (end < start) { 991 int tmp = end; 992 end = start; 993 start = tmp; 994 } 995 996 StringBuffer buf = new StringBuffer (); 997 Segment seg = new Segment(); 998 999 for (int i = selectionStartLine; i <= selectionEndLine; i++) { 1000 Element lineElement = map.getElement(i); 1001 int lineStart = lineElement.getStartOffset(); 1002 int lineEnd = lineElement.getEndOffset() - 1; 1003 int lineLen = lineEnd - lineStart; 1004 1005 lineStart = Math.min(lineStart + start, lineEnd); 1006 lineLen = Math.min(end - start, lineEnd - lineStart); 1007 1008 getText(lineStart, lineLen, seg); 1009 buf.append(seg.array, seg.offset, seg.count); 1010 1011 if (i != selectionEndLine) 1012 buf.append('\n'); 1013 } 1014 1015 return buf.toString(); 1016 } else { 1017 return getText(selectionStart, selectionEnd - selectionStart); 1018 } 1019 } 1020 1021 1025 public void setSelectedText(String selectedText) { 1026 if (!editable) { 1027 throw new InternalError ("Text component" + " read only"); 1028 } 1029 1030 document.beginCompoundEdit(); 1031 1032 try { 1033 if (rectSelect) { 1034 Element map = document.getDefaultRootElement(); 1035 1036 int start = selectionStart - map.getElement(selectionStartLine).getStartOffset(); 1037 int end = selectionEnd - map.getElement(selectionEndLine).getStartOffset(); 1038 1039 if (end < start) { 1041 int tmp = end; 1042 end = start; 1043 start = tmp; 1044 } 1045 1046 int lastNewline = 0; 1047 int currNewline = 0; 1048 1049 for (int i = selectionStartLine; i <= selectionEndLine; i++) { 1050 Element lineElement = map.getElement(i); 1051 int lineStart = lineElement.getStartOffset(); 1052 int lineEnd = lineElement.getEndOffset() - 1; 1053 int rectStart = Math.min(lineEnd, lineStart + start); 1054 1055 document.remove(rectStart, Math.min(lineEnd - rectStart, end - start)); 1056 1057 if (selectedText == null) 1058 continue; 1059 1060 currNewline = selectedText.indexOf('\n', lastNewline); 1061 if (currNewline == -1) 1062 currNewline = selectedText.length(); 1063 1064 document.insertString( 1065 rectStart, 1066 selectedText.substring(lastNewline, currNewline), 1067 null); 1068 1069 lastNewline = Math.min(selectedText.length(), currNewline + 1); 1070 } 1071 1072 if (selectedText != null && currNewline != selectedText.length()) { 1073 int offset = map.getElement(selectionEndLine).getEndOffset() - 1; 1074 document.insertString(offset, "\n", null); 1075 document.insertString( 1076 offset + 1, 1077 selectedText.substring(currNewline + 1), 1078 null); 1079 } 1080 } else { 1081 document.remove(selectionStart, selectionEnd - selectionStart); 1082 if (selectedText != null) { 1083 document.insertString(selectionStart, selectedText, null); 1084 } 1085 } 1086 } catch (BadLocationException bl) { 1087 bl.printStackTrace(); 1088 throw new InternalError ("Cannot replace" + " selection"); 1089 } 1090 finally { 1093 document.endCompoundEdit(); 1094 } 1095 1096 setCaretPosition(selectionEnd); 1097 } 1098 1099 1102 public final boolean isEditable() { 1103 return editable; 1104 } 1105 1106 1111 public final void setEditable(boolean editable) { 1112 this.editable = editable; 1113 } 1114 1115 1118 public final JPopupMenu getRightClickPopup() { 1119 return popup; 1120 } 1121 1122 1126 public final void setRightClickPopup(JPopupMenu popup) { 1127 this.popup = popup; 1128 } 1129 1130 1134 public final int getMagicCaretPosition() { 1135 return magicCaret; 1136 } 1137 1138 1143 public final void setMagicCaretPosition(int magicCaret) { 1144 this.magicCaret = magicCaret; 1145 } 1146 1147 1154 public void overwriteSetSelectedText(String str) { 1155 if (!overwrite || selectionStart != selectionEnd) { 1157 setSelectedText(str); 1158 return; 1159 } 1160 1161 int caret = getCaretPosition(); 1164 int caretLineEnd = getLineEndOffset(getCaretLine()); 1165 if (caretLineEnd - caret <= str.length()) { 1166 setSelectedText(str); 1167 return; 1168 } 1169 1170 document.beginCompoundEdit(); 1171 1172 try { 1173 document.remove(caret, str.length()); 1174 document.insertString(caret, str, null); 1175 } catch (BadLocationException bl) { 1176 bl.printStackTrace(); 1177 } finally { 1178 document.endCompoundEdit(); 1179 } 1180 } 1181 1182 1185 public final boolean isOverwriteEnabled() { 1186 return overwrite; 1187 } 1188 1189 1194 public final void setOverwriteEnabled(boolean overwrite) { 1195 this.overwrite = overwrite; 1196 painter.invalidateSelectedLines(); 1197 } 1198 1199 1202 public final boolean isSelectionRectangular() { 1203 return rectSelect; 1204 } 1205 1206 1211 public final void setSelectionRectangular(boolean rectSelect) { 1212 this.rectSelect = rectSelect; 1213 painter.invalidateSelectedLines(); 1214 } 1215 1216 1220 public final int getBracketPosition() { 1221 return bracketPosition; 1222 } 1223 1224 1228 public final int getBracketLine() { 1229 return bracketLine; 1230 } 1231 1232 1236 public final void addCaretListener(CaretListener listener) { 1237 listenerList.add(CaretListener.class, listener); 1238 } 1239 1240 1244 public final void removeCaretListener(CaretListener listener) { 1245 listenerList.remove(CaretListener.class, listener); 1246 } 1247 1248 1252 public void cut() { 1253 if (editable) { 1254 copy(); 1255 setSelectedText(""); 1256 } 1257 } 1258 1259 1262 public void copy() { 1263 if (selectionStart != selectionEnd) { 1264 Clipboard clipboard = getToolkit().getSystemClipboard(); 1265 1266 String selection = getSelectedText(); 1267 1268 int repeatCount = inputHandler.getRepeatCount(); 1269 StringBuffer buf = new StringBuffer (); 1270 for (int i = 0; i < repeatCount; i++) 1271 buf.append(selection); 1272 1273 clipboard.setContents(new StringSelection(buf.toString()), null); 1274 } 1275 } 1276 1277 1280 public void paste() { 1281 if (editable) { 1282 Clipboard clipboard = getToolkit().getSystemClipboard(); 1283 try { 1284 String selection = 1287 ( 1288 (String ) clipboard.getContents(this).getTransferData( 1289 DataFlavor.stringFlavor)).replace( 1290 '\r', 1291 '\n'); 1292 1293 int repeatCount = inputHandler.getRepeatCount(); 1294 StringBuffer buf = new StringBuffer (); 1295 for (int i = 0; i < repeatCount; i++) 1296 buf.append(selection); 1297 selection = buf.toString(); 1298 setSelectedText(selection); 1299 } catch (Exception e) { 1300 getToolkit().beep(); 1301 System.err.println("Clipboard does not" + " contain a string"); 1302 } 1303 } 1304 } 1305 1306 1310 public void removeNotify() { 1311 super.removeNotify(); 1312 if (focusedComponent == this) 1313 focusedComponent = null; 1314 } 1315 1316 1321 public void processKeyEvent(KeyEvent evt) { 1322 int keyCode = evt.getKeyCode(); 1323 if (keyCode != KeyEvent.VK_TAB) 1324 getParent().dispatchEvent(evt); 1325 1326 if (inputHandler == null) 1327 return; 1328 1329 switch (evt.getID()) { 1330 case KeyEvent.KEY_TYPED : 1331 inputHandler.keyTyped(evt); 1332 break; 1333 case KeyEvent.KEY_PRESSED : 1334 inputHandler.keyPressed(evt); 1335 break; 1336 case KeyEvent.KEY_RELEASED : 1337 inputHandler.keyReleased(evt); 1338 break; 1339 } 1340 } 1341 1342 protected static String CENTER = "center"; 1344 protected static String RIGHT = "right"; 1345 protected static String BOTTOM = "bottom"; 1346 1347 protected static JEditTextArea focusedComponent; 1348 protected static Timer caretTimer; 1349 1350 protected TextAreaPainter painter; 1351 1352 protected JPopupMenu popup; 1353 1354 protected EventListenerList listenerList; 1355 protected MutableCaretEvent caretEvent; 1356 1357 protected boolean caretBlinks; 1358 protected boolean caretVisible; 1359 protected boolean blink; 1360 1361 protected boolean editable; 1362 1363 protected int firstLine; 1364 protected int visibleLines; 1365 protected int electricScroll; 1366 1367 protected int horizontalOffset; 1368 1369 protected JScrollBar vertical; 1370 protected JScrollBar horizontal; 1371 protected boolean scrollBarsInitialized; 1372 1373 protected InputHandler inputHandler; 1374 protected SyntaxDocument document; 1375 protected DocumentHandler documentHandler; 1376 1377 protected Segment lineSegment; 1378 1379 protected int selectionStart; 1380 protected int selectionStartLine; 1381 protected int selectionEnd; 1382 protected int selectionEndLine; 1383 protected boolean biasLeft; 1384 1385 protected int bracketPosition; 1386 protected int bracketLine; 1387 1388 protected int magicCaret; 1389 protected boolean overwrite; 1390 protected boolean rectSelect; 1391 1392 protected void fireCaretEvent() { 1393 Object [] listeners = listenerList.getListenerList(); 1394 for (int i = listeners.length - 2; i >= 0; i--) { 1395 if (listeners[i] == CaretListener.class) { 1396 ((CaretListener) listeners[i + 1]).caretUpdate(caretEvent); 1397 } 1398 } 1399 } 1400 1401 protected void updateBracketHighlight(int newCaretPosition) { 1402 if (newCaretPosition == 0) { 1403 bracketPosition = bracketLine = -1; 1404 return; 1405 } 1406 1407 try { 1408 int offset = TextUtilities.findMatchingBracket(document, newCaretPosition - 1); 1409 if (offset != -1) { 1410 bracketLine = getLineOfOffset(offset); 1411 bracketPosition = offset - getLineStartOffset(bracketLine); 1412 return; 1413 } 1414 } catch (BadLocationException bl) { 1415 bl.printStackTrace(); 1416 } 1417 1418 bracketLine = bracketPosition = -1; 1419 } 1420 1421 protected void documentChanged(DocumentEvent evt) { 1422 DocumentEvent.ElementChange ch = evt.getChange(document.getDefaultRootElement()); 1423 1424 int count; 1425 if (ch == null) 1426 count = 0; 1427 else 1428 count = ch.getChildrenAdded().length - ch.getChildrenRemoved().length; 1429 1430 int line = getLineOfOffset(evt.getOffset()); 1431 if (count == 0) { 1432 painter.invalidateLine(line); 1433 } 1434 else if (line < firstLine) { 1436 setFirstLine(firstLine + count); 1437 } 1438 else { 1440 painter.invalidateLineRange(line, firstLine + visibleLines); 1441 updateScrollBars(); 1442 } 1443 } 1444 1445 class ScrollLayout implements LayoutManager { 1446 public void addLayoutComponent(String name, Component comp) { 1447 if (name.equals(CENTER)) 1448 center = comp; 1449 else if (name.equals(RIGHT)) 1450 right = comp; 1451 else if (name.equals(BOTTOM)) 1452 bottom = comp; 1453 else if (name.equals(LEFT_OF_SCROLLBAR)) 1454 leftOfScrollBar.addElement(comp); 1455 } 1456 1457 public void removeLayoutComponent(Component comp) { 1458 if (center == comp) 1459 center = null; 1460 if (right == comp) 1461 right = null; 1462 if (bottom == comp) 1463 bottom = null; 1464 else 1465 leftOfScrollBar.removeElement(comp); 1466 } 1467 1468 public Dimension preferredLayoutSize(Container parent) { 1469 Dimension dim = new Dimension(); 1470 Insets insets = getInsets(); 1471 dim.width = insets.left + insets.right; 1472 dim.height = insets.top + insets.bottom; 1473 1474 Dimension centerPref = center.getPreferredSize(); 1475 dim.width += centerPref.width; 1476 dim.height += centerPref.height; 1477 Dimension rightPref = right.getPreferredSize(); 1478 dim.width += rightPref.width; 1479 Dimension bottomPref = bottom.getPreferredSize(); 1480 dim.height += bottomPref.height; 1481 1482 return dim; 1483 } 1484 1485 public Dimension minimumLayoutSize(Container parent) { 1486 Dimension dim = new Dimension(); 1487 Insets insets = getInsets(); 1488 dim.width = insets.left + insets.right; 1489 dim.height = insets.top + insets.bottom; 1490 1491 Dimension centerPref = center.getMinimumSize(); 1492 dim.width += centerPref.width; 1493 dim.height += centerPref.height; 1494 Dimension rightPref = right.getMinimumSize(); 1495 dim.width += rightPref.width; 1496 Dimension bottomPref = bottom.getMinimumSize(); 1497 dim.height += bottomPref.height; 1498 1499 return dim; 1500 } 1501 1502 public void layoutContainer(Container parent) { 1503 Dimension size = parent.getSize(); 1504 Insets insets = parent.getInsets(); 1505 int itop = insets.top; 1506 int ileft = insets.left; 1507 int ibottom = insets.bottom; 1508 int iright = insets.right; 1509 1510 int rightWidth = right.getPreferredSize().width; 1511 int bottomHeight = bottom.getPreferredSize().height; 1512 int centerWidth = size.width - rightWidth - ileft - iright; 1513 int centerHeight = size.height - bottomHeight - itop - ibottom; 1514 1515 center.setBounds(ileft, itop, centerWidth, centerHeight); 1516 1517 right.setBounds(ileft + centerWidth, itop, rightWidth, centerHeight); 1518 1519 Enumeration status = leftOfScrollBar.elements(); 1521 while (status.hasMoreElements()) { 1522 Component comp = (Component) status.nextElement(); 1523 Dimension dim = comp.getPreferredSize(); 1524 comp.setBounds(ileft, itop + centerHeight, dim.width, bottomHeight); 1525 ileft += dim.width; 1526 } 1527 1528 bottom.setBounds( 1529 ileft, 1530 itop + centerHeight, 1531 size.width - rightWidth - ileft - iright, 1532 bottomHeight); 1533 } 1534 1535 private Component center; 1537 private Component right; 1538 private Component bottom; 1539 private Vector leftOfScrollBar = new Vector (); 1540 } 1541 1542 static class CaretBlinker implements ActionListener { 1543 public void actionPerformed(ActionEvent evt) { 1544 if (focusedComponent != null && focusedComponent.hasFocus()) 1545 focusedComponent.blinkCaret(); 1546 } 1547 } 1548 1549 class MutableCaretEvent extends CaretEvent { 1550 MutableCaretEvent() { 1551 super(JEditTextArea.this); 1552 } 1553 1554 public int getDot() { 1555 return getCaretPosition(); 1556 } 1557 1558 public int getMark() { 1559 return getMarkPosition(); 1560 } 1561 } 1562 1563 class AdjustHandler implements AdjustmentListener { 1564 public void adjustmentValueChanged(final AdjustmentEvent evt) { 1565 if (!scrollBarsInitialized) 1566 return; 1567 1568 SwingUtilities.invokeLater(new Runnable () { 1572 public void run() { 1573 if (evt.getAdjustable() == vertical) 1574 setFirstLine(vertical.getValue()); 1575 else 1576 setHorizontalOffset(-horizontal.getValue()); 1577 } 1578 }); 1579 } 1580 } 1581 1582 class ComponentHandler extends ComponentAdapter { 1583 public void componentResized(ComponentEvent evt) { 1584 recalculateVisibleLines(); 1585 scrollBarsInitialized = true; 1586 } 1587 } 1588 1589 class DocumentHandler implements DocumentListener { 1590 public void insertUpdate(DocumentEvent evt) { 1591 documentChanged(evt); 1592 1593 int offset = evt.getOffset(); 1594 int length = evt.getLength(); 1595 1596 int newStart; 1597 int newEnd; 1598 1599 if (selectionStart > offset 1600 || (selectionStart == selectionEnd && selectionStart == offset)) 1601 newStart = selectionStart + length; 1602 else 1603 newStart = selectionStart; 1604 1605 if (selectionEnd >= offset) 1606 newEnd = selectionEnd + length; 1607 else 1608 newEnd = selectionEnd; 1609 1610 select(newStart, newEnd); 1611 } 1612 1613 public void removeUpdate(DocumentEvent evt) { 1614 documentChanged(evt); 1615 1616 int offset = evt.getOffset(); 1617 int length = evt.getLength(); 1618 1619 int newStart; 1620 int newEnd; 1621 1622 if (selectionStart > offset) { 1623 if (selectionStart > offset + length) 1624 newStart = selectionStart - length; 1625 else 1626 newStart = offset; 1627 } else 1628 newStart = selectionStart; 1629 1630 if (selectionEnd > offset) { 1631 if (selectionEnd > offset + length) 1632 newEnd = selectionEnd - length; 1633 else 1634 newEnd = offset; 1635 } else 1636 newEnd = selectionEnd; 1637 1638 select(newStart, newEnd); 1639 } 1640 1641 public void changedUpdate(DocumentEvent evt) { 1642 } 1643 } 1644 1645 class DragHandler implements MouseMotionListener { 1646 public void mouseDragged(MouseEvent evt) { 1647 if (popup != null && popup.isVisible()) 1648 return; 1649 1650 setSelectionRectangular((evt.getModifiers() & InputEvent.CTRL_MASK) != 0); 1651 select(getMarkPosition(), xyToOffset(evt.getX(), evt.getY())); 1652 } 1653 1654 public void mouseMoved(MouseEvent evt) { 1655 } 1656 } 1657 1658 class FocusHandler implements FocusListener { 1659 public void focusGained(FocusEvent evt) { 1660 setCaretVisible(true); 1661 focusedComponent = JEditTextArea.this; 1662 } 1663 1664 public void focusLost(FocusEvent evt) { 1665 setCaretVisible(false); 1666 focusedComponent = null; 1667 } 1668 } 1669 1670 class MouseHandler extends MouseAdapter { 1671 public void mousePressed(MouseEvent evt) { 1672 requestFocus(); 1673 1674 setCaretVisible(true); 1676 focusedComponent = JEditTextArea.this; 1677 1678 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0 && popup != null) { 1679 popup.show(painter, evt.getX(), evt.getY()); 1680 return; 1681 } 1682 1683 int line = yToLine(evt.getY()); 1684 int offset = xToOffset(line, evt.getX()); 1685 int dot = getLineStartOffset(line) + offset; 1686 1687 switch (evt.getClickCount()) { 1688 case 1 : 1689 doSingleClick(evt, line, offset, dot); 1690 break; 1691 case 2 : 1692 try { 1695 doDoubleClick(evt, line, offset, dot); 1696 } catch (BadLocationException bl) { 1697 bl.printStackTrace(); 1698 } 1699 break; 1700 case 3 : 1701 doTripleClick(evt, line, offset, dot); 1702 break; 1703 } 1704 } 1705 1706 private void doSingleClick(MouseEvent evt, int line, int offset, int dot) { 1707 if ((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) { 1708 rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0; 1709 select(getMarkPosition(), dot); 1710 } else 1711 setCaretPosition(dot); 1712 } 1713 1714 private void doDoubleClick(MouseEvent evt, int line, int offset, int dot) 1715 throws BadLocationException { 1716 if (getLineLength(line) == 0) 1718 return; 1719 1720 try { 1721 int bracket = TextUtilities.findMatchingBracket(document, Math.max(0, dot - 1)); 1722 if (bracket != -1) { 1723 int mark = getMarkPosition(); 1724 if (bracket > mark) { 1726 bracket++; 1727 mark--; 1728 } 1729 select(mark, bracket); 1730 return; 1731 } 1732 } catch (BadLocationException bl) { 1733 bl.printStackTrace(); 1734 } 1735 1736 String lineText = getLineText(line); 1738 char ch = lineText.charAt(Math.max(0, offset - 1)); 1739 1740 String noWordSep = (String ) document.getProperty("noWordSep"); 1741 if (noWordSep == null) 1742 noWordSep = ""; 1743 1744 boolean selectNoLetter = 1747 (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1); 1748 1749 int wordStart = 0; 1750 1751 for (int i = offset - 1; i >= 0; i--) { 1752 ch = lineText.charAt(i); 1753 if (selectNoLetter 1754 ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) { 1755 wordStart = i + 1; 1756 break; 1757 } 1758 } 1759 1760 int wordEnd = lineText.length(); 1761 for (int i = offset; i < lineText.length(); i++) { 1762 ch = lineText.charAt(i); 1763 if (selectNoLetter 1764 ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) { 1765 wordEnd = i; 1766 break; 1767 } 1768 } 1769 1770 int lineStart = getLineStartOffset(line); 1771 select(lineStart + wordStart, lineStart + wordEnd); 1772 1773 1782 } 1783 1784 private void doTripleClick(MouseEvent evt, int line, int offset, int dot) { 1785 select(getLineStartOffset(line), getLineEndOffset(line) - 1); 1786 } 1787 } 1788 1789 class CaretUndo extends AbstractUndoableEdit { 1790 private int start; 1791 private int end; 1792 1793 CaretUndo(int start, int end) { 1794 this.start = start; 1795 this.end = end; 1796 } 1797 1798 public boolean isSignificant() { 1799 return false; 1800 } 1801 1802 public String getPresentationName() { 1803 return "caret move"; 1804 } 1805 1806 public void undo() throws CannotUndoException { 1807 super.undo(); 1808 1809 select(start, end); 1810 } 1811 1812 public void redo() throws CannotRedoException { 1813 super.redo(); 1814 1815 select(start, end); 1816 } 1817 1818 public boolean addEdit(UndoableEdit edit) { 1819 if (edit instanceof CaretUndo) { 1820 CaretUndo cedit = (CaretUndo) edit; 1821 start = cedit.start; 1822 end = cedit.end; 1823 cedit.die(); 1824 1825 return true; 1826 } else 1827 return false; 1828 } 1829 } 1830 1831 class ScrollWheelHandler implements MouseWheelListener { 1832 1836 public void mouseWheelMoved(MouseWheelEvent e) { 1837 int line_to_scroll_to; 1838 int units = e.getUnitsToScroll(); 1839 1840 line_to_scroll_to = firstLine + units; 1841 if (units < 0) { 1842 line_to_scroll_to += electricScroll; 1843 if (line_to_scroll_to < 0) { 1844 line_to_scroll_to = 0; 1845 } 1846 } else if (units > 0) { 1847 line_to_scroll_to += visibleLines - 1 - electricScroll; 1848 if (line_to_scroll_to >= getLineCount()) { 1849 line_to_scroll_to = getLineCount() - 1; 1850 } 1851 } 1852 1853 scrollTo(line_to_scroll_to, 0); 1854 } 1855 } 1856 1857 static { 1858 caretTimer = new Timer(500, new CaretBlinker()); 1859 caretTimer.setInitialDelay(500); 1860 caretTimer.start(); 1861 } 1862} 1863 | Popular Tags |