GNU Classpath (0.19) | ||
Frames | No Frames |
1: /* DefaultCaret.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing.text; 39: 40: import java.awt.Graphics; 41: import java.awt.Point; 42: import java.awt.Rectangle; 43: import java.awt.event.FocusEvent; 44: import java.awt.event.FocusListener; 45: import java.awt.event.MouseEvent; 46: import java.awt.event.MouseListener; 47: import java.awt.event.MouseMotionListener; 48: import java.beans.PropertyChangeEvent; 49: import java.beans.PropertyChangeListener; 50: import java.util.EventListener; 51: 52: import javax.swing.SwingUtilities; 53: import javax.swing.event.ChangeEvent; 54: import javax.swing.event.ChangeListener; 55: import javax.swing.event.DocumentEvent; 56: import javax.swing.event.DocumentListener; 57: import javax.swing.event.EventListenerList; 58: 59: /** 60: * The default implementation of the {@link Caret} interface. 61: * 62: * @author orgininal author unknown 63: * @author Roman Kennke (roman@kennke.org) 64: */ 65: public class DefaultCaret extends Rectangle 66: implements Caret, FocusListener, MouseListener, MouseMotionListener 67: { 68: /** 69: * Listens for changes in the text component's document and updates the 70: * caret accordingly. 71: * 72: * @author Roman Kennke (kennke@aicas.com) 73: */ 74: private class DocumentHandler implements DocumentListener 75: { 76: /** 77: * Receives notification that some text attributes have changed. No action 78: * is taken here. 79: * 80: * @param event the document event 81: */ 82: public void changedUpdate(DocumentEvent event) 83: { 84: // Nothing to do here. 85: } 86: 87: /** 88: * Receives notification that some text has been inserted from the text 89: * component. The caret is moved forward accordingly. 90: * 91: * @param event the document event 92: */ 93: public void insertUpdate(DocumentEvent event) 94: { 95: if (policy == ALWAYS_UPDATE || 96: (SwingUtilities.isEventDispatchThread() && 97: policy == UPDATE_WHEN_ON_EDT)) 98: { 99: int dot = getDot(); 100: setDot(dot + event.getLength()); 101: } 102: } 103: 104: /** 105: * Receives notification that some text has been removed into the text 106: * component. The caret is moved backwards accordingly. 107: * 108: * @param event the document event 109: */ 110: public void removeUpdate(DocumentEvent event) 111: { 112: if (policy == ALWAYS_UPDATE || 113: (SwingUtilities.isEventDispatchThread() && 114: policy == UPDATE_WHEN_ON_EDT)) 115: { 116: int dot = getDot(); 117: setDot(dot - event.getLength()); 118: } 119: else if (policy == NEVER_UPDATE) 120: { 121: int docLength = event.getDocument().getLength(); 122: if (getDot() > docLength) 123: setDot(docLength); 124: } 125: } 126: } 127: 128: /** 129: * Listens for property changes on the text document. This is used to add and 130: * remove our document listener, if the document of the text component has 131: * changed. 132: * 133: * @author Roman Kennke (kennke@aicas.com) 134: */ 135: private class PropertyChangeHandler implements PropertyChangeListener 136: { 137: 138: /** 139: * Receives notification when a property has changed on the text component. 140: * This adds/removes our document listener from the text component's 141: * document when the document changes. 142: * 143: * @param e the property change event 144: */ 145: public void propertyChange(PropertyChangeEvent e) 146: { 147: if (e.getPropertyName().equals("document")) 148: { 149: Document oldDoc = (Document) e.getOldValue(); 150: oldDoc.removeDocumentListener(documentListener); 151: Document newDoc = (Document) e.getNewValue(); 152: newDoc.addDocumentListener(documentListener); 153: } 154: } 155: 156: } 157: 158: /** The serialization UID (compatible with JDK1.5). */ 159: private static final long serialVersionUID = 4325555698756477346L; 160: 161: /** 162: * Indicates the Caret position should always be updated after Document 163: * changes even if the updates are not performed on the Event Dispatching 164: * thread. 165: * 166: * @since 1.5 167: */ 168: public static final int ALWAYS_UPDATE = 2; 169: 170: /** 171: * Indicates the Caret position should not be changed unless the Document 172: * length becomes less than the Caret position, in which case the Caret 173: * is moved to the end of the Document. 174: * 175: * @since 1.5 176: */ 177: public static final int NEVER_UPDATE = 1; 178: 179: /** 180: * Indicates the Caret position should be updated only if Document changes 181: * are made on the Event Dispatcher thread. 182: * 183: * @since 1.5 184: */ 185: public static final int UPDATE_WHEN_ON_EDT = 0; 186: 187: /** Keeps track of the current update policy **/ 188: int policy = UPDATE_WHEN_ON_EDT; 189: 190: /** 191: * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. 192: */ 193: protected ChangeEvent changeEvent = new ChangeEvent(this); 194: 195: /** 196: * Stores all registered event listeners. 197: */ 198: protected EventListenerList listenerList = new EventListenerList(); 199: 200: /** 201: * Our document listener. 202: */ 203: DocumentListener documentListener; 204: 205: /** 206: * Our property listener. 207: */ 208: PropertyChangeListener propertyChangeListener; 209: 210: /** 211: * The text component in which this caret is installed. 212: */ 213: private JTextComponent textComponent; 214: 215: /** 216: * Indicates if the selection should be visible or not. 217: */ 218: private boolean selectionVisible = true; 219: 220: /** 221: * The blink rate of this <code>Caret</code>. 222: */ 223: private int blinkRate = 500; 224: 225: /** 226: * The current dot position. 227: */ 228: private int dot = 0; 229: 230: /** 231: * The current mark position. 232: */ 233: private int mark = 0; 234: 235: /** 236: * The current visual caret position. 237: */ 238: private Point magicCaretPosition = null; 239: 240: /** 241: * Indicates if this <code>Caret</code> is currently visible or not. 242: */ 243: private boolean visible = true; 244: 245: /** 246: * The current highlight entry. 247: */ 248: private Object highlightEntry; 249: 250: /** 251: * Sets the Caret update policy. 252: * 253: * @param policy the new policy. Valid values are: 254: * ALWAYS_UPDATE: always update the Caret position, even when Document 255: * updates don't occur on the Event Dispatcher thread. 256: * NEVER_UPDATE: don't update the Caret position unless the Document 257: * length becomes less than the Caret position (then update the 258: * Caret to the end of the Document). 259: * UPDATE_WHEN_ON_EDT: update the Caret position when the 260: * Document updates occur on the Event Dispatcher thread. This is the 261: * default. 262: * 263: * @since 1.5 264: * @throws IllegalArgumentException if policy is not one of the above. 265: */ 266: public void setUpdatePolicy (int policy) 267: { 268: if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE 269: && policy != UPDATE_WHEN_ON_EDT) 270: throw new 271: IllegalArgumentException 272: ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT"); 273: this.policy = policy; 274: } 275: 276: /** 277: * Gets the caret update policy. 278: * 279: * @return the caret update policy. 280: * @since 1.5 281: */ 282: public int getUpdatePolicy () 283: { 284: return policy; 285: } 286: 287: /** 288: * Moves the caret position when the mouse is dragged over the text 289: * component, modifying the selection accordingly. 290: * 291: * @param event the <code>MouseEvent</code> describing the drag operation 292: */ 293: public void mouseDragged(MouseEvent event) 294: { 295: // FIXME: Implement this properly. 296: } 297: 298: /** 299: * Indicates a mouse movement over the text component. Does nothing here. 300: * 301: * @param event the <code>MouseEvent</code> describing the mouse operation 302: */ 303: public void mouseMoved(MouseEvent event) 304: { 305: // Nothing to do here. 306: } 307: 308: /** 309: * When the click is received from Button 1 then the following actions 310: * are performed here: 311: * 312: * <ul> 313: * <li>If we receive a double click, the caret position (dot) is set 314: * to the position associated to the mouse click and the word at 315: * this location is selected.</li> 316: * <li>If we receive a triple click, the caret position (dot) is set 317: * to the position associated to the mouse click and the line at 318: * this location is selected.</li> 319: * </ul> 320: * 321: * @param event the <code>MouseEvent</code> describing the click operation 322: */ 323: public void mouseClicked(MouseEvent event) 324: { 325: // FIXME: Implement this properly. 326: } 327: 328: /** 329: * Indicates that the mouse has entered the text component. Nothing is done 330: * here. 331: * 332: * @param event the <code>MouseEvent</code> describing the mouse operation 333: */ 334: public void mouseEntered(MouseEvent event) 335: { 336: // Nothing to do here. 337: } 338: 339: /** 340: * Indicates that the mouse has exited the text component. Nothing is done 341: * here. 342: * 343: * @param event the <code>MouseEvent</code> describing the mouse operation 344: */ 345: public void mouseExited(MouseEvent event) 346: { 347: // TODO: What to do here, if anything? 348: } 349: 350: /** 351: * If the button 1 is pressed, the caret position is updated to the 352: * position of the mouse click and the text component requests the input 353: * focus if it is enabled. If the SHIFT key is held down, the caret will 354: * be moved, which might select the text between the old and new location. 355: * 356: * @param event the <code>MouseEvent</code> describing the press operation 357: */ 358: public void mousePressed(MouseEvent event) 359: { 360: // FIXME: Implement this properly. 361: if (!(event.getButton() == MouseEvent.BUTTON1)) 362: return; 363: setDot(textComponent.viewToModel(event.getPoint())); 364: } 365: 366: /** 367: * Indicates that a mouse button has been released on the text component. 368: * Nothing is done here. 369: * 370: * @param event the <code>MouseEvent</code> describing the mouse operation 371: */ 372: public void mouseReleased(MouseEvent event) 373: { 374: // Nothing to do here. 375: } 376: 377: /** 378: * Sets the caret to <code>visible</code> if the text component is editable. 379: * 380: * @param event the <code>FocusEvent</code> 381: */ 382: public void focusGained(FocusEvent event) 383: { 384: // TODO: Implement this properly. 385: } 386: 387: /** 388: * Sets the caret to <code>invisible</code>. 389: * 390: * @param event the <code>FocusEvent</code> 391: */ 392: public void focusLost(FocusEvent event) 393: { 394: // TODO: Implement this properly. 395: } 396: 397: /** 398: * Moves the caret to the position specified in the <code>MouseEvent</code>. 399: * This will cause a selection if the dot and mark are different. 400: * 401: * @param event the <code>MouseEvent</code> from which to fetch the position 402: */ 403: protected void moveCaret(MouseEvent event) 404: { 405: // FIXME: Implement this properly. 406: } 407: 408: /** 409: * Repositions the caret to the position specified in the 410: * <code>MouseEvent</code>. 411: * 412: * @param event the <code>MouseEvent</code> from which to fetch the position 413: */ 414: protected void positionCaret(MouseEvent event) 415: { 416: // FIXME: Implement this properly. 417: } 418: 419: /** 420: * Deinstalls this <code>Caret</code> from the specified 421: * <code>JTextComponent</code>. This removes any listeners that have been 422: * registered by this <code>Caret</code>. 423: * 424: * @param c the text component from which to install this caret 425: */ 426: public void deinstall(JTextComponent c) 427: { 428: textComponent.removeFocusListener(this); 429: textComponent.removeMouseListener(this); 430: textComponent.removeMouseMotionListener(this); 431: textComponent.getDocument().removeDocumentListener(documentListener); 432: documentListener = null; 433: textComponent.removePropertyChangeListener(propertyChangeListener); 434: propertyChangeListener = null; 435: textComponent = null; 436: } 437: 438: /** 439: * Installs this <code>Caret</code> on the specified 440: * <code>JTextComponent</code>. This registers a couple of listeners 441: * on the text component. 442: * 443: * @param c the text component on which to install this caret 444: */ 445: public void install(JTextComponent c) 446: { 447: textComponent = c; 448: textComponent.addFocusListener(this); 449: textComponent.addMouseListener(this); 450: textComponent.addMouseMotionListener(this); 451: propertyChangeListener = new PropertyChangeHandler(); 452: textComponent.addPropertyChangeListener(propertyChangeListener); 453: documentListener = new DocumentHandler(); 454: textComponent.getDocument().addDocumentListener(documentListener); 455: repaint(); 456: } 457: 458: /** 459: * Sets the current visual position of this <code>Caret</code>. 460: * 461: * @param p the Point to use for the saved location. May be <code>null</code> 462: * to indicate that there is no visual location 463: */ 464: public void setMagicCaretPosition(Point p) 465: { 466: magicCaretPosition = p; 467: } 468: 469: /** 470: * Returns the current visual position of this <code>Caret</code>. 471: * 472: * @return the current visual position of this <code>Caret</code> 473: * 474: * @see #setMagicCaretPosition 475: */ 476: public Point getMagicCaretPosition() 477: { 478: return magicCaretPosition; 479: } 480: 481: /** 482: * Returns the current position of the <code>mark</code>. The 483: * <code>mark</code> marks the location in the <code>Document</code> that 484: * is the end of a selection. If there is no selection, the <code>mark</code> 485: * is the same as the <code>dot</code>. 486: * 487: * @return the current position of the mark 488: */ 489: public int getMark() 490: { 491: return mark; 492: } 493: 494: private void handleHighlight() 495: { 496: Highlighter highlighter = textComponent.getHighlighter(); 497: 498: if (highlighter == null) 499: return; 500: 501: int p0 = Math.min(dot, mark); 502: int p1 = Math.max(dot, mark); 503: 504: if (selectionVisible && p0 != p1) 505: { 506: try 507: { 508: if (highlightEntry == null) 509: highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter()); 510: else 511: highlighter.changeHighlight(highlightEntry, p0, p1); 512: } 513: catch (BadLocationException e) 514: { 515: // This should never happen. 516: throw new InternalError(); 517: } 518: } 519: else 520: { 521: if (highlightEntry != null) 522: { 523: highlighter.removeHighlight(highlightEntry); 524: highlightEntry = null; 525: } 526: } 527: } 528: 529: /** 530: * Sets the visiblity state of the selection. 531: * 532: * @param v <code>true</code> if the selection should be visible, 533: * <code>false</code> otherwise 534: */ 535: public void setSelectionVisible(boolean v) 536: { 537: if (selectionVisible == v) 538: return; 539: 540: selectionVisible = v; 541: handleHighlight(); 542: repaint(); 543: } 544: 545: /** 546: * Returns <code>true</code> if the selection is currently visible, 547: * <code>false</code> otherwise. 548: * 549: * @return <code>true</code> if the selection is currently visible, 550: * <code>false</code> otherwise 551: */ 552: public boolean isSelectionVisible() 553: { 554: return selectionVisible; 555: } 556: 557: /** 558: * Causes the <code>Caret</code> to repaint itself. 559: */ 560: protected final void repaint() 561: { 562: textComponent.repaint(this); 563: } 564: 565: /** 566: * Paints this <code>Caret</code> using the specified <code>Graphics</code> 567: * context. 568: * 569: * @param g the graphics context to use 570: */ 571: public void paint(Graphics g) 572: { 573: if (textComponent == null) 574: return; 575: 576: int dot = getDot(); 577: Rectangle rect = null; 578: 579: try 580: { 581: rect = textComponent.modelToView(dot); 582: } 583: catch (BadLocationException e) 584: { 585: // This should never happen as dot should be always valid. 586: return; 587: } 588: 589: if (rect == null) 590: return; 591: 592: // First we need to delete the old caret. 593: // FIXME: Implement deleting of old caret. 594: 595: // Now draw the caret on the new position if visible. 596: if (visible) 597: { 598: g.setColor(textComponent.getCaretColor()); 599: g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); 600: } 601: } 602: 603: /** 604: * Returns all registered event listeners of the specified type. 605: * 606: * @param listenerType the type of listener to return 607: * 608: * @return all registered event listeners of the specified type 609: */ 610: public EventListener[] getListeners(Class listenerType) 611: { 612: return listenerList.getListeners(listenerType); 613: } 614: 615: /** 616: * Registers a {@link ChangeListener} that is notified whenever that state 617: * of this <code>Caret</code> changes. 618: * 619: * @param listener the listener to register to this caret 620: */ 621: public void addChangeListener(ChangeListener listener) 622: { 623: listenerList.add(ChangeListener.class, listener); 624: } 625: 626: /** 627: * Removes a {@link ChangeListener} from the list of registered listeners. 628: * 629: * @param listener the listener to remove 630: */ 631: public void removeChangeListener(ChangeListener listener) 632: { 633: listenerList.remove(ChangeListener.class, listener); 634: } 635: 636: /** 637: * Returns all registered {@link ChangeListener}s of this <code>Caret</code>. 638: * 639: * @return all registered {@link ChangeListener}s of this <code>Caret</code> 640: */ 641: public ChangeListener[] getChangeListeners() 642: { 643: return (ChangeListener[]) getListeners(ChangeListener.class); 644: } 645: 646: /** 647: * Notifies all registered {@link ChangeListener}s that the state 648: * of this <code>Caret</code> has changed. 649: */ 650: protected void fireStateChanged() 651: { 652: ChangeListener[] listeners = getChangeListeners(); 653: 654: for (int index = 0; index < listeners.length; ++index) 655: listeners[index].stateChanged(changeEvent); 656: } 657: 658: /** 659: * Returns the <code>JTextComponent</code> on which this <code>Caret</code> 660: * is installed. 661: * 662: * @return the <code>JTextComponent</code> on which this <code>Caret</code> 663: * is installed 664: */ 665: protected final JTextComponent getComponent() 666: { 667: return textComponent; 668: } 669: 670: /** 671: * Returns the blink rate of this <code>Caret</code> in milliseconds. 672: * A value of <code>0</code> means that the caret does not blink. 673: * 674: * @return the blink rate of this <code>Caret</code> or <code>0</code> if 675: * this caret does not blink 676: */ 677: public int getBlinkRate() 678: { 679: return blinkRate; 680: } 681: 682: /** 683: * Sets the blink rate of this <code>Caret</code> in milliseconds. 684: * A value of <code>0</code> means that the caret does not blink. 685: * 686: * @param rate the new blink rate to set 687: */ 688: public void setBlinkRate(int rate) 689: { 690: blinkRate = rate; 691: } 692: 693: /** 694: * Returns the current position of this <code>Caret</code> within the 695: * <code>Document</code>. 696: * 697: * @return the current position of this <code>Caret</code> within the 698: * <code>Document</code> 699: */ 700: public int getDot() 701: { 702: return dot; 703: } 704: 705: /** 706: * Moves the <code>dot</code> location without touching the 707: * <code>mark</code>. This is used when making a selection. 708: * 709: * @param dot the location where to move the dot 710: * 711: * @see #setDot(int) 712: */ 713: public void moveDot(int dot) 714: { 715: this.dot = dot; 716: handleHighlight(); 717: repaint(); 718: } 719: 720: /** 721: * Sets the current position of this <code>Caret</code> within the 722: * <code>Document</code>. This also sets the <code>mark</code> to the 723: * new location. 724: * 725: * @param dot the new position to be set 726: * 727: * @see #moveDot(int) 728: */ 729: public void setDot(int dot) 730: { 731: this.dot = dot; 732: this.mark = dot; 733: handleHighlight(); 734: repaint(); 735: } 736: 737: /** 738: * Returns <code>true</code> if this <code>Caret</code> is currently visible, 739: * and <code>false</code> if it is not. 740: * 741: * @return <code>true</code> if this <code>Caret</code> is currently visible, 742: * and <code>false</code> if it is not 743: */ 744: public boolean isVisible() 745: { 746: return visible; 747: } 748: 749: /** 750: * Sets the visibility state of the caret. <code>true</code> shows the 751: * <code>Caret</code>, <code>false</code> hides it. 752: * 753: * @param v the visibility to set 754: */ 755: public void setVisible(boolean v) 756: { 757: if (v != visible) 758: { 759: visible = v; 760: repaint(); 761: } 762: } 763: 764: /** 765: * Returns the {@link Highlighter.HighlightPainter} that should be used 766: * to paint the selection. 767: * 768: * @return the {@link Highlighter.HighlightPainter} that should be used 769: * to paint the selection 770: */ 771: protected Highlighter.HighlightPainter getSelectionPainter() 772: { 773: return DefaultHighlighter.DefaultPainter; 774: } 775: }
GNU Classpath (0.19) |