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