1 19 20 package org.netbeans.editor; 21 22 import java.awt.Container ; 23 import java.awt.Dimension ; 24 import java.awt.Rectangle ; 25 import java.awt.Graphics ; 26 import java.awt.Color ; 27 import java.awt.Font ; 28 import java.awt.Point ; 29 import java.awt.Component ; 30 import java.awt.Toolkit ; 31 import java.awt.datatransfer.Clipboard ; 32 import java.awt.datatransfer.DataFlavor ; 33 import java.awt.datatransfer.Transferable ; 34 import java.awt.datatransfer.UnsupportedFlavorException ; 35 import java.awt.event.ComponentEvent ; 36 import java.awt.event.ComponentAdapter ; 37 import java.awt.event.ComponentListener ; 38 import java.awt.event.MouseListener ; 39 import java.awt.event.MouseEvent ; 40 import java.awt.event.MouseMotionListener ; 41 import java.awt.event.ActionListener ; 42 import java.awt.event.ActionEvent ; 43 import java.awt.event.FocusListener ; 44 import java.awt.event.FocusEvent ; 45 import java.awt.event.InputEvent ; 46 import java.beans.PropertyChangeListener ; 47 import java.beans.PropertyChangeEvent ; 48 import java.io.IOException ; 49 import javax.swing.Action ; 50 import javax.swing.JComponent ; 51 import javax.swing.JScrollBar ; 52 import javax.swing.JScrollPane ; 53 import javax.swing.JViewport ; 54 import javax.swing.Timer ; 55 import javax.swing.SwingUtilities ; 56 import javax.swing.TransferHandler ; 57 import javax.swing.text.BadLocationException ; 58 import javax.swing.text.Document ; 59 import javax.swing.text.JTextComponent ; 60 import javax.swing.text.Caret ; 61 import javax.swing.event.ChangeListener ; 62 import javax.swing.event.ChangeEvent ; 63 import javax.swing.event.DocumentListener ; 64 import javax.swing.event.DocumentEvent ; 65 import javax.swing.event.EventListenerList ; 66 import javax.swing.text.AbstractDocument ; 67 import javax.swing.text.Position ; 68 import org.netbeans.api.editor.fold.Fold; 69 import org.netbeans.api.editor.fold.FoldHierarchy; 70 import org.netbeans.api.editor.fold.FoldHierarchyEvent; 71 import org.netbeans.api.editor.fold.FoldHierarchyListener; 72 import org.netbeans.api.editor.fold.FoldStateChange; 73 import org.netbeans.api.editor.fold.FoldUtilities; 74 import org.netbeans.lib.editor.util.swing.DocumentListenerPriority; 75 import org.openide.util.WeakListeners; 76 77 83 84 public class BaseCaret implements Caret , 85 MouseListener , MouseMotionListener , PropertyChangeListener , 86 DocumentListener , ActionListener , SettingsChangeListener, 87 AtomicLockListener, FoldHierarchyListener { 88 89 90 public static final String BLOCK_CARET = "block-caret"; 92 93 public static final String LINE_CARET = "line-caret"; 95 96 public static final String THIN_LINE_CARET = "thin-line-caret"; 98 private static final boolean debugCaretFocus 99 = Boolean.getBoolean("netbeans.debug.editor.caret.focus"); 101 private static final boolean debugCaretFocusExtra 102 = Boolean.getBoolean("netbeans.debug.editor.caret.focus.extra"); 104 107 private ListenerImpl listenerImpl; 108 109 113 private Rectangle caretBounds; 114 115 116 protected JTextComponent component; 117 118 121 Point magicCaretPosition; 122 123 124 MarkFactory.ContextMark caretMark = new MarkFactory.ContextMark(Position.Bias.Forward, false); 125 126 127 MarkFactory.ContextMark selectionMark = new MarkFactory.ContextMark(Position.Bias.Forward, false); 128 129 130 boolean caretVisible; 131 132 135 boolean blinkVisible; 136 137 138 boolean selectionVisible; 139 140 141 protected EventListenerList listenerList = new EventListenerList (); 142 143 144 protected Timer flasher; 145 146 147 String type; 148 149 150 boolean italic; 151 152 private int xPoints[] = new int[4]; 153 private int yPoints[] = new int[4]; 154 private Action selectWordAction; 155 private Action selectLineAction; 156 157 158 protected ChangeEvent changeEvent; 159 160 161 protected char dotChar[] = {' '}; 162 163 private boolean overwriteMode; 164 165 169 private BaseDocument listenDoc; 170 171 174 protected Font afterCaretFont; 175 176 177 protected Font beforeCaretFont; 178 179 182 protected Color textForeColor; 183 184 187 protected Color textBackColor; 188 189 private transient FocusListener focusListener; 190 191 194 private transient boolean inAtomicLock; 195 196 199 private transient boolean modified; 200 201 202 private transient int undoOffset = -1; 203 204 static final long serialVersionUID =-9113841520331402768L; 205 206 private MouseEvent dndArmedEvent = null; 207 208 212 private boolean updateAfterFoldHierarchyChange; 213 214 public BaseCaret() { 215 listenerImpl = new ListenerImpl(); 216 Settings.addSettingsChangeListener(this); 217 } 218 219 222 public void settingsChange(SettingsChangeEvent evt) { 223 if( evt != null && SettingsNames.CARET_BLINK_RATE.equals( evt.getSettingName() ) ) { 224 225 JTextComponent c = component; 226 if (c == null) return; 227 if (evt.getKitClass() != Utilities.getKitClass(c)) return; 228 229 Object value = evt.getNewValue(); 230 if( value instanceof Integer ) { 231 setBlinkRate( ((Integer )value).intValue() ); 232 } 233 } 234 updateType(); 235 SwingUtilities.invokeLater(new Runnable () { 236 public void run() { 237 updateCaretBounds(); } 239 }); 240 } 241 242 void updateType() { 243 JTextComponent c = component; 244 if (c != null) { 245 Class kitClass = Utilities.getKitClass(c); 246 if (kitClass==null) return; 247 String newType; 248 boolean newItalic; 249 Color caretColor; 250 if (overwriteMode) { 251 newType = SettingsUtil.getString(kitClass, 252 SettingsNames.CARET_TYPE_OVERWRITE_MODE, LINE_CARET); 253 newItalic = SettingsUtil.getBoolean(kitClass, 254 SettingsNames.CARET_ITALIC_OVERWRITE_MODE, false); 255 caretColor = getColor( kitClass, SettingsNames.CARET_COLOR_OVERWRITE_MODE, 256 SettingsDefaults.defaultCaretColorOvwerwriteMode ); 257 258 } else { newType = SettingsUtil.getString(kitClass, 260 SettingsNames.CARET_TYPE_INSERT_MODE, LINE_CARET); 261 newItalic = SettingsUtil.getBoolean(kitClass, 262 SettingsNames.CARET_ITALIC_INSERT_MODE, false); 263 caretColor = getColor( kitClass, SettingsNames.CARET_COLOR_INSERT_MODE, 264 SettingsDefaults.defaultCaretColorInsertMode ); 265 } 266 267 this.type = newType; 268 this.italic = newItalic; 269 c.setCaretColor(caretColor); 270 if (debugCaretFocusExtra){ 271 System.err.println("Updating caret color:"+caretColor); } 273 274 resetBlink(); 275 } 276 } 277 278 private static Color getColor( Class kitClass, String settingName, 279 Color defaultValue) { 280 Object value = Settings.getValue(kitClass, settingName); 281 return (value instanceof Color ) ? (Color )value : defaultValue; 282 } 283 284 290 private boolean updateCaretBounds() { 291 JTextComponent c = component; 292 if (c != null) { 293 int offset = getDot(); 294 Rectangle newCaretBounds; 295 try { 296 newCaretBounds = c.getUI().modelToView( 297 c, offset, Position.Bias.Forward); 298 BaseDocument doc = Utilities.getDocument(c); 299 if (doc != null) { 300 doc.getChars(offset, this.dotChar, 0, 1); 301 } 302 } catch (BadLocationException e) { 303 newCaretBounds = null; 304 Utilities.annotateLoggable(e); 305 } 306 307 if (newCaretBounds != null) { 308 caretBounds = newCaretBounds; 309 return true; 310 } 311 } 312 return false; 313 } 314 315 316 public void install(JTextComponent c) { 317 assert (SwingUtilities.isEventDispatchThread()); component = c; 319 blinkVisible = true; 320 321 BaseDocument doc = Utilities.getDocument(c); 323 if (doc != null) { 324 modelChanged(null, doc); 325 } 326 327 updateCaretBounds(); 330 331 if (caretBounds == null) { 332 component.addComponentListener(listenerImpl); 335 } 336 337 component.addPropertyChangeListener(this); 338 component.addFocusListener(listenerImpl); 339 component.addMouseListener(this); 340 component.addMouseMotionListener(this); 341 342 EditorUI editorUI = Utilities.getEditorUI(component); 343 editorUI.addPropertyChangeListener( this ); 344 345 FoldHierarchy hierarchy = FoldHierarchy.get(c); 346 if (hierarchy != null) { 347 hierarchy.addFoldHierarchyListener(this); 348 } 349 350 if (component.hasFocus()) { 351 if (debugCaretFocus || debugCaretFocusExtra) { 352 System.err.println("Component has focus, calling BaseCaret.focusGained(); doc=" + component.getDocument().getProperty(Document.TitleProperty)); 354 } 355 listenerImpl.focusGained(null); } 357 358 dispatchUpdate(false); 359 } 360 361 362 public void deinstall(JTextComponent c) { 363 component = null; 365 synchronized (listenerImpl) { 366 if (flasher != null) { 367 setBlinkRate(0); 368 } 369 } 370 371 c.removeMouseMotionListener(this); 372 c.removeMouseListener(this); 373 c.removeFocusListener(listenerImpl); 374 c.removePropertyChangeListener(this); 375 376 FoldHierarchy hierarchy = FoldHierarchy.get(c); 377 if (hierarchy != null) { 378 hierarchy.removeFoldHierarchyListener(this); 379 } 380 381 modelChanged(listenDoc, null); 382 } 383 384 protected void modelChanged(BaseDocument oldDoc, BaseDocument newDoc) { 385 if (oldDoc != null) { 386 assert (oldDoc == listenDoc); 388 389 org.netbeans.lib.editor.util.swing.DocumentUtilities.removeDocumentListener( 390 oldDoc, this, DocumentListenerPriority.CARET_UPDATE); 391 oldDoc.removeAtomicLockListener(this); 392 393 try { 394 caretMark.remove(); 395 selectionMark.remove(); 396 } catch (InvalidMarkException e) { 397 Utilities.annotateLoggable(e); 398 } 399 400 listenDoc = null; 401 } 402 403 404 if (newDoc != null) { 405 406 org.netbeans.lib.editor.util.swing.DocumentUtilities.addDocumentListener( 407 newDoc, this, DocumentListenerPriority.CARET_UPDATE); 408 listenDoc = newDoc; 409 newDoc.addAtomicLockListener(this); 410 411 try { 412 Utilities.insertMark(newDoc, caretMark, 0); 413 Utilities.insertMark(newDoc, selectionMark, 0); 414 } catch (InvalidMarkException e) { 415 Utilities.annotateLoggable(e); 416 } catch (BadLocationException e) { 417 Utilities.annotateLoggable(e); 418 } 419 420 settingsChange(null); 422 Utilities.runInEventDispatchThread( 423 new Runnable () { 424 public void run() { 425 updateType(); 426 } 427 } 428 ); 429 430 } 431 } 432 433 434 public void paint(Graphics g) { 435 JTextComponent c = component; 436 if (c == null) return; 437 EditorUI editorUI = Utilities.getEditorUI(c); 438 439 if (getDot() != 0 && caretBounds == null) { 443 update(true); 444 } 445 if (caretBounds != null && isVisible() && blinkVisible) { 446 paintCustomCaret(g); 447 } 448 } 449 450 protected void paintCustomCaret(Graphics g) { 451 JTextComponent c = component; 452 if (c != null) { 453 EditorUI editorUI = Utilities.getEditorUI(c); 454 g.setColor(c.getCaretColor()); 455 if (THIN_LINE_CARET.equals(type)) { int upperX = caretBounds.x; 457 if (beforeCaretFont != null && beforeCaretFont.isItalic() && italic) { 458 upperX += Math.tan(beforeCaretFont.getItalicAngle()) * caretBounds.height; 459 } 460 g.drawLine((int)upperX, caretBounds.y, caretBounds.x, 461 (caretBounds.y + caretBounds.height - 1)); 462 463 } else if (BLOCK_CARET.equals(type)) { if (afterCaretFont != null) g.setFont(afterCaretFont); 465 if (afterCaretFont != null && afterCaretFont.isItalic() && italic) { int upperX = (int)(caretBounds.x 467 + Math.tan(afterCaretFont.getItalicAngle()) * caretBounds.height); 468 xPoints[0] = upperX; 469 yPoints[0] = caretBounds.y; 470 xPoints[1] = upperX + caretBounds.width; 471 yPoints[1] = caretBounds.y; 472 xPoints[2] = caretBounds.x + caretBounds.width; 473 yPoints[2] = caretBounds.y + caretBounds.height - 1; 474 xPoints[3] = caretBounds.x; 475 yPoints[3] = caretBounds.y + caretBounds.height - 1; 476 g.fillPolygon(xPoints, yPoints, 4); 477 478 } else { g.fillRect(caretBounds.x, caretBounds.y, caretBounds.width, caretBounds.height); 480 } 481 482 if (!Character.isWhitespace(dotChar[0])) { 483 g.setColor(Color.white); 484 g.drawChars(dotChar, 0, 1, caretBounds.x, 486 caretBounds.y + editorUI.getLineAscent()); 487 } 488 489 } else { int blkWidth = 2; 491 if (beforeCaretFont != null && beforeCaretFont.isItalic() && italic) { 492 int upperX = (int)(caretBounds.x 493 + Math.tan(beforeCaretFont.getItalicAngle()) * caretBounds.height); 494 xPoints[0] = upperX; 495 yPoints[0] = caretBounds.y; 496 xPoints[1] = upperX + blkWidth; 497 yPoints[1] = caretBounds.y; 498 xPoints[2] = caretBounds.x + blkWidth; 499 yPoints[2] = caretBounds.y + caretBounds.height - 1; 500 xPoints[3] = caretBounds.x; 501 yPoints[3] = caretBounds.y + caretBounds.height - 1; 502 g.fillPolygon(xPoints, yPoints, 4); 503 504 } else { g.fillRect(caretBounds.x, caretBounds.y, blkWidth, caretBounds.height - 1); 506 } 507 } 508 } 509 } 510 511 512 void dispatchUpdate(final boolean scrollViewToCaret) { 513 522 Utilities.runInEventDispatchThread( 523 new Runnable () { 524 public void run() { 525 JTextComponent c = component; 526 if (c != null) { 527 BaseDocument doc = Utilities.getDocument(c); 528 if (doc != null) { 529 doc.readLock(); 530 try { 531 update(scrollViewToCaret); 532 } finally { 533 doc.readUnlock(); 534 } 535 } 536 } 537 } 538 } 539 ); 540 } 541 542 550 protected void update(boolean scrollViewToCaret) { 551 JTextComponent c = component; 552 if (c != null) { 553 BaseTextUI ui = (BaseTextUI)c.getUI(); 554 EditorUI editorUI = ui.getEditorUI(); 555 BaseDocument doc = Utilities.getDocument(c); 556 if (doc != null) { 557 Rectangle oldCaretBounds = caretBounds; if (oldCaretBounds != null) { 559 if (italic) { oldCaretBounds.width += oldCaretBounds.height; 561 } 562 c.repaint(oldCaretBounds); 563 } 564 565 int dot = getDot(); 566 567 if (updateCaretBounds() && (scrollViewToCaret || updateAfterFoldHierarchyChange)) { 568 Rectangle scrollBounds = new Rectangle (caretBounds); 569 570 if (oldCaretBounds == null) { 578 Component viewport = c.getParent(); 579 if (viewport instanceof JViewport ) { 580 Component scrollPane = viewport.getParent(); 581 if (scrollPane instanceof JScrollPane ) { 582 JScrollBar hScrollBar = ((JScrollPane )scrollPane).getHorizontalScrollBar(); 583 if (hScrollBar != null) { 584 int hScrollBarHeight = hScrollBar.getPreferredSize().height; 585 Dimension extentSize = ((JViewport )viewport).getExtentSize(); 586 if (extentSize.height >= caretBounds.height + hScrollBarHeight) { 589 scrollBounds.height += hScrollBarHeight; 590 } 591 } 592 } 593 } 594 } 595 596 Rectangle visibleBounds = c.getVisibleRect(); 597 598 if (oldCaretBounds != null && updateAfterFoldHierarchyChange) { 602 int oldRelY = oldCaretBounds.y - visibleBounds.y; 603 if (oldRelY < visibleBounds.height) { 605 scrollBounds.y = Math.max(caretBounds.y - oldRelY, 0); 606 scrollBounds.height = visibleBounds.height; 607 } 608 } 609 610 if (!updateAfterFoldHierarchyChange && 620 (caretBounds.y > visibleBounds.y + visibleBounds.height + caretBounds.height 621 || caretBounds.y + caretBounds.height < visibleBounds.y - caretBounds.height) 622 ) { 623 scrollBounds.y -= (visibleBounds.height - caretBounds.height) / 2; 625 scrollBounds.height = visibleBounds.height; 626 } 627 628 updateAfterFoldHierarchyChange = false; 629 c.scrollRectToVisible(scrollBounds); 632 633 resetBlink(); 634 635 c.repaint(caretBounds); 636 } 637 } 638 } 639 } 640 641 private void updateSystemSelection() { 642 if (getDot() != getMark() && component != null) { 643 Clipboard clip = getSystemSelection(); 644 645 if (clip != null) { 646 clip.setContents(new java.awt.datatransfer.StringSelection (component.getSelectedText()), null); 647 } 648 } 649 } 650 651 private Clipboard getSystemSelection() { 652 return component.getToolkit().getSystemSelection(); 653 } 654 655 private void updateJumpList() { 656 JumpList.dotMoved(component, getDot()); 657 } 658 659 662 public boolean equals(Object o) { 663 return (this == o); 664 } 665 666 667 public void addChangeListener(ChangeListener l) { 668 listenerList.add(ChangeListener .class, l); 669 } 670 671 672 public void removeChangeListener(ChangeListener l) { 673 listenerList.remove(ChangeListener .class, l); 674 } 675 676 677 protected void fireStateChanged() { 678 Utilities.runInEventDispatchThread(new Runnable () { 680 public void run() { 681 Object listeners[] = listenerList.getListenerList(); 682 for (int i = listeners.length - 2; i >= 0 ; i -= 2) { 683 if (listeners[i] == ChangeListener .class) { 684 if (changeEvent == null) { 685 changeEvent = new ChangeEvent (BaseCaret.this); 686 } 687 ((ChangeListener )listeners[i + 1]).stateChanged(changeEvent); 688 } 689 } 690 } 691 }); 692 updateSystemSelection(); 693 updateJumpList(); 694 } 695 696 702 public final boolean isVisible() { 703 return caretVisible; 704 } 705 706 protected void setVisibleImpl(boolean v) { 707 boolean visible = isVisible(); 708 synchronized (this) { 709 synchronized (listenerImpl) { 710 if (flasher != null) { 711 if (visible) { 712 flasher.stop(); 713 } 714 if (v) { 715 if (debugCaretFocusExtra){ 716 System.err.println("starting the caret blinking timer: visible=" + visible + ", blinkVisible=" + blinkVisible); } 719 flasher.start(); 720 721 } else { 722 if (debugCaretFocusExtra){ 723 System.err.println("stopping the caret blinking timer"); } 725 flasher.stop(); 726 } 727 } 728 } 729 730 caretVisible = v; 731 } 732 JTextComponent c = component; 733 if (c != null && caretBounds != null) { 734 Rectangle repaintRect = caretBounds; 735 if (italic) { 736 repaintRect = new Rectangle (repaintRect); repaintRect.width += repaintRect.height; } 739 c.repaint(repaintRect); 740 } 741 } 742 743 synchronized void resetBlink() { 744 boolean visible = isVisible(); 745 synchronized (listenerImpl) { 746 if (flasher != null) { 747 flasher.stop(); 748 blinkVisible = true; 749 if (visible) { 750 if (debugCaretFocusExtra){ 751 System.err.println("Reset blinking (caret already visible)" + " - starting the caret blinking timer: visible=" + visible + ", blinkVisible=" + blinkVisible ); 755 } 756 flasher.start(); 757 } else { 758 if (debugCaretFocusExtra){ 759 System.err.println("Reset blinking (caret not visible)" + " - caret blinking timer not started: visible=" + visible + ", blinkVisible=" + blinkVisible ); 763 } 764 } 765 } 766 } 767 } 768 769 770 public void setVisible(final boolean v) { 771 Utilities.runInEventDispatchThread( 772 new Runnable () { 773 public void run() { 774 setVisibleImpl(v); 775 } 776 } 777 ); 778 } 779 780 781 public final boolean isSelectionVisible() { 782 return selectionVisible; 783 } 784 785 786 public void setSelectionVisible(boolean v) { 787 if (selectionVisible == v) { 788 return; 789 } 790 JTextComponent c = component; 791 if (c != null) { 792 selectionVisible = v; 793 794 BaseTextUI ui = (BaseTextUI)c.getUI(); 796 try { 797 ui.getEditorUI().repaintBlock(caretMark.getOffset(), selectionMark.getOffset()); 798 } catch (BadLocationException e) { 799 Utilities.annotateLoggable(e); 800 } catch (InvalidMarkException e) { 801 Utilities.annotateLoggable(e); 802 } 803 804 } 805 } 806 807 813 public void setMagicCaretPosition(Point p) { 814 magicCaretPosition = p; 815 } 816 817 818 public final Point getMagicCaretPosition() { 819 return magicCaretPosition; 820 } 821 822 825 public synchronized void setBlinkRate(int rate) { 826 synchronized (listenerImpl) { 827 if (flasher == null && rate > 0) { 828 flasher = new Timer (rate, new WeakTimerListener(this)); 829 } 830 if (flasher != null) { 831 if (rate > 0) { 832 if (flasher.getDelay() != rate) { 833 if (debugCaretFocusExtra){ 834 System.err.println("blink rate:"+rate); } 836 flasher.setDelay(rate); 837 } 838 } else { if (debugCaretFocusExtra){ 840 System.err.println("zero rate - don't blink"); System.err.println("setting blinkVisible to true and disabling timer"); } 843 flasher.stop(); 844 flasher.removeActionListener(this); 845 flasher = null; 846 blinkVisible = true; 847 } 848 } 849 } 850 } 851 852 853 public synchronized int getBlinkRate() { 854 synchronized (listenerImpl) { 855 return (flasher != null) ? flasher.getDelay() : 0; 856 } 857 } 858 859 860 public int getDot() { 861 if (component != null) { 862 try { 863 return caretMark.getOffset(); 864 } catch (InvalidMarkException e) { 865 } 866 } 867 return 0; 868 } 869 870 874 public int getMark() { 875 if (component != null) { 876 if (selectionVisible) { 877 try { 878 return selectionMark.getOffset(); 879 } catch (InvalidMarkException e) { 880 } 881 } else { return getDot(); } 884 } 885 return 0; 886 } 887 888 893 public void setDot(int offset) { 894 setDot(offset, caretBounds, EditorUI.SCROLL_DEFAULT); 905 } 906 907 public void setDot(int offset, boolean expandFold) { 908 setDot(offset, caretBounds, EditorUI.SCROLL_DEFAULT, expandFold); 909 } 910 911 912 928 929 public void setDot(int offset, Rectangle scrollRect, int scrollPolicy, boolean expandFold) { 930 JTextComponent c = component; 931 if (c != null) { 932 setSelectionVisible(false); 933 BaseDocument doc = (BaseDocument)c.getDocument(); 934 boolean dotChanged = false; 935 doc.readLock(); 936 try { 937 if (doc != null && offset >= 0 && offset <= doc.getLength()) { 938 dotChanged = true; 939 try { 940 Utilities.moveMark(doc, caretMark, offset); 941 FoldHierarchy hierarchy = FoldHierarchy.get(c); 943 hierarchy.lock(); 944 try { 945 Fold collapsed = null; 946 while (expandFold && (collapsed = FoldUtilities.findCollapsedFold(hierarchy, offset, offset)) != null && collapsed.getStartOffset() < offset && 947 collapsed.getEndOffset() > offset) { 948 hierarchy.expand(collapsed); 949 } 950 } finally { 951 hierarchy.unlock(); 952 } 953 } catch (BadLocationException e) { 954 throw new IllegalStateException (e.toString()); 955 } catch (InvalidMarkException e) { 957 throw new IllegalStateException (e.toString()); 958 } 960 } 961 } finally { 962 doc.readUnlock(); 963 } 964 965 if (dotChanged) { 966 fireStateChanged(); 967 dispatchUpdate(true); 968 } 969 } 970 } 971 972 986 public void setDot(int offset, Rectangle scrollRect, int scrollPolicy) { 987 setDot(offset, scrollRect, scrollPolicy, true); 988 } 989 990 public void moveDot(int offset) { 991 moveDot(offset, caretBounds, EditorUI.SCROLL_MOVE); 992 } 993 994 1007 public void moveDot(int offset, Rectangle scrollRect, int scrollPolicy) { 1008 JTextComponent c = component; 1009 if (c != null) { 1010 BaseDocument doc = (BaseDocument)c.getDocument(); 1011 if (doc != null && offset >= 0 && offset <= doc.getLength()) { 1012 try { 1013 int oldCaretPos = getDot(); 1014 if (offset == oldCaretPos) { return; 1016 } 1017 int selPos; 1019 if (selectionVisible) { 1020 selPos = selectionMark.getOffset(); 1021 } else { 1022 Utilities.moveMark(doc, selectionMark, oldCaretPos); 1023 selPos = oldCaretPos; 1024 } 1025 1026 Utilities.moveMark(doc, caretMark, offset); 1027 if (selectionVisible) { Utilities.getEditorUI(c).repaintBlock(oldCaretPos, offset); 1029 if (selPos == offset) { setSelectionVisible(false); 1031 } 1032 1033 } else { setSelectionVisible(true); 1035 } 1036 } catch (BadLocationException e) { 1037 throw new IllegalStateException (e.toString()); 1038 } catch (InvalidMarkException e) { 1040 throw new IllegalStateException (e.toString()); 1041 } 1042 } 1043 fireStateChanged(); 1044 dispatchUpdate(true); 1045 } 1046 } 1047 1048 public void insertUpdate(DocumentEvent evt) { 1050 JTextComponent c = component; 1051 if (c != null) { 1052 BaseDocument doc = (BaseDocument)component.getDocument(); 1053 BaseDocumentEvent bevt = (BaseDocumentEvent)evt; 1054 if ((bevt.isInUndo() || bevt.isInRedo()) 1055 && component == Utilities.getLastActiveComponent() 1056 ) { 1057 undoOffset = evt.getOffset() + evt.getLength(); 1059 } else { 1060 undoOffset = -1; 1061 } 1062 1063 modified = true; 1064 1065 modifiedUpdate(); 1066 } 1067 } 1068 1069 public void removeUpdate(DocumentEvent evt) { 1070 JTextComponent c = component; 1071 if (c != null) { 1072 BaseDocument doc = (BaseDocument)c.getDocument(); 1073 if (selectionVisible && (getDot() == getMark())) { 1075 setSelectionVisible(false); 1076 } 1077 1078 BaseDocumentEvent bevt = (BaseDocumentEvent)evt; 1079 if ((bevt.isInUndo() || bevt.isInRedo()) 1080 && c == Utilities.getLastActiveComponent() 1081 ) { 1082 undoOffset = evt.getOffset(); 1084 } else { 1085 undoOffset = -1; 1086 } 1087 1088 modified = true; 1089 1090 modifiedUpdate(); 1091 } 1092 } 1093 1094 private void modifiedUpdate() { 1095 if (!inAtomicLock) { 1096 JTextComponent c = component; 1097 if (modified && c != null) { 1098 if (undoOffset >= 0) { setDot(undoOffset); 1100 } else { fireStateChanged(); 1102 dispatchUpdate(c.hasFocus()); 1104 } 1105 modified = false; 1106 } 1107 } 1108 } 1109 1110 public void atomicLock(AtomicLockEvent evt) { 1111 inAtomicLock = true; 1112 } 1113 1114 public void atomicUnlock(AtomicLockEvent evt) { 1115 inAtomicLock = false; 1116 modifiedUpdate(); 1117 } 1118 1119 public void changedUpdate(DocumentEvent evt) { 1120 } 1122 1123 public void mouseClicked(MouseEvent evt) { 1125 JTextComponent c = component; 1126 if (c != null) { 1127 if (SwingUtilities.isLeftMouseButton(evt)) { 1128 if (evt.getClickCount() == 2) { 1129 BaseTextUI ui = (BaseTextUI)c.getUI(); 1130 int offset = ui.viewToModel(c, 1132 evt.getX(), evt.getY()); 1133 FoldHierarchy hierarchy = FoldHierarchy.get(c); 1134 Document doc = c.getDocument(); 1135 if (doc instanceof AbstractDocument ) { 1136 AbstractDocument adoc = (AbstractDocument )doc; 1137 adoc.readLock(); 1138 try { 1139 hierarchy.lock(); 1140 try { 1141 Fold collapsed = FoldUtilities.findCollapsedFold( 1142 hierarchy, offset, offset); 1143 if (collapsed != null && collapsed.getStartOffset() <= offset && 1144 collapsed.getEndOffset() >= offset) { 1145 hierarchy.expand(collapsed); 1146 } else { 1147 if (selectWordAction == null) { 1148 selectWordAction = ((BaseKit)ui.getEditorKit( 1149 c)).getActionByName(BaseKit.selectWordAction); 1150 } 1151 selectWordAction.actionPerformed(null); 1152 } 1153 } finally { 1154 hierarchy.unlock(); 1155 } 1156 } finally { 1157 adoc.readUnlock(); 1158 } 1159 } 1160 } else if (evt.getClickCount() == 3) { 1161 if (selectLineAction == null) { 1162 BaseTextUI ui = (BaseTextUI)c.getUI(); 1163 selectLineAction = ((BaseKit)ui.getEditorKit( 1164 c)).getActionByName(BaseKit.selectLineAction); 1165 } 1166 selectLineAction.actionPerformed(null); 1167 } 1168 } else if (SwingUtilities.isMiddleMouseButton(evt)){ 1169 if (evt.getClickCount() == 1) { 1170 if (c == null) return; 1171 Toolkit tk = c.getToolkit(); 1172 Clipboard buffer = getSystemSelection(); 1173 1174 if (buffer == null) return; 1175 1176 Transferable trans = buffer.getContents(null); 1177 if (trans == null) return; 1178 1179 BaseDocument doc = (BaseDocument)c.getDocument(); 1180 if (doc == null) return; 1181 1182 int offset = ((BaseTextUI)c.getUI()).viewToModel(c, 1183 evt.getX(), evt.getY()); 1184 1185 try{ 1186 String pastingString = (String )trans.getTransferData(DataFlavor.stringFlavor); 1187 if (pastingString == null) return; 1188 try { 1189 doc.atomicLock(); 1190 try { 1191 doc.insertString(offset, pastingString, null); 1192 setDot(offset+pastingString.length()); 1193 } finally { 1194 doc.atomicUnlock(); 1195 } 1196 } catch( BadLocationException exc ) { 1197 } 1198 }catch(UnsupportedFlavorException ufe){ 1199 }catch(IOException ioe){ 1200 } 1201 } 1202 } 1203 } 1204 } 1205 1206 private void mousePressedImpl(MouseEvent evt){ 1207 JTextComponent c = component; 1208 if (c != null) { 1209 Utilities.getEditorUI(c).getWordMatch().clear(); 1211 if ((SwingUtilities.isLeftMouseButton(evt) && 1213 !(evt.isPopupTrigger()) && 1214 (evt.getModifiers() & (InputEvent.META_MASK|InputEvent.ALT_MASK)) == 0) || 1215 !isSelectionVisible()) { 1216 int offset = ((BaseTextUI)c.getUI()).viewToModel(c, 1217 evt.getX(), evt.getY()); 1218 if (offset >= 0) { 1219 if ((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) { 1220 moveDot(offset); 1221 } else { 1222 setDot(offset); 1223 } 1224 setMagicCaretPosition(null); 1225 } 1226 if (c.isEnabled()) { 1227 c.requestFocus(); 1228 } 1229 } 1230 } 1231 } 1232 1233 public void mousePressed(MouseEvent evt) { 1234 dndArmedEvent = null; 1235 1236 if (isDragPossible(evt) && mapDragOperationFromModifiers(evt) != TransferHandler.NONE) { 1237 dndArmedEvent = evt; 1238 evt.consume(); 1239 return; 1240 } 1241 1242 mousePressedImpl(evt); 1243 } 1244 1245 public void mouseReleased(MouseEvent evt) { 1246 if (dndArmedEvent != null){ 1247 mousePressedImpl(evt); 1248 } 1249 dndArmedEvent = null; 1250 } 1251 1252 public void mouseEntered(MouseEvent evt) { 1253 } 1254 1255 public void mouseExited(MouseEvent evt) { 1256 } 1257 1258 1259 protected int mapDragOperationFromModifiers(MouseEvent e) { 1260 int mods = e.getModifiersEx(); 1261 1262 if ((mods & InputEvent.BUTTON1_DOWN_MASK) != InputEvent.BUTTON1_DOWN_MASK) { 1263 return TransferHandler.NONE; 1264 } 1265 1266 return TransferHandler.COPY_OR_MOVE; 1267 } 1268 1279 protected boolean isDragPossible(MouseEvent e) { 1280 JComponent comp = getEventComponent(e); 1281 boolean possible = (comp == null) ? true : (comp.getTransferHandler() != null); 1282 if (possible){ 1283 JTextComponent c = (JTextComponent ) getEventComponent(e); 1284 if (c.getDragEnabled()) { 1285 Caret caret = c.getCaret(); 1286 int dot = caret.getDot(); 1287 int mark = caret.getMark(); 1288 if (dot != mark) { 1289 Point p = new Point (e.getX(), e.getY()); 1290 int pos = c.viewToModel(p); 1291 1292 int p0 = Math.min(dot, mark); 1293 int p1 = Math.max(dot, mark); 1294 if ((pos >= p0) && (pos < p1)) { 1295 return true; 1296 } 1297 } 1298 } 1299 } 1300 return false; 1301 } 1302 1303 protected JComponent getEventComponent(MouseEvent e) { 1304 Object src = e.getSource(); 1305 if (src instanceof JComponent ) { 1306 JComponent c = (JComponent ) src; 1307 return c; 1308 } 1309 return null; 1310 } 1311 1312 public void mouseDragged(MouseEvent evt) { 1314 if (dndArmedEvent != null){ 1315 evt.consume(); 1316 return; 1317 } 1318 1319 JTextComponent c = component; 1320 1321 if (SwingUtilities.isLeftMouseButton(evt)) { 1322 if (c != null) { 1323 int offset = ((BaseTextUI)c.getUI()).viewToModel(c, 1324 evt.getX(), evt.getY()); 1325 if (offset == -1) 1327 offset = 0; 1328 if (offset >= 0 && (evt.getModifiers() & InputEvent.SHIFT_MASK) == 0) 1330 moveDot(offset); 1331 } 1332 } 1333 } 1334 1335 public void mouseMoved(MouseEvent evt) { 1336 } 1337 1338 public void propertyChange(PropertyChangeEvent evt) { 1340 String propName = evt.getPropertyName(); 1341 1342 if ("document".equals(propName)) { BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument) 1344 ? (BaseDocument)evt.getNewValue() : null; 1345 modelChanged(listenDoc, newDoc); 1346 1347 } else if (EditorUI.OVERWRITE_MODE_PROPERTY.equals(propName)) { 1348 Boolean b = (Boolean )evt.getNewValue(); 1349 overwriteMode = (b != null) ? b.booleanValue() : false; 1350 updateType(); 1351 1352 } else if ("ancestor".equals(propName) && evt.getSource() == component) { Container parent = component.getParent(); 1363 if (parent instanceof JViewport ) { 1364 parent = parent.getParent(); if (parent instanceof JScrollPane ) { 1366 JScrollPane scrollPane = (JScrollPane )parent; 1367 JScrollBar hScrollBar = scrollPane.getHorizontalScrollBar(); 1368 if (hScrollBar != null) { 1369 hScrollBar.addComponentListener( 1372 (ComponentListener )WeakListeners.create( 1373 ComponentListener .class, listenerImpl, hScrollBar)); 1374 } 1375 } 1376 } 1377 } 1378 } 1379 1380 1382 public void actionPerformed(ActionEvent evt) { 1383 JTextComponent c = component; 1384 if (c != null) { 1385 blinkVisible = !blinkVisible; 1386 if (caretBounds != null) { 1387 Rectangle repaintRect = caretBounds; 1388 if (italic) { 1389 repaintRect = new Rectangle (repaintRect); repaintRect.width += repaintRect.height; 1391 } 1392 c.repaint(repaintRect); 1393 } 1394 } 1395 } 1396 1397 public void foldHierarchyChanged(FoldHierarchyEvent evt) { 1398 int caretOffset = getDot(); 1399 int addedFoldCnt = evt.getAddedFoldCount(); 1400 if (addedFoldCnt > 0) { 1401 FoldHierarchy hierarchy = (FoldHierarchy)evt.getSource(); 1402 Fold collapsed = null; 1403 while ((collapsed = FoldUtilities.findCollapsedFold(hierarchy, caretOffset, caretOffset)) != null && collapsed.getStartOffset() < caretOffset && 1404 collapsed.getEndOffset() > caretOffset) { 1405 hierarchy.expand(collapsed); 1406 } 1407 } else { 1408 int endOffset = -1; 1409 if (evt.getAffectedStartOffset() <= caretOffset && evt.getAffectedEndOffset() >= caretOffset) { 1411 for (int i = 0; i < evt.getFoldStateChangeCount(); i++) { 1412 FoldStateChange change = evt.getFoldStateChange(i); 1413 if (change.isCollapsedChanged()) { 1414 Fold fold = change.getFold(); 1415 if (fold.isCollapsed() && fold.getStartOffset() <= caretOffset && fold.getEndOffset() >= caretOffset) { 1416 if (fold.getEndOffset() > endOffset) 1417 endOffset = fold.getEndOffset(); 1418 } 1419 } 1420 } 1421 if (endOffset > -1) 1422 setDot(endOffset, false); 1423 } 1424 } 1425 updateAfterFoldHierarchyChange = true; 1427 dispatchUpdate(false); } 1429 1430 private class ListenerImpl extends ComponentAdapter 1431 implements FocusListener { 1432 1433 ListenerImpl() { 1434 } 1435 1436 public void focusGained(FocusEvent evt) { 1438 if (debugCaretFocus || debugCaretFocusExtra) { 1439 System.err.println( 1440 (debugCaretFocusExtra ? "\n" : "") + "BaseCaret.focusGained(); doc=" + component.getDocument().getProperty(Document.TitleProperty) 1443 ); 1444 } 1445 1446 JTextComponent c = component; 1447 if (c != null) { 1448 updateType(); 1449 if (debugCaretFocusExtra) { 1450 System.err.println("going to set caret visible to: "+c.isEnabled()); } 1452 setVisible(c.isEnabled()); } else { 1454 if (debugCaretFocusExtra) { 1455 System.err.println("component is null, caret will not be dislayed"); } 1457 } 1458 } 1459 1460 public void focusLost(FocusEvent evt) { 1461 if (debugCaretFocus || debugCaretFocusExtra) { 1462 System.err.println((debugCaretFocusExtra ? "\n" : "") + "BaseCaret.focusLost(); doc=" + component.getDocument().getProperty(Document.TitleProperty) 1465 + "\nFOCUS GAINER: " + evt.getOppositeComponent() ); 1467 if (debugCaretFocusExtra) { 1468 System.err.println("FOCUS EVENT: " + evt); } 1470 } 1471 setVisible(false); 1472 } 1473 1474 1478 public void componentShown(ComponentEvent e) { 1479 Component hScrollBar = e.getComponent(); 1484 if (hScrollBar != component) { Component scrollPane = hScrollBar.getParent(); 1486 if (caretBounds != null && scrollPane instanceof JScrollPane ) { 1487 Rectangle viewRect = ((JScrollPane )scrollPane).getViewport().getViewRect(); 1488 Rectangle hScrollBarRect = new Rectangle ( 1489 viewRect.x, 1490 viewRect.y + viewRect.height, 1491 hScrollBar.getWidth(), 1492 hScrollBar.getHeight() 1493 ); 1494 if (hScrollBarRect.intersects(caretBounds)) { 1495 dispatchUpdate(true); } 1498 } 1499 } 1500 } 1501 1502 1503 1506 public void componentResized(ComponentEvent e) { 1507 Component c = e.getComponent(); 1508 if (c == component) { if (caretBounds == null) { 1513 dispatchUpdate(true); 1514 if (caretBounds != null) { c.removeComponentListener(this); 1516 } 1517 } 1518 } 1519 } 1520 1521 } 1522 1523} 1524 | Popular Tags |