KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > DefaultCaret


1 /*
2  * @(#)DefaultCaret.java 1.141 06/04/18
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.awt.*;
10 import java.awt.event.*;
11 import java.awt.datatransfer.*;
12 import java.beans.*;
13 import java.awt.event.ActionEvent JavaDoc;
14 import java.awt.event.ActionListener JavaDoc;
15 import java.io.*;
16 import javax.swing.*;
17 import javax.swing.event.*;
18 import javax.swing.plaf.*;
19 import java.util.EventListener JavaDoc;
20 import com.sun.java.swing.SwingUtilities2;
21
22 /**
23  * A default implementation of Caret. The caret is rendered as
24  * a vertical line in the color specified by the CaretColor property
25  * of the associated JTextComponent. It can blink at the rate specified
26  * by the BlinkRate property.
27  * <p>
28  * This implementation expects two sources of asynchronous notification.
29  * The timer thread fires asynchronously, and causes the caret to simply
30  * repaint the most recent bounding box. The caret also tracks change
31  * as the document is modified. Typically this will happen on the
32  * event dispatch thread as a result of some mouse or keyboard event.
33  * The caret behavior on both synchronous and asynchronous documents updates
34  * is controlled by <code>UpdatePolicy</code> property. The repaint of the
35  * new caret location will occur on the event thread in any case, as calls to
36  * <code>modelToView</code> are only safe on the event thread.
37  * <p>
38  * The caret acts as a mouse and focus listener on the text component
39  * it has been installed in, and defines the caret semantics based upon
40  * those events. The listener methods can be reimplemented to change the
41  * semantics.
42  * By default, the first mouse button will be used to set focus and caret
43  * position. Dragging the mouse pointer with the first mouse button will
44  * sweep out a selection that is contiguous in the model. If the associated
45  * text component is editable, the caret will become visible when focus
46  * is gained, and invisible when focus is lost.
47  * <p>
48  * The Highlighter bound to the associated text component is used to
49  * render the selection by default.
50  * Selection appearance can be customized by supplying a
51  * painter to use for the highlights. By default a painter is used that
52  * will render a solid color as specified in the associated text component
53  * in the <code>SelectionColor</code> property. This can easily be changed
54  * by reimplementing the
55  * <a HREF="#getSelectionHighlighter">getSelectionHighlighter</a>
56  * method.
57  * <p>
58  * A customized caret appearance can be achieved by reimplementing
59  * the paint method. If the paint method is changed, the damage method
60  * should also be reimplemented to cause a repaint for the area needed
61  * to render the caret. The caret extends the Rectangle class which
62  * is used to hold the bounding box for where the caret was last rendered.
63  * This enables the caret to repaint in a thread-safe manner when the
64  * caret moves without making a call to modelToView which is unstable
65  * between model updates and view repair (i.e. the order of delivery
66  * to DocumentListeners is not guaranteed).
67  * <p>
68  * The magic caret position is set to null when the caret position changes.
69  * A timer is used to determine the new location (after the caret change).
70  * When the timer fires, if the magic caret position is still null it is
71  * reset to the current caret position. Any actions that change
72  * the caret position and want the magic caret position to remain the
73  * same, must remember the magic caret position, change the cursor, and
74  * then set the magic caret position to its original value. This has the
75  * benefit that only actions that want the magic caret position to persist
76  * (such as open/down) need to know about it.
77  * <p>
78  * <strong>Warning:</strong>
79  * Serialized objects of this class will not be compatible with
80  * future Swing releases. The current serialization support is
81  * appropriate for short term storage or RMI between applications running
82  * the same version of Swing. As of 1.4, support for long term storage
83  * of all JavaBeans<sup><font size="-2">TM</font></sup>
84  * has been added to the <code>java.beans</code> package.
85  * Please see {@link java.beans.XMLEncoder}.
86  *
87  * @author Timothy Prinzing
88  * @version 1.141 04/18/06
89  * @see Caret
90  */

91 public class DefaultCaret extends Rectangle implements Caret JavaDoc, FocusListener, MouseListener, MouseMotionListener {
92
93     /**
94      * Indicates that the caret position is to be updated only when
95      * document changes are performed on the Event Dispatching Thread.
96      * @see #setUpdatePolicy
97      * @see #getUpdatePolicy
98      * @since 1.5
99      */

100     public static final int UPDATE_WHEN_ON_EDT = 0;
101
102     /**
103      * Indicates that the caret should remain at the same
104      * absolute position in the document regardless of any document
105      * updates, except when the document length becomes less than
106      * the current caret position due to removal. In that case the caret
107      * position is adjusted to the end of the document.
108      *
109      * @see #setUpdatePolicy
110      * @see #getUpdatePolicy
111      * @since 1.5
112      */

113     public static final int NEVER_UPDATE = 1;
114
115     /**
116      * Indicates that the caret position is to be <b>always</b>
117      * updated accordingly to the document changes regardless whether
118      * the document updates are performed on the Event Dispatching Thread
119      * or not.
120      *
121      * @see #setUpdatePolicy
122      * @see #getUpdatePolicy
123      * @since 1.5
124      */

125     public static final int ALWAYS_UPDATE = 2;
126
127     /**
128      * Constructs a default caret.
129      */

130     public DefaultCaret() {
131     }
132
133     /**
134      * Sets the caret movement policy on the document updates. Normally
135      * the caret updates its absolute position within the document on
136      * insertions occurred before or at the caret position and
137      * on removals before the caret position. 'Absolute position'
138      * means here the position relative to the start of the document.
139      * For example if
140      * a character is typed within editable text component it is inserted
141      * at the caret position and the caret moves to the next absolute
142      * position within the document due to insertion and if
143      * <code>BACKSPACE</code> is typed then caret decreases its absolute
144      * position due to removal of a character before it. Sometimes
145      * it may be useful to turn off the caret position updates so that
146      * the caret stays at the same absolute position within the
147      * document position regardless of any document updates.
148      * <p>
149      * The following update policies are allowed:
150      * <ul>
151      * <li><code>NEVER_UPDATE</code>: the caret stays at the same
152      * absolute position in the document regardless of any document
153      * updates, except when document length becomes less than
154      * the current caret position due to removal. In that case caret
155      * position is adjusted to the end of the document.
156      * The caret doesn't try to keep itself visible by scrolling
157      * the associated view when using this policy. </li>
158      * <li><code>ALWAYS_UPDATE</code>: the caret always tracks document
159      * changes. For regular changes it increases its position
160      * if an insertion occurs before or at its current position,
161      * and decreases position if a removal occurs before
162      * its current position. For undo/redo updates it is always
163      * moved to the position where update occurred. The caret
164      * also tries to keep itself visible by calling
165      * <code>adjustVisibility</code> method.</li>
166      * <li><code>UPDATE_WHEN_ON_EDT</code>: acts like <code>ALWAYS_UPDATE</code>
167      * if the document updates are performed on the Event Dispatching Thread
168      * and like <code>NEVER_UPDATE</code> if updates are performed on
169      * other thread. </li>
170      * </ul> <p>
171      * The default property value is <code>UPDATE_WHEN_ON_EDT</code>.
172      *
173      * @param policy one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,
174      * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>
175      * @throws IllegalArgumentException if invalid value is passed
176      *
177      * @see #getUpdatePolicy
178      * @see #adjustVisibility
179      * @see #UPDATE_WHEN_ON_EDT
180      * @see #NEVER_UPDATE
181      * @see #ALWAYS_UPDATE
182      *
183      * @since 1.5
184      */

185     public void setUpdatePolicy(int policy) {
186         updatePolicy = policy;
187     }
188
189     /**
190      * Gets the caret movement policy on document updates.
191      *
192      * @return one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,
193      * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>
194      *
195      * @see #setUpdatePolicy
196      * @see #UPDATE_WHEN_ON_EDT
197      * @see #NEVER_UPDATE
198      * @see #ALWAYS_UPDATE
199      *
200      * @since 1.5
201      */

202     public int getUpdatePolicy() {
203         return updatePolicy;
204     }
205
206     /**
207      * Gets the text editor component that this caret is
208      * is bound to.
209      *
210      * @return the component
211      */

212     protected final JTextComponent JavaDoc getComponent() {
213     return component;
214     }
215
216     /**
217      * Cause the caret to be painted. The repaint
218      * area is the bounding box of the caret (i.e.
219      * the caret rectangle or <em>this</em>).
220      * <p>
221      * This method is thread safe, although most Swing methods
222      * are not. Please see
223      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">
224      * Threads and Swing</A> for more information.
225      */

226     protected final synchronized void repaint() {
227     if (component != null) {
228         component.repaint(x, y, width, height);
229     }
230     }
231
232     /**
233      * Damages the area surrounding the caret to cause
234      * it to be repainted in a new location. If paint()
235      * is reimplemented, this method should also be
236      * reimplemented. This method should update the
237      * caret bounds (x, y, width, and height).
238      *
239      * @param r the current location of the caret
240      * @see #paint
241      */

242     protected synchronized void damage(Rectangle r) {
243         if (r != null) {
244             int damageWidth = getCaretWidth(r.height);
245             x = r.x - 4 - (damageWidth >> 1);
246             y = r.y;
247             width = 9 + damageWidth;
248             height = r.height;
249             repaint();
250         }
251     }
252
253     /**
254      * Scrolls the associated view (if necessary) to make
255      * the caret visible. Since how this should be done
256      * is somewhat of a policy, this method can be
257      * reimplemented to change the behavior. By default
258      * the scrollRectToVisible method is called on the
259      * associated component.
260      *
261      * @param nloc the new position to scroll to
262      */

263     protected void adjustVisibility(Rectangle nloc) {
264         if(component == null) {
265             return;
266         }
267         if (SwingUtilities.isEventDispatchThread()) {
268                 component.scrollRectToVisible(nloc);
269         } else {
270             SwingUtilities.invokeLater(new SafeScroller(nloc));
271         }
272     }
273
274     /**
275      * Gets the painter for the Highlighter.
276      *
277      * @return the painter
278      */

279     protected Highlighter.HighlightPainter JavaDoc getSelectionPainter() {
280     return DefaultHighlighter.DefaultPainter;
281     }
282
283     /**
284      * Tries to set the position of the caret from
285      * the coordinates of a mouse event, using viewToModel().
286      *
287      * @param e the mouse event
288      */

289     protected void positionCaret(MouseEvent e) {
290     Point pt = new Point(e.getX(), e.getY());
291     Position.Bias JavaDoc[] biasRet = new Position.Bias JavaDoc[1];
292     int pos = component.getUI().viewToModel(component, pt, biasRet);
293     if(biasRet[0] == null)
294         biasRet[0] = Position.Bias.Forward;
295     if (pos >= 0) {
296         setDot(pos, biasRet[0]);
297     }
298     }
299
300     /**
301      * Tries to move the position of the caret from
302      * the coordinates of a mouse event, using viewToModel().
303      * This will cause a selection if the dot and mark
304      * are different.
305      *
306      * @param e the mouse event
307      */

308     protected void moveCaret(MouseEvent e) {
309     Point pt = new Point(e.getX(), e.getY());
310     Position.Bias JavaDoc[] biasRet = new Position.Bias JavaDoc[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         moveDot(pos, biasRet[0]);
316     }
317     }
318
319     // --- FocusListener methods --------------------------
320

321     /**
322      * Called when the component containing the caret gains
323      * focus. This is implemented to set the caret to visible
324      * if the component is editable.
325      *
326      * @param e the focus event
327      * @see FocusListener#focusGained
328      */

329     public void focusGained(FocusEvent e) {
330     if (component.isEnabled()) {
331         if (component.isEditable()) {
332         setVisible(true);
333         }
334         setSelectionVisible(true);
335     }
336     }
337
338     /**
339      * Called when the component containing the caret loses
340      * focus. This is implemented to set the caret to visibility
341      * to false.
342      *
343      * @param e the focus event
344      * @see FocusListener#focusLost
345      */

346     public void focusLost(FocusEvent e) {
347     setVisible(false);
348         setSelectionVisible(ownsSelection || e.isTemporary());
349     }
350
351     
352     /**
353      * Selects word based on the MouseEvent
354      */

355     private void selectWord(MouseEvent e) {
356         if (selectedWordEvent != null
357             && selectedWordEvent.getX() == e.getX()
358             && selectedWordEvent.getY() == e.getY()) {
359             //we already done selection for this
360
return;
361         }
362             Action a = null;
363             ActionMap map = getComponent().getActionMap();
364             if (map != null) {
365             a = map.get(DefaultEditorKit.selectWordAction);
366             }
367             if (a == null) {
368             if (selectWord == null) {
369                 selectWord = new DefaultEditorKit.SelectWordAction JavaDoc();
370             }
371             a = selectWord;
372             }
373             a.actionPerformed(new ActionEvent JavaDoc(getComponent(),
374                               ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));
375         selectedWordEvent = e;
376     }
377
378     // --- MouseListener methods -----------------------------------
379

380     /**
381      * Called when the mouse is clicked. If the click was generated
382      * from button1, a double click selects a word,
383      * and a triple click the current line.
384      *
385      * @param e the mouse event
386      * @see MouseListener#mouseClicked
387      */

388     public void mouseClicked(MouseEvent e) {
389         int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);
390
391     if (! e.isConsumed()) {
392         if (SwingUtilities.isLeftMouseButton(e)) {
393         // mouse 1 behavior
394
if(nclicks == 1) {
395                     selectedWordEvent = null;
396                 } else if(nclicks == 2
397                           && SwingUtilities2.canEventAccessSystemClipboard(e)) {
398                     selectWord(e);
399                     selectedWordEvent = null;
400         } else if(nclicks == 3
401                           && SwingUtilities2.canEventAccessSystemClipboard(e)) {
402             Action a = null;
403             ActionMap map = getComponent().getActionMap();
404             if (map != null) {
405             a = map.get(DefaultEditorKit.selectLineAction);
406             }
407             if (a == null) {
408             if (selectLine == null) {
409                 selectLine = new DefaultEditorKit.SelectLineAction JavaDoc();
410             }
411             a = selectLine;
412             }
413             a.actionPerformed(new ActionEvent JavaDoc(getComponent(),
414                               ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));
415         }
416         } else if (SwingUtilities.isMiddleMouseButton(e)) {
417         // mouse 2 behavior
418
if (nclicks == 1 && component.isEditable() && component.isEnabled()
419                     && SwingUtilities2.canEventAccessSystemClipboard(e)) {
420             // paste system selection, if it exists
421
JTextComponent JavaDoc c = (JTextComponent JavaDoc) e.getSource();
422             if (c != null) {
423             try {
424                 Toolkit tk = c.getToolkit();
425                 Clipboard buffer = tk.getSystemSelection();
426                 if (buffer != null) {
427                 // platform supports system selections, update it.
428
adjustCaret(e);
429                 TransferHandler th = c.getTransferHandler();
430                 if (th != null) {
431                                     Transferable trans = null;
432
433                                     try {
434                                         trans = buffer.getContents(null);
435                                     } catch (IllegalStateException JavaDoc ise) {
436                                         // clipboard was unavailable
437
UIManager.getLookAndFeel().provideErrorFeedback(c);
438                                     }
439
440                                     if (trans != null) {
441                                         th.importData(c, trans);
442                                     }
443                 }
444                                 adjustFocus(true);
445                 }
446             } catch (HeadlessException he) {
447                 // do nothing... there is no system clipboard
448
}
449             }
450         }
451         }
452     }
453     }
454
455     /**
456      * If button 1 is pressed, this is implemented to
457      * request focus on the associated text component,
458      * and to set the caret position. If the shift key is held down,
459      * the caret will be moved, potentially resulting in a selection,
460      * otherwise the
461      * caret position will be set to the new location. If the component
462      * is not enabled, there will be no request for focus.
463      *
464      * @param e the mouse event
465      * @see MouseListener#mousePressed
466      */

467     public void mousePressed(MouseEvent e) {
468         int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);
469
470         if (SwingUtilities.isLeftMouseButton(e)) {
471             if (e.isConsumed()) {
472                 shouldHandleRelease = true;
473             } else {
474                 shouldHandleRelease = false;
475                 adjustCaretAndFocus(e);
476                 if (nclicks == 2
477                     && SwingUtilities2.canEventAccessSystemClipboard(e)) {
478                     selectWord(e);
479                 }
480             }
481         }
482     }
483
484     void adjustCaretAndFocus(MouseEvent e) {
485         adjustCaret(e);
486         adjustFocus(false);
487     }
488
489     /**
490      * Adjusts the caret location based on the MouseEvent.
491      */

492     private void adjustCaret(MouseEvent e) {
493     if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0 &&
494         getDot() != -1) {
495         moveCaret(e);
496     } else {
497         positionCaret(e);
498     }
499     }
500
501     /**
502      * Adjusts the focus, if necessary.
503      *
504      * @param inWindow if true indicates requestFocusInWindow should be used
505      */

506     private void adjustFocus(boolean inWindow) {
507     if ((component != null) && component.isEnabled() &&
508                                    component.isRequestFocusEnabled()) {
509             if (inWindow) {
510                 component.requestFocusInWindow();
511             }
512             else {
513                 component.requestFocus();
514             }
515     }
516     }
517
518     /**
519      * Called when the mouse is released.
520      *
521      * @param e the mouse event
522      * @see MouseListener#mouseReleased
523      */

524     public void mouseReleased(MouseEvent e) {
525         if (!e.isConsumed()
526                 && shouldHandleRelease
527                 && SwingUtilities.isLeftMouseButton(e)) {
528
529             adjustCaretAndFocus(e);
530         }
531     }
532
533     /**
534      * Called when the mouse enters a region.
535      *
536      * @param e the mouse event
537      * @see MouseListener#mouseEntered
538      */

539     public void mouseEntered(MouseEvent e) {
540     }
541
542     /**
543      * Called when the mouse exits a region.
544      *
545      * @param e the mouse event
546      * @see MouseListener#mouseExited
547      */

548     public void mouseExited(MouseEvent e) {
549     }
550
551     // --- MouseMotionListener methods -------------------------
552

553     /**
554      * Moves the caret position
555      * according to the mouse pointer's current
556      * location. This effectively extends the
557      * selection. By default, this is only done
558      * for mouse button 1.
559      *
560      * @param e the mouse event
561      * @see MouseMotionListener#mouseDragged
562      */

563     public void mouseDragged(MouseEvent e) {
564     if ((! e.isConsumed()) && SwingUtilities.isLeftMouseButton(e)) {
565         moveCaret(e);
566     }
567     }
568
569     /**
570      * Called when the mouse is moved.
571      *
572      * @param e the mouse event
573      * @see MouseMotionListener#mouseMoved
574      */

575     public void mouseMoved(MouseEvent e) {
576     }
577
578     // ---- Caret methods ---------------------------------
579

580     /**
581      * Renders the caret as a vertical line. If this is reimplemented
582      * the damage method should also be reimplemented as it assumes the
583      * shape of the caret is a vertical line. Sets the caret color to
584      * the value returned by getCaretColor().
585      * <p>
586      * If there are multiple text directions present in the associated
587      * document, a flag indicating the caret bias will be rendered.
588      * This will occur only if the associated document is a subclass
589      * of AbstractDocument and there are multiple bidi levels present
590      * in the bidi element structure (i.e. the text has multiple
591      * directions associated with it).
592      *
593      * @param g the graphics context
594      * @see #damage
595      */

596     public void paint(Graphics g) {
597     if(isVisible()) {
598         try {
599         TextUI mapper = component.getUI();
600         Rectangle r = mapper.modelToView(component, dot, dotBias);
601
602                 if ((r == null) || ((r.width == 0) && (r.height == 0))) {
603                     return;
604                 }
605                 if (width > 0 && height > 0 &&
606                                 !this._contains(r.x, r.y, r.width, r.height)) {
607                     // We seem to have gotten out of sync and no longer
608
// contain the right location, adjust accordingly.
609
Rectangle clip = g.getClipBounds();
610
611                     if (clip != null && !clip.contains(this)) {
612                         // Clip doesn't contain the old location, force it
613
// to be repainted lest we leave a caret around.
614
repaint();
615                     }
616                     // This will potentially cause a repaint of something
617
// we're already repainting, but without changing the
618
// semantics of damage we can't really get around this.
619
damage(r);
620                 }
621         g.setColor(component.getCaretColor());
622                 int paintWidth = getCaretWidth(r.height);
623                 r.x -= paintWidth >> 1;
624                 g.fillRect(r.x, r.y, paintWidth , r.height - 1);
625
626         // see if we should paint a flag to indicate the bias
627
// of the caret.
628
// PENDING(prinz) this should be done through
629
// protected methods so that alternative LAF
630
// will show bidi information.
631
Document JavaDoc doc = component.getDocument();
632         if (doc instanceof AbstractDocument JavaDoc) {
633             Element JavaDoc bidi = ((AbstractDocument JavaDoc)doc).getBidiRootElement();
634             if ((bidi != null) && (bidi.getElementCount() > 1)) {
635             // there are multiple directions present.
636
flagXPoints[0] = r.x + ((dotLTR) ? paintWidth : 0);
637                         flagYPoints[0] = r.y;
638                         flagXPoints[1] = flagXPoints[0];
639                         flagYPoints[1] = flagYPoints[0] + 4;
640                         flagXPoints[2] = flagXPoints[0] + ((dotLTR) ? 4 : -4);
641                         flagYPoints[2] = flagYPoints[0];
642                         g.fillPolygon(flagXPoints, flagYPoints, 3);
643             }
644         }
645         } catch (BadLocationException JavaDoc e) {
646         // can't render I guess
647
//System.err.println("Can't render cursor");
648
}
649     }
650     }
651     
652     /**
653      * Called when the UI is being installed into the
654      * interface of a JTextComponent. This can be used
655      * to gain access to the model that is being navigated
656      * by the implementation of this interface. Sets the dot
657      * and mark to 0, and establishes document, property change,
658      * focus, mouse, and mouse motion listeners.
659      *
660      * @param c the component
661      * @see Caret#install
662      */

663     public void install(JTextComponent JavaDoc c) {
664     component = c;
665     Document JavaDoc doc = c.getDocument();
666     dot = mark = 0;
667     dotLTR = markLTR = true;
668     dotBias = markBias = Position.Bias.Forward;
669     if (doc != null) {
670         doc.addDocumentListener(handler);
671     }
672     c.addPropertyChangeListener(handler);
673     c.addFocusListener(this);
674     c.addMouseListener(this);
675     c.addMouseMotionListener(this);
676
677     // if the component already has focus, it won't
678
// be notified.
679
if (component.hasFocus()) {
680         focusGained(null);
681     }
682
683         Number JavaDoc ratio = (Number JavaDoc) c.getClientProperty("caretAspectRatio");
684         if (ratio != null) {
685             aspectRatio = ratio.floatValue();
686         } else {
687             aspectRatio = -1;
688         }
689
690         Integer JavaDoc width = (Integer JavaDoc) c.getClientProperty("caretWidth");
691         if (width != null) {
692             caretWidth = width.intValue();
693         } else {
694             caretWidth = -1;
695         }
696     }
697
698     /**
699      * Called when the UI is being removed from the
700      * interface of a JTextComponent. This is used to
701      * unregister any listeners that were attached.
702      *
703      * @param c the component
704      * @see Caret#deinstall
705      */

706     public void deinstall(JTextComponent JavaDoc c) {
707     c.removeMouseListener(this);
708     c.removeMouseMotionListener(this);
709         c.removeFocusListener(this);
710     c.removePropertyChangeListener(handler);
711     Document JavaDoc doc = c.getDocument();
712     if (doc != null) {
713         doc.removeDocumentListener(handler);
714     }
715     synchronized(this) {
716         component = null;
717     }
718     if (flasher != null) {
719         flasher.stop();
720     }
721
722     
723     }
724
725     /**
726      * Adds a listener to track whenever the caret position has
727      * been changed.
728      *
729      * @param l the listener
730      * @see Caret#addChangeListener
731      */

732     public void addChangeListener(ChangeListener l) {
733     listenerList.add(ChangeListener.class, l);
734     }
735     
736     /**
737      * Removes a listener that was tracking caret position changes.
738      *
739      * @param l the listener
740      * @see Caret#removeChangeListener
741      */

742     public void removeChangeListener(ChangeListener l) {
743     listenerList.remove(ChangeListener.class, l);
744     }
745
746     /**
747      * Returns an array of all the change listeners
748      * registered on this caret.
749      *
750      * @return all of this caret's <code>ChangeListener</code>s
751      * or an empty
752      * array if no change listeners are currently registered
753      *
754      * @see #addChangeListener
755      * @see #removeChangeListener
756      *
757      * @since 1.4
758      */

759     public ChangeListener[] getChangeListeners() {
760         return (ChangeListener[])listenerList.getListeners(
761                 ChangeListener.class);
762     }
763
764     /**
765      * Notifies all listeners that have registered interest for
766      * notification on this event type. The event instance
767      * is lazily created using the parameters passed into
768      * the fire method. The listener list is processed last to first.
769      *
770      * @see EventListenerList
771      */

772     protected void fireStateChanged() {
773     // Guaranteed to return a non-null array
774
Object JavaDoc[] listeners = listenerList.getListenerList();
775     // Process the listeners last to first, notifying
776
// those that are interested in this event
777
for (int i = listeners.length-2; i>=0; i-=2) {
778         if (listeners[i]==ChangeListener.class) {
779         // Lazily create the event:
780
if (changeEvent == null)
781             changeEvent = new ChangeEvent(this);
782         ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
783         }
784     }
785     }
786
787     /**
788      * Returns an array of all the objects currently registered
789      * as <code><em>Foo</em>Listener</code>s
790      * upon this caret.
791      * <code><em>Foo</em>Listener</code>s are registered using the
792      * <code>add<em>Foo</em>Listener</code> method.
793      *
794      * <p>
795      *
796      * You can specify the <code>listenerType</code> argument
797      * with a class literal,
798      * such as
799      * <code><em>Foo</em>Listener.class</code>.
800      * For example, you can query a
801      * <code>DefaultCaret</code> <code>c</code>
802      * for its change listeners with the following code:
803      *
804      * <pre>ChangeListener[] cls = (ChangeListener[])(c.getListeners(ChangeListener.class));</pre>
805      *
806      * If no such listeners exist, this method returns an empty array.
807      *
808      * @param listenerType the type of listeners requested; this parameter
809      * should specify an interface that descends from
810      * <code>java.util.EventListener</code>
811      * @return an array of all objects registered as
812      * <code><em>Foo</em>Listener</code>s on this component,
813      * or an empty array if no such
814      * listeners have been added
815      * @exception ClassCastException if <code>listenerType</code>
816      * doesn't specify a class or interface that implements
817      * <code>java.util.EventListener</code>
818      *
819      * @see #getChangeListeners
820      *
821      * @since 1.3
822      */

823     public <T extends EventListener JavaDoc> T[] getListeners(Class JavaDoc<T> listenerType) {
824     return listenerList.getListeners(listenerType);
825     }
826
827     /**
828      * Changes the selection visibility.
829      *
830      * @param vis the new visibility
831      */

832     public void setSelectionVisible(boolean vis) {
833     if (vis != selectionVisible) {
834         selectionVisible = vis;
835         if (selectionVisible) {
836         // show
837
Highlighter JavaDoc h = component.getHighlighter();
838         if ((dot != mark) && (h != null) && (selectionTag == null)) {
839             int p0 = Math.min(dot, mark);
840             int p1 = Math.max(dot, mark);
841             Highlighter.HighlightPainter JavaDoc p = getSelectionPainter();
842             try {
843             selectionTag = h.addHighlight(p0, p1, p);
844             } catch (BadLocationException JavaDoc bl) {
845             selectionTag = null;
846             }
847         }
848         } else {
849         // hide
850
if (selectionTag != null) {
851             Highlighter JavaDoc h = component.getHighlighter();
852             h.removeHighlight(selectionTag);
853             selectionTag = null;
854         }
855         }
856     }
857     }
858
859     /**
860      * Checks whether the current selection is visible.
861      *
862      * @return true if the selection is visible
863      */

864     public boolean isSelectionVisible() {
865     return selectionVisible;
866     }
867
868     /**
869      * Determines if the caret is currently active.
870      * <p>
871      * This method returns whether or not the <code>Caret</code>
872      * is currently in a blinking state. It does not provide
873      * information as to whether it is currently blinked on or off.
874      * To determine if the caret is currently painted use the
875      * <code>isVisible</code> method.
876      *
877      * @return <code>true</code> if active else <code>false</code>
878      * @see #isVisible
879      *
880      * @since 1.5
881      */

882     public boolean isActive() {
883         return active;
884     }
885
886     /**
887      * Indicates whether or not the caret is currently visible. As the
888      * caret flashes on and off the return value of this will change
889      * between true, when the caret is painted, and false, when the
890      * caret is not painted. <code>isActive</code> indicates whether
891      * or not the caret is in a blinking state, such that it <b>can</b>
892      * be visible, and <code>isVisible</code> indicates whether or not
893      * the caret <b>is</b> actually visible.
894      * <p>
895      * Subclasses that wish to render a different flashing caret
896      * should override paint and only paint the caret if this method
897      * returns true.
898      *
899      * @return true if visible else false
900      * @see Caret#isVisible
901      * @see #isActive
902      */

903     public boolean isVisible() {
904         return visible;
905     }
906
907     /**
908      * Sets the caret visibility, and repaints the caret.
909      * It is important to understand the relationship between this method,
910      * <code>isVisible</code> and <code>isActive</code>.
911      * Calling this method with a value of <code>true</code> activates the
912      * caret blinking. Setting it to <code>false</code> turns it completely off.
913      * To determine whether the blinking is active, you should call
914      * <code>isActive</code>. In effect, <code>isActive</code> is an
915      * appropriate corresponding "getter" method for this one.
916      * <code>isVisible</code> can be used to fetch the current
917      * visibility status of the caret, meaning whether or not it is currently
918      * painted. This status will change as the caret blinks on and off.
919      * <p>
920      * Here's a list showing the potential return values of both
921      * <code>isActive</code> and <code>isVisible</code>
922      * after calling this method:
923      * <p>
924      * <b><code>setVisible(true)</code></b>:
925      * <ul>
926      * <li>isActive(): true</li>
927      * <li>isVisible(): true or false depending on whether
928      * or not the caret is blinked on or off</li>
929      * </ul>
930      * <p>
931      * <b><code>setVisible(false)</code></b>:
932      * <ul>
933      * <li>isActive(): false</li>
934      * <li>isVisible(): false</li>
935      * </ul>
936      *
937      * @param e the visibility specifier
938      * @see #isActive
939      * @see Caret#setVisible
940      */

941     public void setVisible(boolean e) {
942     // focus lost notification can come in later after the
943
// caret has been deinstalled, in which case the component
944
// will be null.
945
if (component != null) {
946             active = e;
947             TextUI mapper = component.getUI();
948         if (visible != e) {
949                 visible = e;
950         // repaint the caret
951
try {
952             Rectangle loc = mapper.modelToView(component, dot,dotBias);
953             damage(loc);
954         } catch (BadLocationException JavaDoc badloc) {
955             // hmm... not legally positioned
956
}
957         }
958     }
959     if (flasher != null) {
960         if (visible) {
961         flasher.start();
962         } else {
963         flasher.stop();
964         }
965     }
966     }
967
968     /**
969      * Sets the caret blink rate.
970      *
971      * @param rate the rate in milliseconds, 0 to stop blinking
972      * @see Caret#setBlinkRate
973      */

974     public void setBlinkRate(int rate) {
975     if (rate != 0) {
976         if (flasher == null) {
977         flasher = new Timer(rate, handler);
978       &