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