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