1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.awt.datatransfer.*;
  30 import java.beans.*;
  31 import java.awt.event.ActionEvent;
  32 import java.awt.event.ActionListener;
  33 import java.io.*;
  34 import javax.swing.*;
  35 import javax.swing.event.*;
  36 import javax.swing.plaf.*;
  37 import java.util.EventListener;
  38 import sun.swing.SwingUtilities2;
  39 
  40 /**
  41  * A default implementation of Caret.  The caret is rendered as
  42  * a vertical line in the color specified by the CaretColor property
  43  * of the associated JTextComponent.  It can blink at the rate specified
  44  * by the BlinkRate property.
  45  * <p>
  46  * This implementation expects two sources of asynchronous notification.
  47  * The timer thread fires asynchronously, and causes the caret to simply
  48  * repaint the most recent bounding box.  The caret also tracks change
  49  * as the document is modified.  Typically this will happen on the
  50  * event dispatch thread as a result of some mouse or keyboard event.
  51  * The caret behavior on both synchronous and asynchronous documents updates
  52  * is controlled by <code>UpdatePolicy</code> property. The repaint of the
  53  * new caret location will occur on the event thread in any case, as calls to
  54  * <code>modelToView</code> are only safe on the event thread.
  55  * <p>
  56  * The caret acts as a mouse and focus listener on the text component
  57  * it has been installed in, and defines the caret semantics based upon
  58  * those events.  The listener methods can be reimplemented to change the
  59  * semantics.
  60  * By default, the first mouse button will be used to set focus and caret
  61  * position.  Dragging the mouse pointer with the first mouse button will
  62  * sweep out a selection that is contiguous in the model.  If the associated
  63  * text component is editable, the caret will become visible when focus
  64  * is gained, and invisible when focus is lost.
  65  * <p>
  66  * The Highlighter bound to the associated text component is used to
  67  * render the selection by default.
  68  * Selection appearance can be customized by supplying a
  69  * painter to use for the highlights.  By default a painter is used that
  70  * will render a solid color as specified in the associated text component
  71  * in the <code>SelectionColor</code> property.  This can easily be changed
  72  * by reimplementing the
  73  * {@link #getSelectionPainter getSelectionPainter}
  74  * method.
  75  * <p>
  76  * A customized caret appearance can be achieved by reimplementing
  77  * the paint method.  If the paint method is changed, the damage method
  78  * should also be reimplemented to cause a repaint for the area needed
  79  * to render the caret.  The caret extends the Rectangle class which
  80  * is used to hold the bounding box for where the caret was last rendered.
  81  * This enables the caret to repaint in a thread-safe manner when the
  82  * caret moves without making a call to modelToView which is unstable
  83  * between model updates and view repair (i.e. the order of delivery
  84  * to DocumentListeners is not guaranteed).
  85  * <p>
  86  * The magic caret position is set to null when the caret position changes.
  87  * A timer is used to determine the new location (after the caret change).
  88  * When the timer fires, if the magic caret position is still null it is
  89  * reset to the current caret position. Any actions that change
  90  * the caret position and want the magic caret position to remain the
  91  * same, must remember the magic caret position, change the cursor, and
  92  * then set the magic caret position to its original value. This has the
  93  * benefit that only actions that want the magic caret position to persist
  94  * (such as open/down) need to know about it.
  95  * <p>
  96  * <strong>Warning:</strong>
  97  * Serialized objects of this class will not be compatible with
  98  * future Swing releases. The current serialization support is
  99  * appropriate for short term storage or RMI between applications running
 100  * the same version of Swing.  As of 1.4, support for long term storage
 101  * of all JavaBeans&trade;
 102  * has been added to the <code>java.beans</code> package.
 103  * Please see {@link java.beans.XMLEncoder}.
 104  *
 105  * @author  Timothy Prinzing
 106  * @see     Caret
 107  */
 108 @SuppressWarnings("serial") // Same-version serialization only
 109 public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener {
 110 
 111     /**
 112      * Indicates that the caret position is to be updated only when
 113      * document changes are performed on the Event Dispatching Thread.
 114      * @see #setUpdatePolicy
 115      * @see #getUpdatePolicy
 116      * @since 1.5
 117      */
 118     public static final int UPDATE_WHEN_ON_EDT = 0;
 119 
 120     /**
 121      * Indicates that the caret should remain at the same
 122      * absolute position in the document regardless of any document
 123      * updates, except when the document length becomes less than
 124      * the current caret position due to removal. In that case the caret
 125      * position is adjusted to the end of the document.
 126      *
 127      * @see #setUpdatePolicy
 128      * @see #getUpdatePolicy
 129      * @since 1.5
 130      */
 131     public static final int NEVER_UPDATE = 1;
 132 
 133     /**
 134      * Indicates that the caret position is to be <b>always</b>
 135      * updated accordingly to the document changes regardless whether
 136      * the document updates are performed on the Event Dispatching Thread
 137      * or not.
 138      *
 139      * @see #setUpdatePolicy
 140      * @see #getUpdatePolicy
 141      * @since 1.5
 142      */
 143     public static final int ALWAYS_UPDATE = 2;
 144 
 145     /**
 146      * Constructs a default caret.
 147      */
 148     public DefaultCaret() {
 149     }
 150 
 151     /**
 152      * Sets the caret movement policy on the document updates. Normally
 153      * the caret updates its absolute position within the document on
 154      * insertions occurred before or at the caret position and
 155      * on removals before the caret position. 'Absolute position'
 156      * means here the position relative to the start of the document.
 157      * For example if
 158      * a character is typed within editable text component it is inserted
 159      * at the caret position and the caret moves to the next absolute
 160      * position within the document due to insertion and if
 161      * <code>BACKSPACE</code> is typed then caret decreases its absolute
 162      * position due to removal of a character before it. Sometimes
 163      * it may be useful to turn off the caret position updates so that
 164      * the caret stays at the same absolute position within the
 165      * document position regardless of any document updates.
 166      * <p>
 167      * The following update policies are allowed:
 168      * <ul>
 169      *   <li><code>NEVER_UPDATE</code>: the caret stays at the same
 170      *       absolute position in the document regardless of any document
 171      *       updates, except when document length becomes less than
 172      *       the current caret position due to removal. In that case caret
 173      *       position is adjusted to the end of the document.
 174      *       The caret doesn't try to keep itself visible by scrolling
 175      *       the associated view when using this policy. </li>
 176      *   <li><code>ALWAYS_UPDATE</code>: the caret always tracks document
 177      *       changes. For regular changes it increases its position
 178      *       if an insertion occurs before or at its current position,
 179      *       and decreases position if a removal occurs before
 180      *       its current position. For undo/redo updates it is always
 181      *       moved to the position where update occurred. The caret
 182      *       also tries to keep itself visible by calling
 183      *       <code>adjustVisibility</code> method.</li>
 184      *   <li><code>UPDATE_WHEN_ON_EDT</code>: acts like <code>ALWAYS_UPDATE</code>
 185      *       if the document updates are performed on the Event Dispatching Thread
 186      *       and like <code>NEVER_UPDATE</code> if updates are performed on
 187      *       other thread. </li>
 188      * </ul> <p>
 189      * The default property value is <code>UPDATE_WHEN_ON_EDT</code>.
 190      *
 191      * @param policy one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,
 192      * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>
 193      * @throws IllegalArgumentException if invalid value is passed
 194      *
 195      * @see #getUpdatePolicy
 196      * @see #adjustVisibility
 197      * @see #UPDATE_WHEN_ON_EDT
 198      * @see #NEVER_UPDATE
 199      * @see #ALWAYS_UPDATE
 200      *
 201      * @since 1.5
 202      */
 203     public void setUpdatePolicy(int policy) {
 204         updatePolicy = policy;
 205     }
 206 
 207     /**
 208      * Gets the caret movement policy on document updates.
 209      *
 210      * @return one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,
 211      * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>
 212      *
 213      * @see #setUpdatePolicy
 214      * @see #UPDATE_WHEN_ON_EDT
 215      * @see #NEVER_UPDATE
 216      * @see #ALWAYS_UPDATE
 217      *
 218      * @since 1.5
 219      */
 220     public int getUpdatePolicy() {
 221         return updatePolicy;
 222     }
 223 
 224     /**
 225      * Gets the text editor component that this caret is
 226      * is bound to.
 227      *
 228      * @return the component
 229      */
 230     protected final JTextComponent getComponent() {
 231         return component;
 232     }
 233 
 234     /**
 235      * Cause the caret to be painted.  The repaint
 236      * area is the bounding box of the caret (i.e.
 237      * the caret rectangle or <em>this</em>).
 238      * <p>
 239      * This method is thread safe, although most Swing methods
 240      * are not. Please see
 241      * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency
 242      * in Swing</A> for more information.
 243      */
 244     protected final synchronized void repaint() {
 245         if (component != null) {
 246             component.repaint(x, y, width, height);
 247         }
 248     }
 249 
 250     /**
 251      * Damages the area surrounding the caret to cause
 252      * it to be repainted in a new location.  If paint()
 253      * is reimplemented, this method should also be
 254      * reimplemented.  This method should update the
 255      * caret bounds (x, y, width, and height).
 256      *
 257      * @param r  the current location of the caret
 258      * @see #paint
 259      */
 260     protected synchronized void damage(Rectangle r) {
 261         if (r != null) {
 262             int damageWidth = getCaretWidth(r.height);
 263             x = r.x - 4 - (damageWidth >> 1);
 264             y = r.y;
 265             width = 9 + damageWidth;
 266             height = r.height;
 267             repaint();
 268         }
 269     }
 270 
 271     /**
 272      * Scrolls the associated view (if necessary) to make
 273      * the caret visible.  Since how this should be done
 274      * is somewhat of a policy, this method can be
 275      * reimplemented to change the behavior.  By default
 276      * the scrollRectToVisible method is called on the
 277      * associated component.
 278      *
 279      * @param nloc the new position to scroll to
 280      */
 281     protected void adjustVisibility(Rectangle nloc) {
 282         if(component == null) {
 283             return;
 284         }
 285         if (SwingUtilities.isEventDispatchThread()) {
 286                 component.scrollRectToVisible(nloc);
 287         } else {
 288             SwingUtilities.invokeLater(new SafeScroller(nloc));
 289         }
 290     }
 291 
 292     /**
 293      * Gets the painter for the Highlighter.
 294      *
 295      * @return the painter
 296      */
 297     protected Highlighter.HighlightPainter getSelectionPainter() {
 298         return DefaultHighlighter.DefaultPainter;
 299     }
 300 
 301     /**
 302      * Tries to set the position of the caret from
 303      * the coordinates of a mouse event, using viewToModel().
 304      *
 305      * @param e the mouse event
 306      */
 307     protected void positionCaret(MouseEvent e) {
 308         Point pt = new Point(e.getX(), e.getY());
 309         Position.Bias[] biasRet = new Position.Bias[1];
 310         int pos = component.getUI().viewToModel(component, pt, biasRet);
 311         if(biasRet[0] == null)
 312             biasRet[0] = Position.Bias.Forward;
 313         if (pos >= 0) {
 314             setDot(pos, biasRet[0]);
 315         }
 316     }
 317 
 318     /**
 319      * Tries to move the position of the caret from
 320      * the coordinates of a mouse event, using viewToModel().
 321      * This will cause a selection if the dot and mark
 322      * are different.
 323      *
 324      * @param e the mouse event
 325      */
 326     protected void moveCaret(MouseEvent e) {
 327         Point pt = new Point(e.getX(), e.getY());
 328         Position.Bias[] biasRet = new Position.Bias[1];
 329         int pos = component.getUI().viewToModel(component, pt, biasRet);
 330         if(biasRet[0] == null)
 331             biasRet[0] = Position.Bias.Forward;
 332         if (pos >= 0) {
 333             moveDot(pos, biasRet[0]);
 334         }
 335     }
 336 
 337     // --- FocusListener methods --------------------------
 338 
 339     /**
 340      * Called when the component containing the caret gains
 341      * focus.  This is implemented to set the caret to visible
 342      * if the component is editable.
 343      *
 344      * @param e the focus event
 345      * @see FocusListener#focusGained
 346      */
 347     public void focusGained(FocusEvent e) {
 348         if (component.isEnabled()) {
 349             if (component.isEditable()) {
 350                 setVisible(true);
 351             }
 352             setSelectionVisible(true);
 353         }
 354     }
 355 
 356     /**
 357      * Called when the component containing the caret loses
 358      * focus.  This is implemented to set the caret to visibility
 359      * to false.
 360      *
 361      * @param e the focus event
 362      * @see FocusListener#focusLost
 363      */
 364     public void focusLost(FocusEvent e) {
 365         setVisible(false);
 366         setSelectionVisible(ownsSelection || e.isTemporary());
 367     }
 368 
 369 
 370     /**
 371      * Selects word based on the MouseEvent
 372      */
 373     private void selectWord(MouseEvent e) {
 374         if (selectedWordEvent != null
 375             && selectedWordEvent.getX() == e.getX()
 376             && selectedWordEvent.getY() == e.getY()) {
 377             //we already done selection for this
 378             return;
 379         }
 380                     Action a = null;
 381                     ActionMap map = getComponent().getActionMap();
 382                     if (map != null) {
 383                         a = map.get(DefaultEditorKit.selectWordAction);
 384                     }
 385                     if (a == null) {
 386                         if (selectWord == null) {
 387                             selectWord = new DefaultEditorKit.SelectWordAction();
 388                         }
 389                         a = selectWord;
 390                     }
 391                     a.actionPerformed(new ActionEvent(getComponent(),
 392                                                       ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));
 393         selectedWordEvent = e;
 394     }
 395 
 396     // --- MouseListener methods -----------------------------------
 397 
 398     /**
 399      * Called when the mouse is clicked.  If the click was generated
 400      * from button1, a double click selects a word,
 401      * and a triple click the current line.
 402      *
 403      * @param e the mouse event
 404      * @see MouseListener#mouseClicked
 405      */
 406     public void mouseClicked(MouseEvent e) {
 407         if (getComponent() == null) {
 408             return;
 409         }
 410 
 411         int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);
 412 
 413         if (! e.isConsumed()) {
 414             if (SwingUtilities.isLeftMouseButton(e)) {
 415                 // mouse 1 behavior
 416                 if(nclicks == 1) {
 417                     selectedWordEvent = null;
 418                 } else if(nclicks == 2
 419                           && SwingUtilities2.canEventAccessSystemClipboard(e)) {
 420                     selectWord(e);
 421                     selectedWordEvent = null;
 422                 } else if(nclicks == 3
 423                           && SwingUtilities2.canEventAccessSystemClipboard(e)) {
 424                     Action a = null;
 425                     ActionMap map = getComponent().getActionMap();
 426                     if (map != null) {
 427                         a = map.get(DefaultEditorKit.selectLineAction);
 428                     }
 429                     if (a == null) {
 430                         if (selectLine == null) {
 431                             selectLine = new DefaultEditorKit.SelectLineAction();
 432                         }
 433                         a = selectLine;
 434                     }
 435                     a.actionPerformed(new ActionEvent(getComponent(),
 436                                                       ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));
 437                 }
 438             } else if (SwingUtilities.isMiddleMouseButton(e)) {
 439                 // mouse 2 behavior
 440                 if (nclicks == 1 && component.isEditable() && component.isEnabled()
 441                     && SwingUtilities2.canEventAccessSystemClipboard(e)) {
 442                     // paste system selection, if it exists
 443                     JTextComponent c = (JTextComponent) e.getSource();
 444                     if (c != null) {
 445                         try {
 446                             Toolkit tk = c.getToolkit();
 447                             Clipboard buffer = tk.getSystemSelection();
 448                             if (buffer != null) {
 449                                 // platform supports system selections, update it.
 450                                 adjustCaret(e);
 451                                 TransferHandler th = c.getTransferHandler();
 452                                 if (th != null) {
 453                                     Transferable trans = null;
 454 
 455                                     try {
 456                                         trans = buffer.getContents(null);
 457                                     } catch (IllegalStateException ise) {
 458                                         // clipboard was unavailable
 459                                         UIManager.getLookAndFeel().provideErrorFeedback(c);
 460                                     }
 461 
 462                                     if (trans != null) {
 463                                         th.importData(c, trans);
 464                                     }
 465                                 }
 466                                 adjustFocus(true);
 467                             }
 468                         } catch (HeadlessException he) {
 469                             // do nothing... there is no system clipboard
 470                         }
 471                     }
 472                 }
 473             }
 474         }
 475     }
 476 
 477     /**
 478      * If button 1 is pressed, this is implemented to
 479      * request focus on the associated text component,
 480      * and to set the caret position. If the shift key is held down,
 481      * the caret will be moved, potentially resulting in a selection,
 482      * otherwise the
 483      * caret position will be set to the new location.  If the component
 484      * is not enabled, there will be no request for focus.
 485      *
 486      * @param e the mouse event
 487      * @see MouseListener#mousePressed
 488      */
 489     public void mousePressed(MouseEvent e) {
 490         int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);
 491 
 492         if (SwingUtilities.isLeftMouseButton(e)) {
 493             if (e.isConsumed()) {
 494                 shouldHandleRelease = true;
 495             } else {
 496                 shouldHandleRelease = false;
 497                 adjustCaretAndFocus(e);
 498                 if (nclicks == 2
 499                     && SwingUtilities2.canEventAccessSystemClipboard(e)) {
 500                     selectWord(e);
 501                 }
 502             }
 503         }
 504     }
 505 
 506     void adjustCaretAndFocus(MouseEvent e) {
 507         adjustCaret(e);
 508         adjustFocus(false);
 509     }
 510 
 511     /**
 512      * Adjusts the caret location based on the MouseEvent.
 513      */
 514     private void adjustCaret(MouseEvent e) {
 515         if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0 &&
 516             getDot() != -1) {
 517             moveCaret(e);
 518         } else if (!e.isPopupTrigger()) {
 519             positionCaret(e);
 520         }
 521     }
 522 
 523     /**
 524      * Adjusts the focus, if necessary.
 525      *
 526      * @param inWindow if true indicates requestFocusInWindow should be used
 527      */
 528     private void adjustFocus(boolean inWindow) {
 529         if ((component != null) && component.isEnabled() &&
 530                                    component.isRequestFocusEnabled()) {
 531             if (inWindow) {
 532                 component.requestFocusInWindow();
 533             }
 534             else {
 535                 component.requestFocus();
 536             }
 537         }
 538     }
 539 
 540     /**
 541      * Called when the mouse is released.
 542      *
 543      * @param e the mouse event
 544      * @see MouseListener#mouseReleased
 545      */
 546     public void mouseReleased(MouseEvent e) {
 547         if (!e.isConsumed()
 548                 && shouldHandleRelease
 549                 && SwingUtilities.isLeftMouseButton(e)) {
 550 
 551             adjustCaretAndFocus(e);
 552         }
 553     }
 554 
 555     /**
 556      * Called when the mouse enters a region.
 557      *
 558      * @param e the mouse event
 559      * @see MouseListener#mouseEntered
 560      */
 561     public void mouseEntered(MouseEvent e) {
 562     }
 563 
 564     /**
 565      * Called when the mouse exits a region.
 566      *
 567      * @param e the mouse event
 568      * @see MouseListener#mouseExited
 569      */
 570     public void mouseExited(MouseEvent e) {
 571     }
 572 
 573     // --- MouseMotionListener methods -------------------------
 574 
 575     /**
 576      * Moves the caret position
 577      * according to the mouse pointer's current
 578      * location.  This effectively extends the
 579      * selection.  By default, this is only done
 580      * for mouse button 1.
 581      *
 582      * @param e the mouse event
 583      * @see MouseMotionListener#mouseDragged
 584      */
 585     public void mouseDragged(MouseEvent e) {
 586         if ((! e.isConsumed()) && SwingUtilities.isLeftMouseButton(e)) {
 587             moveCaret(e);
 588         }
 589     }
 590 
 591     /**
 592      * Called when the mouse is moved.
 593      *
 594      * @param e the mouse event
 595      * @see MouseMotionListener#mouseMoved
 596      */
 597     public void mouseMoved(MouseEvent e) {
 598     }
 599 
 600     // ---- Caret methods ---------------------------------
 601 
 602     /**
 603      * Renders the caret as a vertical line.  If this is reimplemented
 604      * the damage method should also be reimplemented as it assumes the
 605      * shape of the caret is a vertical line.  Sets the caret color to
 606      * the value returned by getCaretColor().
 607      * <p>
 608      * If there are multiple text directions present in the associated
 609      * document, a flag indicating the caret bias will be rendered.
 610      * This will occur only if the associated document is a subclass
 611      * of AbstractDocument and there are multiple bidi levels present
 612      * in the bidi element structure (i.e. the text has multiple
 613      * directions associated with it).
 614      *
 615      * @param g the graphics context
 616      * @see #damage
 617      */
 618     public void paint(Graphics g) {
 619         if(isVisible()) {
 620             try {
 621                 TextUI mapper = component.getUI();
 622                 Rectangle r = mapper.modelToView(component, dot, dotBias);
 623 
 624                 if ((r == null) || ((r.width == 0) && (r.height == 0))) {
 625                     return;
 626                 }
 627                 if (width > 0 && height > 0 &&
 628                                 !this._contains(r.x, r.y, r.width, r.height)) {
 629                     // We seem to have gotten out of sync and no longer
 630                     // contain the right location, adjust accordingly.
 631                     Rectangle clip = g.getClipBounds();
 632 
 633                     if (clip != null && !clip.contains(this)) {
 634                         // Clip doesn't contain the old location, force it
 635                         // to be repainted lest we leave a caret around.
 636                         repaint();
 637                     }
 638                     // This will potentially cause a repaint of something
 639                     // we're already repainting, but without changing the
 640                     // semantics of damage we can't really get around this.
 641                     damage(r);
 642                 }
 643                 g.setColor(component.getCaretColor());
 644                 int paintWidth = getCaretWidth(r.height);
 645                 r.x -= paintWidth  >> 1;
 646                 g.fillRect(r.x, r.y, paintWidth, r.height);
 647 
 648                 // see if we should paint a flag to indicate the bias
 649                 // of the caret.
 650                 // PENDING(prinz) this should be done through
 651                 // protected methods so that alternative LAF
 652                 // will show bidi information.
 653                 Document doc = component.getDocument();
 654                 if (doc instanceof AbstractDocument) {
 655                     Element bidi = ((AbstractDocument)doc).getBidiRootElement();
 656                     if ((bidi != null) && (bidi.getElementCount() > 1)) {
 657                         // there are multiple directions present.
 658                         flagXPoints[0] = r.x + ((dotLTR) ? paintWidth : 0);
 659                         flagYPoints[0] = r.y;
 660                         flagXPoints[1] = flagXPoints[0];
 661                         flagYPoints[1] = flagYPoints[0] + 4;
 662                         flagXPoints[2] = flagXPoints[0] + ((dotLTR) ? 4 : -4);
 663                         flagYPoints[2] = flagYPoints[0];
 664                         g.fillPolygon(flagXPoints, flagYPoints, 3);
 665                     }
 666                 }
 667             } catch (BadLocationException e) {
 668                 // can't render I guess
 669                 //System.err.println("Can't render cursor");
 670             }
 671         }
 672     }
 673 
 674     /**
 675      * Called when the UI is being installed into the
 676      * interface of a JTextComponent.  This can be used
 677      * to gain access to the model that is being navigated
 678      * by the implementation of this interface.  Sets the dot
 679      * and mark to 0, and establishes document, property change,
 680      * focus, mouse, and mouse motion listeners.
 681      *
 682      * @param c the component
 683      * @see Caret#install
 684      */
 685     public void install(JTextComponent c) {
 686         component = c;
 687         Document doc = c.getDocument();
 688         dot = mark = 0;
 689         dotLTR = markLTR = true;
 690         dotBias = markBias = Position.Bias.Forward;
 691         if (doc != null) {
 692             doc.addDocumentListener(handler);
 693         }
 694         c.addPropertyChangeListener(handler);
 695         c.addFocusListener(this);
 696         c.addMouseListener(this);
 697         c.addMouseMotionListener(this);
 698 
 699         // if the component already has focus, it won't
 700         // be notified.
 701         if (component.hasFocus()) {
 702             focusGained(null);
 703         }
 704 
 705         Number ratio = (Number) c.getClientProperty("caretAspectRatio");
 706         if (ratio != null) {
 707             aspectRatio = ratio.floatValue();
 708         } else {
 709             aspectRatio = -1;
 710         }
 711 
 712         Integer width = (Integer) c.getClientProperty("caretWidth");
 713         if (width != null) {
 714             caretWidth = width.intValue();
 715         } else {
 716             caretWidth = -1;
 717         }
 718     }
 719 
 720     /**
 721      * Called when the UI is being removed from the
 722      * interface of a JTextComponent.  This is used to
 723      * unregister any listeners that were attached.
 724      *
 725      * @param c the component
 726      * @see Caret#deinstall
 727      */
 728     public void deinstall(JTextComponent c) {
 729         c.removeMouseListener(this);
 730         c.removeMouseMotionListener(this);
 731         c.removeFocusListener(this);
 732         c.removePropertyChangeListener(handler);
 733         Document doc = c.getDocument();
 734         if (doc != null) {
 735             doc.removeDocumentListener(handler);
 736         }
 737         synchronized(this) {
 738             component = null;
 739         }
 740         if (flasher != null) {
 741             flasher.stop();
 742         }
 743 
 744 
 745     }
 746 
 747     /**
 748      * Adds a listener to track whenever the caret position has
 749      * been changed.
 750      *
 751      * @param l the listener
 752      * @see Caret#addChangeListener
 753      */
 754     public void addChangeListener(ChangeListener l) {
 755         listenerList.add(ChangeListener.class, l);
 756     }
 757 
 758     /**
 759      * Removes a listener that was tracking caret position changes.
 760      *
 761      * @param l the listener
 762      * @see Caret#removeChangeListener
 763      */
 764     public void removeChangeListener(ChangeListener l) {
 765         listenerList.remove(ChangeListener.class, l);
 766     }
 767 
 768     /**
 769      * Returns an array of all the change listeners
 770      * registered on this caret.
 771      *
 772      * @return all of this caret's <code>ChangeListener</code>s
 773      *         or an empty
 774      *         array if no change listeners are currently registered
 775      *
 776      * @see #addChangeListener
 777      * @see #removeChangeListener
 778      *
 779      * @since 1.4
 780      */
 781     public ChangeListener[] getChangeListeners() {
 782         return listenerList.getListeners(ChangeListener.class);
 783     }
 784 
 785     /**
 786      * Notifies all listeners that have registered interest for
 787      * notification on this event type.  The event instance
 788      * is lazily created using the parameters passed into
 789      * the fire method.  The listener list is processed last to first.
 790      *
 791      * @see EventListenerList
 792      */
 793     protected void fireStateChanged() {
 794         // Guaranteed to return a non-null array
 795         Object[] listeners = listenerList.getListenerList();
 796         // Process the listeners last to first, notifying
 797         // those that are interested in this event
 798         for (int i = listeners.length-2; i>=0; i-=2) {
 799             if (listeners[i]==ChangeListener.class) {
 800                 // Lazily create the event:
 801                 if (changeEvent == null)
 802                     changeEvent = new ChangeEvent(this);
 803                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
 804             }
 805         }
 806     }
 807 
 808     /**
 809      * Returns an array of all the objects currently registered
 810      * as <code><em>Foo</em>Listener</code>s
 811      * upon this caret.
 812      * <code><em>Foo</em>Listener</code>s are registered using the
 813      * <code>add<em>Foo</em>Listener</code> method.
 814      *
 815      * <p>
 816      *
 817      * You can specify the <code>listenerType</code> argument
 818      * with a class literal,
 819      * such as
 820      * <code><em>Foo</em>Listener.class</code>.
 821      * For example, you can query a
 822      * <code>DefaultCaret</code> <code>c</code>
 823      * for its change listeners with the following code:
 824      *
 825      * <pre>ChangeListener[] cls = (ChangeListener[])(c.getListeners(ChangeListener.class));</pre>
 826      *
 827      * If no such listeners exist, this method returns an empty array.
 828      *
 829      * @param listenerType the type of listeners requested; this parameter
 830      *          should specify an interface that descends from
 831      *          <code>java.util.EventListener</code>
 832      * @return an array of all objects registered as
 833      *          <code><em>Foo</em>Listener</code>s on this component,
 834      *          or an empty array if no such
 835      *          listeners have been added
 836      * @exception ClassCastException if <code>listenerType</code>
 837      *          doesn't specify a class or interface that implements
 838      *          <code>java.util.EventListener</code>
 839      *
 840      * @see #getChangeListeners
 841      *
 842      * @since 1.3
 843      */
 844     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 845         return listenerList.getListeners(listenerType);
 846     }
 847 
 848     /**
 849      * Changes the selection visibility.
 850      *
 851      * @param vis the new visibility
 852      */
 853     public void setSelectionVisible(boolean vis) {
 854         if (vis != selectionVisible) {
 855             selectionVisible = vis;
 856             if (selectionVisible) {
 857                 // show
 858                 Highlighter h = component.getHighlighter();
 859                 if ((dot != mark) && (h != null) && (selectionTag == null)) {
 860                     int p0 = Math.min(dot, mark);
 861                     int p1 = Math.max(dot, mark);
 862                     Highlighter.HighlightPainter p = getSelectionPainter();
 863                     try {
 864                         selectionTag = h.addHighlight(p0, p1, p);
 865                     } catch (BadLocationException bl) {
 866                         selectionTag = null;
 867                     }
 868                 }
 869             } else {
 870                 // hide
 871                 if (selectionTag != null) {
 872                     Highlighter h = component.getHighlighter();
 873                     h.removeHighlight(selectionTag);
 874                     selectionTag = null;
 875                 }
 876             }
 877         }
 878     }
 879 
 880     /**
 881      * Checks whether the current selection is visible.
 882      *
 883      * @return true if the selection is visible
 884      */
 885     public boolean isSelectionVisible() {
 886         return selectionVisible;
 887     }
 888 
 889     /**
 890      * Determines if the caret is currently active.
 891      * <p>
 892      * This method returns whether or not the <code>Caret</code>
 893      * is currently in a blinking state. It does not provide
 894      * information as to whether it is currently blinked on or off.
 895      * To determine if the caret is currently painted use the
 896      * <code>isVisible</code> method.
 897      *
 898      * @return <code>true</code> if active else <code>false</code>
 899      * @see #isVisible
 900      *
 901      * @since 1.5
 902      */
 903     public boolean isActive() {
 904         return active;
 905     }
 906 
 907     /**
 908      * Indicates whether or not the caret is currently visible. As the
 909      * caret flashes on and off the return value of this will change
 910      * between true, when the caret is painted, and false, when the
 911      * caret is not painted. <code>isActive</code> indicates whether
 912      * or not the caret is in a blinking state, such that it <b>can</b>
 913      * be visible, and <code>isVisible</code> indicates whether or not
 914      * the caret <b>is</b> actually visible.
 915      * <p>
 916      * Subclasses that wish to render a different flashing caret
 917      * should override paint and only paint the caret if this method
 918      * returns true.
 919      *
 920      * @return true if visible else false
 921      * @see Caret#isVisible
 922      * @see #isActive
 923      */
 924     public boolean isVisible() {
 925         return visible;
 926     }
 927 
 928     /**
 929      * Sets the caret visibility, and repaints the caret.
 930      * It is important to understand the relationship between this method,
 931      * <code>isVisible</code> and <code>isActive</code>.
 932      * Calling this method with a value of <code>true</code> activates the
 933      * caret blinking. Setting it to <code>false</code> turns it completely off.
 934      * To determine whether the blinking is active, you should call
 935      * <code>isActive</code>. In effect, <code>isActive</code> is an
 936      * appropriate corresponding "getter" method for this one.
 937      * <code>isVisible</code> can be used to fetch the current
 938      * visibility status of the caret, meaning whether or not it is currently
 939      * painted. This status will change as the caret blinks on and off.
 940      * <p>
 941      * Here's a list showing the potential return values of both
 942      * <code>isActive</code> and <code>isVisible</code>
 943      * after calling this method:
 944      * <p>
 945      * <b><code>setVisible(true)</code></b>:
 946      * <ul>
 947      *     <li>isActive(): true</li>
 948      *     <li>isVisible(): true or false depending on whether
 949      *         or not the caret is blinked on or off</li>
 950      * </ul>
 951      * <p>
 952      * <b><code>setVisible(false)</code></b>:
 953      * <ul>
 954      *     <li>isActive(): false</li>
 955      *     <li>isVisible(): false</li>
 956      * </ul>
 957      *
 958      * @param e the visibility specifier
 959      * @see #isActive
 960      * @see Caret#setVisible
 961      */
 962     public void setVisible(boolean e) {
 963         // focus lost notification can come in later after the
 964         // caret has been deinstalled, in which case the component
 965         // will be null.
 966         active = e;
 967         if (component != null) {
 968             TextUI mapper = component.getUI();
 969             if (visible != e) {
 970                 visible = e;
 971                 // repaint the caret
 972                 try {
 973                     Rectangle loc = mapper.modelToView(component, dot,dotBias);
 974                     damage(loc);
 975                 } catch (BadLocationException badloc) {
 976                     // hmm... not legally positioned
 977                 }
 978             }
 979         }
 980         if (flasher != null) {
 981             if (visible) {
 982                 flasher.start();
 983             } else {
 984                 flasher.stop();
 985             }
 986         }
 987     }
 988 
 989     /**
 990      * Sets the caret blink rate.
 991      *
 992      * @param rate the rate in milliseconds, 0 to stop blinking
 993      * @see Caret#setBlinkRate
 994      */
 995     public void setBlinkRate(int rate) {
 996         if (rate != 0) {
 997             if (flasher == null) {
 998                 flasher = new Timer(rate, handler);
 999             }
1000             flasher.setDelay(rate);
1001         } else {
1002             if (flasher != null) {
1003                 flasher.stop();
1004                 flasher.removeActionListener(handler);
1005                 flasher = null;
1006             }
1007         }
1008     }
1009 
1010     /**
1011      * Gets the caret blink rate.
1012      *
1013      * @return the delay in milliseconds.  If this is
1014      *  zero the caret will not blink.
1015      * @see Caret#getBlinkRate
1016      */
1017     public int getBlinkRate() {
1018         return (flasher == null) ? 0 : flasher.getDelay();
1019     }
1020 
1021     /**
1022      * Fetches the current position of the caret.
1023      *
1024      * @return the position &gt;= 0
1025      * @see Caret#getDot
1026      */
1027     public int getDot() {
1028         return dot;
1029     }
1030 
1031     /**
1032      * Fetches the current position of the mark.  If there is a selection,
1033      * the dot and mark will not be the same.
1034      *
1035      * @return the position &gt;= 0
1036      * @see Caret#getMark
1037      */
1038     public int getMark() {
1039         return mark;
1040     }
1041 
1042     /**
1043      * Sets the caret position and mark to the specified position,
1044      * with a forward bias. This implicitly sets the
1045      * selection range to zero.
1046      *
1047      * @param dot the position &gt;= 0
1048      * @see #setDot(int, Position.Bias)
1049      * @see Caret#setDot
1050      */
1051     public void setDot(int dot) {
1052         setDot(dot, Position.Bias.Forward);
1053     }
1054 
1055     /**
1056      * Moves the caret position to the specified position,
1057      * with a forward bias.
1058      *
1059      * @param dot the position &gt;= 0
1060      * @see #moveDot(int, javax.swing.text.Position.Bias)
1061      * @see Caret#moveDot
1062      */
1063     public void moveDot(int dot) {
1064         moveDot(dot, Position.Bias.Forward);
1065     }
1066 
1067     // ---- Bidi methods (we could put these in a subclass)
1068 
1069     /**
1070      * Moves the caret position to the specified position, with the
1071      * specified bias.
1072      *
1073      * @param dot the position &gt;= 0
1074      * @param dotBias the bias for this position, not <code>null</code>
1075      * @throws IllegalArgumentException if the bias is <code>null</code>
1076      * @see Caret#moveDot
1077      * @since 1.6
1078      */
1079     public void moveDot(int dot, Position.Bias dotBias) {
1080         if (dotBias == null) {
1081             throw new IllegalArgumentException("null bias");
1082         }
1083 
1084         if (! component.isEnabled()) {
1085             // don't allow selection on disabled components.
1086             setDot(dot, dotBias);
1087             return;
1088         }
1089         if (dot != this.dot) {
1090             NavigationFilter filter = component.getNavigationFilter();
1091 
1092             if (filter != null) {
1093                 filter.moveDot(getFilterBypass(), dot, dotBias);
1094             }
1095             else {
1096                 handleMoveDot(dot, dotBias);
1097             }
1098         }
1099     }
1100 
1101     void handleMoveDot(int dot, Position.Bias dotBias) {
1102         changeCaretPosition(dot, dotBias);
1103 
1104         if (selectionVisible) {
1105             Highlighter h = component.getHighlighter();
1106             if (h != null) {
1107                 int p0 = Math.min(dot, mark);
1108                 int p1 = Math.max(dot, mark);
1109 
1110                 // if p0 == p1 then there should be no highlight, remove it if necessary
1111                 if (p0 == p1) {
1112                     if (selectionTag != null) {
1113                         h.removeHighlight(selectionTag);
1114                         selectionTag = null;
1115                     }
1116                 // otherwise, change or add the highlight
1117                 } else {
1118                     try {
1119                         if (selectionTag != null) {
1120                             h.changeHighlight(selectionTag, p0, p1);
1121                         } else {
1122                             Highlighter.HighlightPainter p = getSelectionPainter();
1123                             selectionTag = h.addHighlight(p0, p1, p);
1124                         }
1125                     } catch (BadLocationException e) {
1126                         throw new StateInvariantError("Bad caret position");
1127                     }
1128                 }
1129             }
1130         }
1131     }
1132 
1133     /**
1134      * Sets the caret position and mark to the specified position, with the
1135      * specified bias. This implicitly sets the selection range
1136      * to zero.
1137      *
1138      * @param dot the position &gt;= 0
1139      * @param dotBias the bias for this position, not <code>null</code>
1140      * @throws IllegalArgumentException if the bias is <code>null</code>
1141      * @see Caret#setDot
1142      * @since 1.6
1143      */
1144     public void setDot(int dot, Position.Bias dotBias) {
1145         if (dotBias == null) {
1146             throw new IllegalArgumentException("null bias");
1147         }
1148 
1149         NavigationFilter filter = component.getNavigationFilter();
1150 
1151         if (filter != null) {
1152             filter.setDot(getFilterBypass(), dot, dotBias);
1153         }
1154         else {
1155             handleSetDot(dot, dotBias);
1156         }
1157     }
1158 
1159     void handleSetDot(int dot, Position.Bias dotBias) {
1160         // move dot, if it changed
1161         Document doc = component.getDocument();
1162         if (doc != null) {
1163             dot = Math.min(dot, doc.getLength());
1164         }
1165         dot = Math.max(dot, 0);
1166 
1167         // The position (0,Backward) is out of range so disallow it.
1168         if( dot == 0 )
1169             dotBias = Position.Bias.Forward;
1170 
1171         mark = dot;
1172         if (this.dot != dot || this.dotBias != dotBias ||
1173             selectionTag != null || forceCaretPositionChange) {
1174             changeCaretPosition(dot, dotBias);
1175         }
1176         this.markBias = this.dotBias;
1177         this.markLTR = dotLTR;
1178         Highlighter h = component.getHighlighter();
1179         if ((h != null) && (selectionTag != null)) {
1180             h.removeHighlight(selectionTag);
1181             selectionTag = null;
1182         }
1183     }
1184 
1185     /**
1186      * Returns the bias of the caret position.
1187      *
1188      * @return the bias of the caret position
1189      * @since 1.6
1190      */
1191     public Position.Bias getDotBias() {
1192         return dotBias;
1193     }
1194 
1195     /**
1196      * Returns the bias of the mark.
1197      *
1198      * @return the bias of the mark
1199      * @since 1.6
1200      */
1201     public Position.Bias getMarkBias() {
1202         return markBias;
1203     }
1204 
1205     boolean isDotLeftToRight() {
1206         return dotLTR;
1207     }
1208 
1209     boolean isMarkLeftToRight() {
1210         return markLTR;
1211     }
1212 
1213     boolean isPositionLTR(int position, Position.Bias bias) {
1214         Document doc = component.getDocument();
1215         if(bias == Position.Bias.Backward && --position < 0)
1216             position = 0;
1217         return AbstractDocument.isLeftToRight(doc, position, position);
1218     }
1219 
1220     Position.Bias guessBiasForOffset(int offset, Position.Bias lastBias,
1221                                      boolean lastLTR) {
1222         // There is an abiguous case here. That if your model looks like:
1223         // abAB with the cursor at abB]A (visual representation of
1224         // 3 forward) deleting could either become abB] or
1225         // ab[B. I'ld actually prefer abB]. But, if I implement that
1226         // a delete at abBA] would result in aBA] vs a[BA which I
1227         // think is totally wrong. To get this right we need to know what
1228         // was deleted. And we could get this from the bidi structure
1229         // in the change event. So:
1230         // PENDING: base this off what was deleted.
1231         if(lastLTR != isPositionLTR(offset, lastBias)) {
1232             lastBias = Position.Bias.Backward;
1233         }
1234         else if(lastBias != Position.Bias.Backward &&
1235                 lastLTR != isPositionLTR(offset, Position.Bias.Backward)) {
1236             lastBias = Position.Bias.Backward;
1237         }
1238         if (lastBias == Position.Bias.Backward && offset > 0) {
1239             try {
1240                 Segment s = new Segment();
1241                 component.getDocument().getText(offset - 1, 1, s);
1242                 if (s.count > 0 && s.array[s.offset] == '\n') {
1243                     lastBias = Position.Bias.Forward;
1244                 }
1245             }
1246             catch (BadLocationException ble) {}
1247         }
1248         return lastBias;
1249     }
1250 
1251     // ---- local methods --------------------------------------------
1252 
1253     /**
1254      * Sets the caret position (dot) to a new location.  This
1255      * causes the old and new location to be repainted.  It
1256      * also makes sure that the caret is within the visible
1257      * region of the view, if the view is scrollable.
1258      */
1259     void changeCaretPosition(int dot, Position.Bias dotBias) {
1260         // repaint the old position and set the new value of
1261         // the dot.
1262         repaint();
1263 
1264 
1265         // Make sure the caret is visible if this window has the focus.
1266         if (flasher != null && flasher.isRunning()) {
1267             visible = true;
1268             flasher.restart();
1269         }
1270 
1271         // notify listeners at the caret moved
1272         this.dot = dot;
1273         this.dotBias = dotBias;
1274         dotLTR = isPositionLTR(dot, dotBias);
1275         fireStateChanged();
1276 
1277         updateSystemSelection();
1278 
1279         setMagicCaretPosition(null);
1280 
1281         // We try to repaint the caret later, since things
1282         // may be unstable at the time this is called
1283         // (i.e. we don't want to depend upon notification
1284         // order or the fact that this might happen on
1285         // an unsafe thread).
1286         Runnable callRepaintNewCaret = new Runnable() {
1287             public void run() {
1288                 repaintNewCaret();
1289             }
1290         };
1291         SwingUtilities.invokeLater(callRepaintNewCaret);
1292     }
1293 
1294     /**
1295      * Repaints the new caret position, with the
1296      * assumption that this is happening on the
1297      * event thread so that calling <code>modelToView</code>
1298      * is safe.
1299      */
1300     void repaintNewCaret() {
1301         if (component != null) {
1302             TextUI mapper = component.getUI();
1303             Document doc = component.getDocument();
1304             if ((mapper != null) && (doc != null)) {
1305                 // determine the new location and scroll if
1306                 // not visible.
1307                 Rectangle newLoc;
1308                 try {
1309                     newLoc = mapper.modelToView(component, this.dot, this.dotBias);
1310                 } catch (BadLocationException e) {
1311                     newLoc = null;
1312                 }
1313                 if (newLoc != null) {
1314                     adjustVisibility(newLoc);
1315                     // If there is no magic caret position, make one
1316                     if (getMagicCaretPosition() == null) {
1317                         setMagicCaretPosition(new Point(newLoc.x, newLoc.y));
1318                     }
1319                 }
1320 
1321                 // repaint the new position
1322                 damage(newLoc);
1323             }
1324         }
1325     }
1326 
1327     private void updateSystemSelection() {
1328         if ( ! SwingUtilities2.canCurrentEventAccessSystemClipboard() ) {
1329             return;
1330         }
1331         if (this.dot != this.mark && component != null && component.hasFocus()) {
1332             Clipboard clip = getSystemSelection();
1333             if (clip != null) {
1334                 String selectedText;
1335                 if (component instanceof JPasswordField
1336                     && component.getClientProperty("JPasswordField.cutCopyAllowed") !=
1337                     Boolean.TRUE) {
1338                     //fix for 4793761
1339                     StringBuilder txt = null;
1340                     char echoChar = ((JPasswordField)component).getEchoChar();
1341                     int p0 = Math.min(getDot(), getMark());
1342                     int p1 = Math.max(getDot(), getMark());
1343                     for (int i = p0; i < p1; i++) {
1344                         if (txt == null) {
1345                             txt = new StringBuilder();
1346                         }
1347                         txt.append(echoChar);
1348                     }
1349                     selectedText = (txt != null) ? txt.toString() : null;
1350                 } else {
1351                     selectedText = component.getSelectedText();
1352                 }
1353                 try {
1354                     clip.setContents(
1355                         new StringSelection(selectedText), getClipboardOwner());
1356 
1357                     ownsSelection = true;
1358                 } catch (IllegalStateException ise) {
1359                     // clipboard was unavailable
1360                     // no need to provide error feedback to user since updating
1361                     // the system selection is not a user invoked action
1362                 }
1363             }
1364         }
1365     }
1366 
1367     private Clipboard getSystemSelection() {
1368         try {
1369             return component.getToolkit().getSystemSelection();
1370         } catch (HeadlessException he) {
1371             // do nothing... there is no system clipboard
1372         } catch (SecurityException se) {
1373             // do nothing... there is no allowed system clipboard
1374         }
1375         return null;
1376     }
1377 
1378     private ClipboardOwner getClipboardOwner() {
1379         return handler;
1380     }
1381 
1382     /**
1383      * This is invoked after the document changes to verify the current
1384      * dot/mark is valid. We do this in case the <code>NavigationFilter</code>
1385      * changed where to position the dot, that resulted in the current location
1386      * being bogus.
1387      */
1388     private void ensureValidPosition() {
1389         int length = component.getDocument().getLength();
1390         if (dot > length || mark > length) {
1391             // Current location is bogus and filter likely vetoed the
1392             // change, force the reset without giving the filter a
1393             // chance at changing it.
1394             handleSetDot(length, Position.Bias.Forward);
1395         }
1396     }
1397 
1398 
1399     /**
1400      * Saves the current caret position.  This is used when
1401      * caret up/down actions occur, moving between lines
1402      * that have uneven end positions.
1403      *
1404      * @param p the position
1405      * @see #getMagicCaretPosition
1406      */
1407     public void setMagicCaretPosition(Point p) {
1408         magicCaretPosition = p;
1409     }
1410 
1411     /**
1412      * Gets the saved caret position.
1413      *
1414      * @return the position
1415      * see #setMagicCaretPosition
1416      */
1417     public Point getMagicCaretPosition() {
1418         return magicCaretPosition;
1419     }
1420 
1421     /**
1422      * Compares this object to the specified object.
1423      * The superclass behavior of comparing rectangles
1424      * is not desired, so this is changed to the Object
1425      * behavior.
1426      *
1427      * @param     obj   the object to compare this font with
1428      * @return    <code>true</code> if the objects are equal;
1429      *            <code>false</code> otherwise
1430      */
1431     public boolean equals(Object obj) {
1432         return (this == obj);
1433     }
1434 
1435     public String toString() {
1436         String s = "Dot=(" + dot + ", " + dotBias + ")";
1437         s += " Mark=(" + mark + ", " + markBias + ")";
1438         return s;
1439     }
1440 
1441     private NavigationFilter.FilterBypass getFilterBypass() {
1442         if (filterBypass == null) {
1443             filterBypass = new DefaultFilterBypass();
1444         }
1445         return filterBypass;
1446     }
1447 
1448     // Rectangle.contains returns false if passed a rect with a w or h == 0,
1449     // this won't (assuming X,Y are contained with this rectangle).
1450     private boolean _contains(int X, int Y, int W, int H) {
1451         int w = this.width;
1452         int h = this.height;
1453         if ((w | h | W | H) < 0) {
1454             // At least one of the dimensions is negative...
1455             return false;
1456         }
1457         // Note: if any dimension is zero, tests below must return false...
1458         int x = this.x;
1459         int y = this.y;
1460         if (X < x || Y < y) {
1461             return false;
1462         }
1463         if (W > 0) {
1464             w += x;
1465             W += X;
1466             if (W <= X) {
1467                 // X+W overflowed or W was zero, return false if...
1468                 // either original w or W was zero or
1469                 // x+w did not overflow or
1470                 // the overflowed x+w is smaller than the overflowed X+W
1471                 if (w >= x || W > w) return false;
1472             } else {
1473                 // X+W did not overflow and W was not zero, return false if...
1474                 // original w was zero or
1475                 // x+w did not overflow and x+w is smaller than X+W
1476                 if (w >= x && W > w) return false;
1477             }
1478         }
1479         else if ((x + w) < X) {
1480             return false;
1481         }
1482         if (H > 0) {
1483             h += y;
1484             H += Y;
1485             if (H <= Y) {
1486                 if (h >= y || H > h) return false;
1487             } else {
1488                 if (h >= y && H > h) return false;
1489             }
1490         }
1491         else if ((y + h) < Y) {
1492             return false;
1493         }
1494         return true;
1495     }
1496 
1497     int getCaretWidth(int height) {
1498         if (aspectRatio > -1) {
1499             return (int) (aspectRatio * height) + 1;
1500         }
1501 
1502         if (caretWidth > -1) {
1503             return caretWidth;
1504         } else {
1505             Object property = UIManager.get("Caret.width");
1506             if (property instanceof Integer) {
1507                 return ((Integer) property).intValue();
1508             } else {
1509                 return 1;
1510             }
1511         }
1512     }
1513 
1514     // --- serialization ---------------------------------------------
1515 
1516     private void readObject(ObjectInputStream s)
1517       throws ClassNotFoundException, IOException
1518     {
1519         ObjectInputStream.GetField f = s.readFields();
1520 
1521         EventListenerList newListenerList = (EventListenerList) f.get("listenerList", null);
1522         if (newListenerList == null) {
1523             throw new InvalidObjectException("Null listenerList");
1524         }
1525         listenerList = newListenerList;
1526         component = (JTextComponent) f.get("component", null);
1527         updatePolicy = f.get("updatePolicy", 0);
1528         visible = f.get("visible", false);
1529         active = f.get("active", false);
1530         dot = f.get("dot", 0);
1531         mark = f.get("mark", 0);
1532         selectionTag = f.get("selectionTag", null);
1533         selectionVisible = f.get("selectionVisible", false);
1534         flasher = (Timer) f.get("flasher", null);
1535         magicCaretPosition = (Point) f.get("magicCaretPosition", null);
1536         dotLTR = f.get("dotLTR", false);
1537         markLTR = f.get("markLTR", false);
1538         ownsSelection = f.get("ownsSelection", false);
1539         forceCaretPositionChange = f.get("forceCaretPositionChange", false);
1540         caretWidth = f.get("caretWidth", 0);
1541         aspectRatio = f.get("aspectRatio", 0.0f);
1542 
1543         handler = new Handler();
1544         if (!s.readBoolean()) {
1545             dotBias = Position.Bias.Forward;
1546         }
1547         else {
1548             dotBias = Position.Bias.Backward;
1549         }
1550         if (!s.readBoolean()) {
1551             markBias = Position.Bias.Forward;
1552         }
1553         else {
1554             markBias = Position.Bias.Backward;
1555         }
1556     }
1557 
1558     private void writeObject(ObjectOutputStream s) throws IOException {
1559         s.defaultWriteObject();
1560         s.writeBoolean((dotBias == Position.Bias.Backward));
1561         s.writeBoolean((markBias == Position.Bias.Backward));
1562     }
1563 
1564     // ---- member variables ------------------------------------------
1565 
1566     /**
1567      * The event listener list.
1568      */
1569     protected EventListenerList listenerList = new EventListenerList();
1570 
1571     /**
1572      * The change event for the model.
1573      * Only one ChangeEvent is needed per model instance since the
1574      * event's only (read-only) state is the source property.  The source
1575      * of events generated here is always "this".
1576      */
1577     protected transient ChangeEvent changeEvent = null;
1578 
1579     // package-private to avoid inner classes private member
1580     // access bug
1581     JTextComponent component;
1582 
1583     int updatePolicy = UPDATE_WHEN_ON_EDT;
1584     boolean visible;
1585     boolean active;
1586     int dot;
1587     int mark;
1588     Object selectionTag;
1589     boolean selectionVisible;
1590     Timer flasher;
1591     Point magicCaretPosition;
1592     transient Position.Bias dotBias;
1593     transient Position.Bias markBias;
1594     boolean dotLTR;
1595     boolean markLTR;
1596     transient Handler handler = new Handler();
1597     transient private int[] flagXPoints = new int[3];
1598     transient private int[] flagYPoints = new int[3];
1599     private transient NavigationFilter.FilterBypass filterBypass;
1600     static private transient Action selectWord = null;
1601     static private transient Action selectLine = null;
1602     /**
1603      * This is used to indicate if the caret currently owns the selection.
1604      * This is always false if the system does not support the system
1605      * clipboard.
1606      */
1607     private boolean ownsSelection;
1608 
1609     /**
1610      * If this is true, the location of the dot is updated regardless of
1611      * the current location. This is set in the DocumentListener
1612      * such that even if the model location of dot hasn't changed (perhaps do
1613      * to a forward delete) the visual location is updated.
1614      */
1615     private boolean forceCaretPositionChange;
1616 
1617     /**
1618      * Whether or not mouseReleased should adjust the caret and focus.
1619      * This flag is set by mousePressed if it wanted to adjust the caret
1620      * and focus but couldn't because of a possible DnD operation.
1621      */
1622     private transient boolean shouldHandleRelease;
1623 
1624 
1625     /**
1626      * holds last MouseEvent which caused the word selection
1627      */
1628     private transient MouseEvent selectedWordEvent = null;
1629 
1630     /**
1631      * The width of the caret in pixels.
1632      */
1633     private int caretWidth = -1;
1634     private float aspectRatio = -1;
1635 
1636     class SafeScroller implements Runnable {
1637 
1638         SafeScroller(Rectangle r) {
1639             this.r = r;
1640         }
1641 
1642         public void run() {
1643             if (component != null) {
1644                 component.scrollRectToVisible(r);
1645             }
1646         }
1647 
1648         Rectangle r;
1649     }
1650 
1651 
1652     class Handler implements PropertyChangeListener, DocumentListener, ActionListener, ClipboardOwner {
1653 
1654         // --- ActionListener methods ----------------------------------
1655 
1656         /**
1657          * Invoked when the blink timer fires.  This is called
1658          * asynchronously.  The simply changes the visibility
1659          * and repaints the rectangle that last bounded the caret.
1660          *
1661          * @param e the action event
1662          */
1663         public void actionPerformed(ActionEvent e) {
1664             if (width == 0 || height == 0) {
1665                 // setVisible(true) will cause a scroll, only do this if the
1666                 // new location is really valid.
1667                 if (component != null) {
1668                     TextUI mapper = component.getUI();
1669                     try {
1670                         Rectangle r = mapper.modelToView(component, dot,
1671                                                          dotBias);
1672                         if (r != null && r.width != 0 && r.height != 0) {
1673                             damage(r);
1674                         }
1675                     } catch (BadLocationException ble) {
1676                     }
1677                 }
1678             }
1679             visible = !visible;
1680             repaint();
1681         }
1682 
1683         // --- DocumentListener methods --------------------------------
1684 
1685         /**
1686          * Updates the dot and mark if they were changed by
1687          * the insertion.
1688          *
1689          * @param e the document event
1690          * @see DocumentListener#insertUpdate
1691          */
1692         public void insertUpdate(DocumentEvent e) {
1693             if (getUpdatePolicy() == NEVER_UPDATE ||
1694                     (getUpdatePolicy() == UPDATE_WHEN_ON_EDT &&
1695                     !SwingUtilities.isEventDispatchThread())) {
1696 
1697                 if ((e.getOffset() <= dot || e.getOffset() <= mark)
1698                         && selectionTag != null) {
1699                     try {
1700                         component.getHighlighter().changeHighlight(selectionTag,
1701                                 Math.min(dot, mark), Math.max(dot, mark));
1702                     } catch (BadLocationException e1) {
1703                         e1.printStackTrace();
1704                     }
1705                 }
1706                 return;
1707             }
1708             int offset = e.getOffset();
1709             int length = e.getLength();
1710             int newDot = dot;
1711             short changed = 0;
1712 
1713             if (e instanceof AbstractDocument.UndoRedoDocumentEvent) {
1714                 setDot(offset + length);
1715                 return;
1716             }
1717             if (newDot >= offset) {
1718                 newDot += length;
1719                 changed |= 1;
1720             }
1721             int newMark = mark;
1722             if (newMark >= offset) {
1723                 newMark += length;
1724                 changed |= 2;
1725             }
1726 
1727             if (changed != 0) {
1728                 Position.Bias dotBias = DefaultCaret.this.dotBias;
1729                 if (dot == offset) {
1730                     Document doc = component.getDocument();
1731                     boolean isNewline;
1732                     try {
1733                         Segment s = new Segment();
1734                         doc.getText(newDot - 1, 1, s);
1735                         isNewline = (s.count > 0 &&
1736                                 s.array[s.offset] == '\n');
1737                     } catch (BadLocationException ble) {
1738                         isNewline = false;
1739                     }
1740                     if (isNewline) {
1741                         dotBias = Position.Bias.Forward;
1742                     } else {
1743                         dotBias = Position.Bias.Backward;
1744                     }
1745                 }
1746                 if (newMark == newDot) {
1747                     setDot(newDot, dotBias);
1748                     ensureValidPosition();
1749                 }
1750                 else {
1751                     setDot(newMark, markBias);
1752                     if (getDot() == newMark) {
1753                         // Due this test in case the filter vetoed the
1754                         // change in which case this probably won't be
1755                         // valid either.
1756                         moveDot(newDot, dotBias);
1757                     }
1758                     ensureValidPosition();
1759                 }
1760             }
1761         }
1762 
1763         /**
1764          * Updates the dot and mark if they were changed
1765          * by the removal.
1766          *
1767          * @param e the document event
1768          * @see DocumentListener#removeUpdate
1769          */
1770         public void removeUpdate(DocumentEvent e) {
1771             if (getUpdatePolicy() == NEVER_UPDATE ||
1772                     (getUpdatePolicy() == UPDATE_WHEN_ON_EDT &&
1773                     !SwingUtilities.isEventDispatchThread())) {
1774 
1775                 int length = component.getDocument().getLength();
1776                 dot = Math.min(dot, length);
1777                 mark = Math.min(mark, length);
1778                 if ((e.getOffset() < dot || e.getOffset() < mark)
1779                         && selectionTag != null) {
1780                     try {
1781                         component.getHighlighter().changeHighlight(selectionTag,
1782                                 Math.min(dot, mark), Math.max(dot, mark));
1783                     } catch (BadLocationException e1) {
1784                         e1.printStackTrace();
1785                     }
1786                 }
1787                 return;
1788             }
1789             int offs0 = e.getOffset();
1790             int offs1 = offs0 + e.getLength();
1791             int newDot = dot;
1792             boolean adjustDotBias = false;
1793             int newMark = mark;
1794             boolean adjustMarkBias = false;
1795 
1796             if(e instanceof AbstractDocument.UndoRedoDocumentEvent) {
1797                 setDot(offs0);
1798                 return;
1799             }
1800             if (newDot >= offs1) {
1801                 newDot -= (offs1 - offs0);
1802                 if(newDot == offs1) {
1803                     adjustDotBias = true;
1804                 }
1805             } else if (newDot >= offs0) {
1806                 newDot = offs0;
1807                 adjustDotBias = true;
1808             }
1809             if (newMark >= offs1) {
1810                 newMark -= (offs1 - offs0);
1811                 if(newMark == offs1) {
1812                     adjustMarkBias = true;
1813                 }
1814             } else if (newMark >= offs0) {
1815                 newMark = offs0;
1816                 adjustMarkBias = true;
1817             }
1818             if (newMark == newDot) {
1819                 forceCaretPositionChange = true;
1820                 try {
1821                     setDot(newDot, guessBiasForOffset(newDot, dotBias,
1822                             dotLTR));
1823                 } finally {
1824                     forceCaretPositionChange = false;
1825                 }
1826                 ensureValidPosition();
1827             } else {
1828                 Position.Bias dotBias = DefaultCaret.this.dotBias;
1829                 Position.Bias markBias = DefaultCaret.this.markBias;
1830                 if(adjustDotBias) {
1831                     dotBias = guessBiasForOffset(newDot, dotBias, dotLTR);
1832                 }
1833                 if(adjustMarkBias) {
1834                     markBias = guessBiasForOffset(mark, markBias, markLTR);
1835                 }
1836                 setDot(newMark, markBias);
1837                 if (getDot() == newMark) {
1838                     // Due this test in case the filter vetoed the change
1839                     // in which case this probably won't be valid either.
1840                     moveDot(newDot, dotBias);
1841                 }
1842                 ensureValidPosition();
1843             }
1844         }
1845 
1846         /**
1847          * Gives notification that an attribute or set of attributes changed.
1848          *
1849          * @param e the document event
1850          * @see DocumentListener#changedUpdate
1851          */
1852         public void changedUpdate(DocumentEvent e) {
1853             if (getUpdatePolicy() == NEVER_UPDATE ||
1854                     (getUpdatePolicy() == UPDATE_WHEN_ON_EDT &&
1855                     !SwingUtilities.isEventDispatchThread())) {
1856                 return;
1857             }
1858             if(e instanceof AbstractDocument.UndoRedoDocumentEvent) {
1859                 setDot(e.getOffset() + e.getLength());
1860             }
1861         }
1862 
1863         // --- PropertyChangeListener methods -----------------------
1864 
1865         /**
1866          * This method gets called when a bound property is changed.
1867          * We are looking for document changes on the editor.
1868          */
1869         public void propertyChange(PropertyChangeEvent evt) {
1870             Object oldValue = evt.getOldValue();
1871             Object newValue = evt.getNewValue();
1872             if ((oldValue instanceof Document) || (newValue instanceof Document)) {
1873                 setDot(0);
1874                 if (oldValue != null) {
1875                     ((Document)oldValue).removeDocumentListener(this);
1876                 }
1877                 if (newValue != null) {
1878                     ((Document)newValue).addDocumentListener(this);
1879                 }
1880             } else if("enabled".equals(evt.getPropertyName())) {
1881                 Boolean enabled = (Boolean) evt.getNewValue();
1882                 if(component.isFocusOwner()) {
1883                     if(enabled == Boolean.TRUE) {
1884                         if(component.isEditable()) {
1885                             setVisible(true);
1886                         }
1887                         setSelectionVisible(true);
1888                     } else {
1889                         setVisible(false);
1890                         setSelectionVisible(false);
1891                     }
1892                 }
1893             } else if("caretWidth".equals(evt.getPropertyName())) {
1894                 Integer newWidth = (Integer) evt.getNewValue();
1895                 if (newWidth != null) {
1896                     caretWidth = newWidth.intValue();
1897                 } else {
1898                     caretWidth = -1;
1899                 }
1900                 repaint();
1901             } else if("caretAspectRatio".equals(evt.getPropertyName())) {
1902                 Number newRatio = (Number) evt.getNewValue();
1903                 if (newRatio != null) {
1904                     aspectRatio = newRatio.floatValue();
1905                 } else {
1906                     aspectRatio = -1;
1907                 }
1908                 repaint();
1909             }
1910         }
1911 
1912 
1913         //
1914         // ClipboardOwner
1915         //
1916         /**
1917          * Toggles the visibility of the selection when ownership is lost.
1918          */
1919         public void lostOwnership(Clipboard clipboard,
1920                                       Transferable contents) {
1921             if (ownsSelection) {
1922                 ownsSelection = false;
1923                 if (component != null && !component.hasFocus()) {
1924                     setSelectionVisible(false);
1925                 }
1926             }
1927         }
1928     }
1929 
1930 
1931     private class DefaultFilterBypass extends NavigationFilter.FilterBypass {
1932         public Caret getCaret() {
1933             return DefaultCaret.this;
1934         }
1935 
1936         public void setDot(int dot, Position.Bias bias) {
1937             handleSetDot(dot, bias);
1938         }
1939 
1940         public void moveDot(int dot, Position.Bias bias) {
1941             handleMoveDot(dot, bias);
1942         }
1943     }
1944 }