Source for javax.swing.text.DefaultCaret

   1: /* DefaultCaret.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing.text;
  39: 
  40: import java.awt.Graphics;
  41: import java.awt.Point;
  42: import java.awt.Rectangle;
  43: import java.awt.event.ActionEvent;
  44: import java.awt.event.ActionListener;
  45: import java.awt.event.FocusEvent;
  46: import java.awt.event.FocusListener;
  47: import java.awt.event.MouseEvent;
  48: import java.awt.event.MouseListener;
  49: import java.awt.event.MouseMotionListener;
  50: import java.beans.PropertyChangeEvent;
  51: import java.beans.PropertyChangeListener;
  52: import java.util.EventListener;
  53: 
  54: import javax.swing.JComponent;
  55: import javax.swing.SwingUtilities;
  56: import javax.swing.Timer;
  57: import javax.swing.event.ChangeEvent;
  58: import javax.swing.event.ChangeListener;
  59: import javax.swing.event.DocumentEvent;
  60: import javax.swing.event.DocumentListener;
  61: import javax.swing.event.EventListenerList;
  62: import javax.swing.text.Position.Bias;
  63: 
  64: /**
  65:  * The default implementation of the {@link Caret} interface.
  66:  *
  67:  * @author original author unknown
  68:  * @author Roman Kennke (roman@kennke.org)
  69:  */
  70: public class DefaultCaret extends Rectangle
  71:   implements Caret, FocusListener, MouseListener, MouseMotionListener
  72: {
  73:   
  74:   /** A text component in the current VM which currently has a
  75:    * text selection or <code>null</code>.
  76:    */ 
  77:   static JTextComponent componentWithSelection;
  78: 
  79:   /** An implementation of NavigationFilter.FilterBypass which delegates
  80:    * to the corresponding methods of the <code>DefaultCaret</code>. 
  81:    * 
  82:    * @author Robert Schuster (robertschuster@fsfe.org)
  83:    */
  84:   class Bypass extends NavigationFilter.FilterBypass
  85:   {
  86: 
  87:     public Caret getCaret()
  88:     {
  89:       return DefaultCaret.this;
  90:     }
  91: 
  92:     public void moveDot(int dot, Bias bias)
  93:     {
  94:       DefaultCaret.this.moveDotImpl(dot);
  95:     }
  96: 
  97:     public void setDot(int dot, Bias bias)
  98:     {
  99:       DefaultCaret.this.setDotImpl(dot);
 100:     }
 101:     
 102:   }
 103:   
 104:   /**
 105:    * Controls the blinking of the caret.
 106:    *
 107:    * @author Roman Kennke (kennke@aicas.com)
 108:    * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
 109:    */
 110:   private class BlinkTimerListener implements ActionListener
 111:   {
 112:     /**
 113:      * Forces the next event to be ignored. The next event should be ignored
 114:      * if we force the caret to appear. We do not know how long will it take
 115:      * to fire the comming event; this may be near immediately. Better to leave
 116:      * the caret visible one iteration longer.
 117:      */
 118:     boolean ignoreNextEvent;
 119:     
 120:     /**
 121:      * Receives notification when the blink timer fires and updates the visible
 122:      * state of the caret.
 123:      * 
 124:      * @param event the action event
 125:      */
 126:     public void actionPerformed(ActionEvent event)
 127:     {
 128:       if (ignoreNextEvent)
 129:         ignoreNextEvent = false;
 130:       else
 131:         {
 132:           visible = !visible;
 133:           repaint();
 134:         }
 135:     }
 136:   }
 137: 
 138:   /**
 139:    * Listens for changes in the text component's document and updates the
 140:    * caret accordingly.
 141:    * 
 142:    * @author Roman Kennke (kennke@aicas.com)
 143:    */
 144:   private class DocumentHandler implements DocumentListener
 145:   {
 146:     /**
 147:      * Receives notification that some text attributes have changed. No action
 148:      * is taken here.
 149:      *
 150:      * @param event the document event
 151:      */
 152:     public void changedUpdate(DocumentEvent event)
 153:     {
 154:       // Nothing to do here.
 155:     }
 156: 
 157:     /**
 158:      * Receives notification that some text has been inserted from the text
 159:      * component. The caret is moved forward accordingly.
 160:      *
 161:      * @param event the document event
 162:      */
 163:     public void insertUpdate(DocumentEvent event)
 164:     {
 165:       if (policy == ALWAYS_UPDATE || 
 166:           (SwingUtilities.isEventDispatchThread() && 
 167:            policy == UPDATE_WHEN_ON_EDT))
 168:         {        
 169:           int dot = getDot();
 170:           setDot(dot + event.getLength());
 171:         }
 172:     }
 173: 
 174:     /**
 175:      * Receives notification that some text has been removed into the text
 176:      * component. The caret is moved backwards accordingly.
 177:      *
 178:      * @param event the document event
 179:      */
 180:     public void removeUpdate(DocumentEvent event)
 181:     {
 182:       if (policy == ALWAYS_UPDATE
 183:           || (SwingUtilities.isEventDispatchThread()
 184:               && policy == UPDATE_WHEN_ON_EDT))
 185:         {
 186:           int dot = getDot();
 187:           setDot(dot - event.getLength());
 188:         }
 189:       else if (policy == NEVER_UPDATE
 190:                || (! SwingUtilities.isEventDispatchThread()
 191:                    && policy == UPDATE_WHEN_ON_EDT))
 192:         {
 193:           int docLength = event.getDocument().getLength();
 194:           if (getDot() > docLength)
 195:             setDot(docLength);
 196:         }
 197:     }
 198:   }
 199: 
 200:   /**
 201:    * Listens for property changes on the text document. This is used to add and
 202:    * remove our document listener, if the document of the text component has
 203:    * changed.
 204:    *
 205:    * @author Roman Kennke (kennke@aicas.com)
 206:    */
 207:   private class PropertyChangeHandler implements PropertyChangeListener
 208:   {
 209: 
 210:     /**
 211:      * Receives notification when a property has changed on the text component.
 212:      * This adds/removes our document listener from the text component's
 213:      * document when the document changes.
 214:      *
 215:      * @param e the property change event
 216:      */
 217:     public void propertyChange(PropertyChangeEvent e)
 218:     {
 219:       if (e.getPropertyName().equals("document"))
 220:         {
 221:           Document oldDoc = (Document) e.getOldValue();
 222:           oldDoc.removeDocumentListener(documentListener);
 223:           Document newDoc = (Document) e.getNewValue();
 224:           newDoc.addDocumentListener(documentListener);
 225:         }
 226:     }
 227:     
 228:   }
 229: 
 230:   /** The serialization UID (compatible with JDK1.5). */
 231:   private static final long serialVersionUID = 4325555698756477346L;
 232:   
 233:   /**
 234:    * Indicates the Caret position should always be updated after Document
 235:    * changes even if the updates are not performed on the Event Dispatching
 236:    * thread.
 237:    * 
 238:    * @since 1.5
 239:    */
 240:   public static final int ALWAYS_UPDATE = 2;
 241: 
 242:   /**
 243:    * Indicates the Caret position should not be changed unless the Document
 244:    * length becomes less than the Caret position, in which case the Caret
 245:    * is moved to the end of the Document.
 246:    * 
 247:    * @since 1.5
 248:    */
 249:   public static final int NEVER_UPDATE = 1;
 250:   
 251:   /** 
 252:    * Indicates the Caret position should be updated only if Document changes
 253:    * are made on the Event Dispatcher thread.
 254:    *  
 255:    * @since 1.5
 256:    */
 257:   public static final int UPDATE_WHEN_ON_EDT = 0;
 258:   
 259:   /** Keeps track of the current update policy **/
 260:   int policy = UPDATE_WHEN_ON_EDT;
 261:     
 262:   /**
 263:    * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
 264:    */
 265:   protected ChangeEvent changeEvent = new ChangeEvent(this);
 266: 
 267:   /**
 268:    * Stores all registered event listeners.
 269:    */
 270:   protected EventListenerList listenerList = new EventListenerList();
 271: 
 272:   /**
 273:    * Our document listener.
 274:    */
 275:   DocumentListener documentListener;
 276: 
 277:   /**
 278:    * Our property listener.
 279:    */
 280:   PropertyChangeListener propertyChangeListener;
 281: 
 282:   /**
 283:    * The text component in which this caret is installed.
 284:    */
 285:   private JTextComponent textComponent;
 286: 
 287:   /**
 288:    * Indicates if the selection should be visible or not.
 289:    */
 290:   private boolean selectionVisible = true;
 291: 
 292:   /**
 293:    * The blink rate of this <code>Caret</code>.
 294:    */
 295:   private int blinkRate = 500;
 296: 
 297:   /**
 298:    * The current dot position.
 299:    */
 300:   private int dot = 0;
 301: 
 302:   /**
 303:    * The current mark position.
 304:    */
 305:   private int mark = 0;
 306: 
 307:   /**
 308:    * The current visual caret position.
 309:    */
 310:   private Point magicCaretPosition = null;
 311: 
 312:   /**
 313:    * Indicates if this <code>Caret</code> is currently visible or not. This is
 314:    * package private to avoid an accessor method.
 315:    */
 316:   boolean visible = false;
 317: 
 318:   /**
 319:    * The current highlight entry.
 320:    */
 321:   private Object highlightEntry;
 322: 
 323:   private Timer blinkTimer;
 324:   
 325:   private BlinkTimerListener blinkListener;
 326: 
 327:   /**
 328:    * A <code>NavigationFilter.FilterBypass</code> instance which
 329:    * is provided to the a <code>NavigationFilter</code> to
 330:    * unconditionally set or move the caret.
 331:    */
 332:   NavigationFilter.FilterBypass bypass;
 333: 
 334:   /**
 335:    * Creates a new <code>DefaultCaret</code> instance.
 336:    */
 337:   public DefaultCaret()
 338:   {
 339:     // Nothing to do here.
 340:   }
 341:   
 342:   /** Returns the caret's <code>NavigationFilter.FilterBypass</code> instance
 343:    * and creates it if it does not yet exist.
 344:    * 
 345:    * @return The caret's <code>NavigationFilter.FilterBypass</code> instance.
 346:    */
 347:   private NavigationFilter.FilterBypass getBypass()
 348:   {
 349:     return (bypass == null) ? bypass = new Bypass() : bypass;
 350:   }
 351: 
 352:   /**
 353:    * Sets the Caret update policy.
 354:    *    
 355:    * @param policy the new policy.  Valid values are:
 356:    * ALWAYS_UPDATE: always update the Caret position, even when Document
 357:    * updates don't occur on the Event Dispatcher thread.
 358:    * NEVER_UPDATE: don't update the Caret position unless the Document
 359:    * length becomes less than the Caret position (then update the
 360:    * Caret to the end of the Document).
 361:    * UPDATE_WHEN_ON_EDT: update the Caret position when the 
 362:    * Document updates occur on the Event Dispatcher thread.  This is the 
 363:    * default.
 364:    * 
 365:    * @since 1.5
 366:    * @throws IllegalArgumentException if policy is not one of the above.
 367:    */
 368:   public void setUpdatePolicy (int policy)
 369:   {
 370:     if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
 371:         && policy != UPDATE_WHEN_ON_EDT)
 372:       throw new 
 373:         IllegalArgumentException
 374:         ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
 375:     this.policy = policy;
 376:   }
 377:   
 378:   /**
 379:    * Gets the caret update policy.
 380:    * 
 381:    * @return the caret update policy.
 382:    * @since 1.5
 383:    */
 384:   public int getUpdatePolicy ()
 385:   {
 386:     return policy;
 387:   }
 388:   
 389:   /**
 390:    * Moves the caret position when the mouse is dragged over the text
 391:    * component, modifying the selection accordingly.
 392:    *
 393:    * @param event the <code>MouseEvent</code> describing the drag operation
 394:    */
 395:   public void mouseDragged(MouseEvent event)
 396:   {
 397:     if (event.getButton() == MouseEvent.BUTTON1)
 398:       moveCaret(event);
 399:   }
 400: 
 401:   /**
 402:    * Indicates a mouse movement over the text component. Does nothing here.
 403:    *
 404:    * @param event the <code>MouseEvent</code> describing the mouse operation
 405:    */
 406:   public void mouseMoved(MouseEvent event)
 407:   {
 408:     // Nothing to do here.
 409:   }
 410: 
 411:   /**
 412:    * When the click is received from Button 1 then the following actions
 413:    * are performed here:
 414:    *
 415:    * <ul>
 416:    * <li>If we receive a double click, the caret position (dot) is set
 417:    *   to the position associated to the mouse click and the word at
 418:    *   this location is selected. If there is no word at the pointer
 419:    *   the gap is selected instead.</li>
 420:    * <li>If we receive a triple click, the caret position (dot) is set
 421:    *   to the position associated to the mouse click and the line at
 422:    *   this location is selected.</li>
 423:    * </ul>
 424:    *
 425:    * @param event the <code>MouseEvent</code> describing the click operation
 426:    */
 427:   public void mouseClicked(MouseEvent event)
 428:   {
 429:     int count = event.getClickCount();
 430:     
 431:     if (event.getButton() == MouseEvent.BUTTON1 && count >= 2)
 432:       {
 433:         int newDot = getComponent().viewToModel(event.getPoint());
 434:         JTextComponent t = getComponent();
 435: 
 436:         try
 437:           {
 438:             if (count == 3)
 439:               {
 440:                 setDot(Utilities.getRowStart(t, newDot));
 441:                 moveDot( Utilities.getRowEnd(t, newDot));
 442:               }
 443:             else
 444:               {
 445:                 int wordStart = Utilities.getWordStart(t, newDot);
 446:                 
 447:                 // When the mouse points at the offset of the first character
 448:                 // in a word Utilities().getPreviousWord will not return that
 449:                 // word but we want to select that. We have to use
 450:                 // Utilities.getWordStart() to get it.
 451:                 if (newDot == wordStart)
 452:                   {
 453:                     setDot(wordStart);
 454:                     moveDot(Utilities.getWordEnd(t, wordStart));
 455:                   }
 456:                 else
 457:                   {
 458:                     int nextWord = Utilities.getNextWord(t, newDot);
 459:                     int previousWord = Utilities.getPreviousWord(t, newDot);
 460:                     int previousWordEnd = Utilities.getWordEnd(t, previousWord);
 461:                     
 462:                     // If the user clicked in the space between two words,
 463:                     // then select the space.
 464:                     if (newDot >= previousWordEnd && newDot <= nextWord)
 465:                       {
 466:                         setDot(previousWordEnd);
 467:                         moveDot(nextWord);
 468:                       }
 469:                     // Otherwise select the word under the mouse pointer.
 470:                     else
 471:                       {
 472:                         setDot(previousWord);
 473:                         moveDot(previousWordEnd);
 474:                       }
 475:                   }
 476:               }
 477:           }
 478:         catch(BadLocationException ble)
 479:           {
 480:             // TODO: Swallowing ok here?
 481:           }
 482:       }
 483:     
 484:   }
 485: 
 486:   /**
 487:    * Indicates that the mouse has entered the text component. Nothing is done
 488:    * here.
 489:    *
 490:    * @param event the <code>MouseEvent</code> describing the mouse operation
 491:    */
 492:   public void mouseEntered(MouseEvent event)
 493:   {
 494:     // Nothing to do here.
 495:   }
 496: 
 497:   /**
 498:    * Indicates that the mouse has exited the text component. Nothing is done
 499:    * here.
 500:    *
 501:    * @param event the <code>MouseEvent</code> describing the mouse operation
 502:    */
 503:   public void mouseExited(MouseEvent event)
 504:   {
 505:     // Nothing to do here.
 506:   }
 507: 
 508:   /**
 509:    * If the button 1 is pressed, the caret position is updated to the
 510:    * position of the mouse click and the text component requests the input
 511:    * focus if it is enabled. If the SHIFT key is held down, the caret will
 512:    * be moved, which might select the text between the old and new location.
 513:    *
 514:    * @param event the <code>MouseEvent</code> describing the press operation
 515:    */
 516:   public void mousePressed(MouseEvent event)
 517:   {
 518:     int button = event.getButton();
 519:     
 520:     // The implementation assumes that consuming the event makes the AWT event
 521:     // mechanism forget about this event instance and not transfer focus.
 522:     // By observing how the RI reacts the following behavior has been
 523:     // implemented (in regard to text components):
 524:     // - a left-click moves the caret
 525:     // - a left-click when shift is held down expands the selection
 526:     // - a right-click or click with any additionaly mouse button
 527:     //   on a text component is ignored
 528:     // - a middle-click positions the caret and pastes the clipboard
 529:     //   contents.
 530:     // - a middle-click when shift is held down is ignored
 531:     
 532:     if (button == MouseEvent.BUTTON1)
 533:       if (event.isShiftDown())
 534:         moveCaret(event);
 535:       else
 536:         positionCaret(event);
 537:       else if(button == MouseEvent.BUTTON2)
 538:         if (event.isShiftDown())
 539:           event.consume();
 540:         else
 541:           {
 542:             positionCaret(event);
 543:             textComponent.paste();
 544:           }
 545:       else
 546:         event.consume();
 547:   }
 548: 
 549:   /**
 550:    * Indicates that a mouse button has been released on the text component.
 551:    * Nothing is done here.
 552:    *
 553:    * @param event the <code>MouseEvent</code> describing the mouse operation
 554:    */
 555:   public void mouseReleased(MouseEvent event)
 556:   {
 557:     // Nothing to do here.
 558:   }
 559: 
 560:   /**
 561:    * Sets the caret to <code>visible</code> if the text component is editable.
 562:    *
 563:    * @param event the <code>FocusEvent</code>
 564:    */
 565:   public void focusGained(FocusEvent event)
 566:   {
 567:     setVisible(true);    
 568:     updateTimerStatus();
 569:   }
 570: 
 571:   /**
 572:    * Sets the caret to <code>invisible</code>.
 573:    * 
 574:    * @param event the <code>FocusEvent</code>
 575:    */
 576:   public void focusLost(FocusEvent event)
 577:   {
 578:     if (event.isTemporary() == false)
 579:       {
 580:         setVisible(false);
 581:         // Stop the blinker, if running.
 582:         if (blinkTimer != null && blinkTimer.isRunning())
 583:           blinkTimer.stop();
 584:       }
 585:   }
 586:   
 587:   /**
 588:    * Install (if not present) and start the timer, if the caret must blink. The
 589:    * caret does not blink if it is invisible, or the component is disabled or
 590:    * not editable.
 591:    */
 592:   private void updateTimerStatus()
 593:   {
 594:     if (textComponent.isEnabled() && textComponent.isEditable())
 595:       {
 596:         if (blinkTimer == null)
 597:           initBlinkTimer();
 598:         if (!blinkTimer.isRunning())
 599:           blinkTimer.start();
 600:       }
 601:     else
 602:       {
 603:         if (blinkTimer != null)
 604:           blinkTimer.stop();
 605:       }
 606:   }
 607: 
 608:   /**
 609:    * Moves the caret to the position specified in the <code>MouseEvent</code>.
 610:    * This will cause a selection if the dot and mark are different.
 611:    *
 612:    * @param event the <code>MouseEvent</code> from which to fetch the position
 613:    */
 614:   protected void moveCaret(MouseEvent event)
 615:   {
 616:     int newDot = getComponent().viewToModel(event.getPoint());
 617:     moveDot(newDot);
 618:   }
 619: 
 620:   /**
 621:    * Repositions the caret to the position specified in the
 622:    * <code>MouseEvent</code>.
 623:    *
 624:    * @param event the <code>MouseEvent</code> from which to fetch the position
 625:    */
 626:   protected void positionCaret(MouseEvent event)
 627:   {
 628:     int newDot = getComponent().viewToModel(event.getPoint());
 629:     setDot(newDot);
 630:   }
 631: 
 632:   /**
 633:    * Deinstalls this <code>Caret</code> from the specified
 634:    * <code>JTextComponent</code>. This removes any listeners that have been
 635:    * registered by this <code>Caret</code>.
 636:    *
 637:    * @param c the text component from which to install this caret
 638:    */
 639:   public void deinstall(JTextComponent c)
 640:   {
 641:     textComponent.removeFocusListener(this);
 642:     textComponent.removeMouseListener(this);
 643:     textComponent.removeMouseMotionListener(this);
 644:     textComponent.getDocument().removeDocumentListener(documentListener);
 645:     documentListener = null;
 646:     textComponent.removePropertyChangeListener(propertyChangeListener);
 647:     propertyChangeListener = null;
 648:     textComponent = null;
 649: 
 650:     // Deinstall blink timer if present.
 651:     if (blinkTimer != null)
 652:       blinkTimer.stop();
 653:     blinkTimer = null;
 654:   }
 655: 
 656:   /**
 657:    * Installs this <code>Caret</code> on the specified
 658:    * <code>JTextComponent</code>. This registers a couple of listeners
 659:    * on the text component.
 660:    *
 661:    * @param c the text component on which to install this caret
 662:    */
 663:   public void install(JTextComponent c)
 664:   {
 665:     textComponent = c;
 666:     textComponent.addFocusListener(this);
 667:     textComponent.addMouseListener(this);
 668:     textComponent.addMouseMotionListener(this);
 669:     propertyChangeListener = new PropertyChangeHandler();
 670:     textComponent.addPropertyChangeListener(propertyChangeListener);
 671:     documentListener = new DocumentHandler();
 672:     textComponent.getDocument().addDocumentListener(documentListener);
 673: 
 674:     repaint();
 675:   }
 676: 
 677:   /**
 678:    * Sets the current visual position of this <code>Caret</code>.
 679:    *
 680:    * @param p the Point to use for the saved location. May be <code>null</code>
 681:    *        to indicate that there is no visual location
 682:    */
 683:   public void setMagicCaretPosition(Point p)
 684:   {
 685:     magicCaretPosition = p;
 686:   }
 687: 
 688:   /**
 689:    * Returns the current visual position of this <code>Caret</code>.
 690:    *
 691:    * @return the current visual position of this <code>Caret</code>
 692:    *
 693:    * @see #setMagicCaretPosition
 694:    */
 695:   public Point getMagicCaretPosition()
 696:   {
 697:     return magicCaretPosition;
 698:   }
 699: 
 700:   /**
 701:    * Returns the current position of the <code>mark</code>. The
 702:    * <code>mark</code> marks the location in the <code>Document</code> that
 703:    * is the end of a selection. If there is no selection, the <code>mark</code>
 704:    * is the same as the <code>dot</code>.
 705:    *
 706:    * @return the current position of the mark
 707:    */
 708:   public int getMark()
 709:   {
 710:     return mark;
 711:   }
 712:   
 713:   private void clearHighlight()
 714:   {
 715:     Highlighter highlighter = textComponent.getHighlighter();
 716:     
 717:     if (highlighter == null)
 718:       return;
 719:     
 720:     if (selectionVisible)
 721:       {
 722:     try
 723:       {
 724:         if (highlightEntry != null)
 725:           highlighter.changeHighlight(highlightEntry, 0, 0);
 726:         
 727:         // Free the global variable which stores the text component with an active
 728:         // selection.
 729:         if (componentWithSelection == textComponent)
 730:           componentWithSelection = null;
 731:       }
 732:     catch (BadLocationException e)
 733:       {
 734:         // This should never happen.
 735:         throw new InternalError();
 736:       }
 737:       }
 738:     else
 739:       {
 740:     if (highlightEntry != null)
 741:       {
 742:         highlighter.removeHighlight(highlightEntry);
 743:         highlightEntry = null;
 744:       }
 745:       }
 746:   }
 747:   
 748:   private void handleHighlight()
 749:   {
 750:     Highlighter highlighter = textComponent.getHighlighter();
 751:     
 752:     if (highlighter == null)
 753:       return;
 754:     
 755:     int p0 = Math.min(dot, mark);
 756:     int p1 = Math.max(dot, mark);
 757:     
 758:     if (selectionVisible)
 759:       {
 760:     try
 761:       {
 762:         if (highlightEntry == null)
 763:           highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
 764:         else
 765:           highlighter.changeHighlight(highlightEntry, p0, p1);
 766:             
 767:             // If another component currently has a text selection clear that selection
 768:             // first.
 769:             if (componentWithSelection != null)
 770:               if (componentWithSelection != textComponent)
 771:                 {
 772:                   Caret c = componentWithSelection.getCaret();
 773:                   c.setDot(c.getDot());
 774:                 }
 775:             componentWithSelection = textComponent;
 776:             
 777:       }
 778:     catch (BadLocationException e)
 779:       {
 780:         // This should never happen.
 781:         throw new InternalError();
 782:       }
 783:       }
 784:     else
 785:       {
 786:     if (highlightEntry != null)
 787:       {
 788:         highlighter.removeHighlight(highlightEntry);
 789:         highlightEntry = null;
 790:       }
 791:       }
 792:   }
 793: 
 794:   /**
 795:    * Sets the visiblity state of the selection.
 796:    *
 797:    * @param v <code>true</code> if the selection should be visible,
 798:    *        <code>false</code> otherwise
 799:    */
 800:   public void setSelectionVisible(boolean v)
 801:   {
 802:     if (selectionVisible == v)
 803:       return;
 804:     
 805:     selectionVisible = v;
 806:     handleHighlight();
 807:     repaint();
 808:   }
 809: 
 810:   /**
 811:    * Returns <code>true</code> if the selection is currently visible,
 812:    * <code>false</code> otherwise.
 813:    *
 814:    * @return <code>true</code> if the selection is currently visible,
 815:    *         <code>false</code> otherwise
 816:    */
 817:   public boolean isSelectionVisible()
 818:   {
 819:     return selectionVisible;
 820:   }
 821: 
 822:   /**
 823:    * Causes the <code>Caret</code> to repaint itself.
 824:    */
 825:   protected final void repaint()
 826:   {
 827:     getComponent().repaint(x, y, width, height);
 828:   }
 829: 
 830:   /**
 831:    * Paints this <code>Caret</code> using the specified <code>Graphics</code>
 832:    * context.
 833:    *
 834:    * @param g the graphics context to use
 835:    */
 836:   public void paint(Graphics g)
 837:   {
 838:     JTextComponent comp = getComponent();
 839:     if (comp == null)
 840:       return;
 841: 
 842:     // Make sure the dot has a sane position.
 843:     dot = Math.min(dot, textComponent.getDocument().getLength());
 844:     dot = Math.max(dot, 0);
 845: 
 846:     Rectangle rect = null;
 847: 
 848:     try
 849:       {
 850:         rect = textComponent.modelToView(dot);
 851:       }
 852:     catch (BadLocationException e)
 853:       {
 854:         AssertionError ae;
 855:         ae = new AssertionError("Unexpected bad caret location: " + dot);
 856:         ae.initCause(e);
 857:         throw ae;
 858:       }
 859: 
 860:     if (rect == null)
 861:       return;
 862: 
 863:     // Check if paint has possibly been called directly, without a previous
 864:     // call to damage(). In this case we need to do some cleanup first.
 865:     if ((x != rect.x) || (y != rect.y))
 866:       {
 867:         repaint(); // Erase previous location of caret.
 868:         x = rect.x;
 869:         y = rect.y;
 870:         width = 1;
 871:         height = rect.height;
 872:       }
 873: 
 874:     // Now draw the caret on the new position if visible.
 875:     if (visible)
 876:       {
 877:         g.setColor(textComponent.getCaretColor());
 878:         g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);
 879:       }
 880:   }
 881: 
 882:   /**
 883:    * Returns all registered event listeners of the specified type.
 884:    *
 885:    * @param listenerType the type of listener to return
 886:    *
 887:    * @return all registered event listeners of the specified type
 888:    */
 889:   public EventListener[] getListeners(Class listenerType)
 890:   {
 891:     return listenerList.getListeners(listenerType);
 892:   }
 893: 
 894:   /**
 895:    * Registers a {@link ChangeListener} that is notified whenever that state
 896:    * of this <code>Caret</code> changes.
 897:    *
 898:    * @param listener the listener to register to this caret
 899:    */
 900:   public void addChangeListener(ChangeListener listener)
 901:   {
 902:     listenerList.add(ChangeListener.class, listener);
 903:   }
 904: 
 905:   /**
 906:    * Removes a {@link ChangeListener} from the list of registered listeners.
 907:    *
 908:    * @param listener the listener to remove
 909:    */
 910:   public void removeChangeListener(ChangeListener listener)
 911:   {
 912:     listenerList.remove(ChangeListener.class, listener);
 913:   }
 914: 
 915:   /**
 916:    * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
 917:    *
 918:    * @return all registered {@link ChangeListener}s of this <code>Caret</code>
 919:    */
 920:   public ChangeListener[] getChangeListeners()
 921:   {
 922:     return (ChangeListener[]) getListeners(ChangeListener.class);
 923:   }
 924: 
 925:   /**
 926:    * Notifies all registered {@link ChangeListener}s that the state
 927:    * of this <code>Caret</code> has changed.
 928:    */
 929:   protected void fireStateChanged()
 930:   {
 931:     ChangeListener[] listeners = getChangeListeners();
 932: 
 933:     for (int index = 0; index < listeners.length; ++index)
 934:       listeners[index].stateChanged(changeEvent);
 935:   }
 936: 
 937:   /**
 938:    * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
 939:    * is installed.
 940:    *
 941:    * @return the <code>JTextComponent</code> on which this <code>Caret</code>
 942:    *         is installed
 943:    */
 944:   protected final JTextComponent getComponent()
 945:   {
 946:     return textComponent;
 947:   }
 948: 
 949:   /**
 950:    * Returns the blink rate of this <code>Caret</code> in milliseconds.
 951:    * A value of <code>0</code> means that the caret does not blink.
 952:    *
 953:    * @return the blink rate of this <code>Caret</code> or <code>0</code> if
 954:    *         this caret does not blink
 955:    */
 956:   public int getBlinkRate()
 957:   {
 958:     return blinkRate;
 959:   }
 960: 
 961:   /**
 962:    * Sets the blink rate of this <code>Caret</code> in milliseconds.
 963:    * A value of <code>0</code> means that the caret does not blink.
 964:    *
 965:    * @param rate the new blink rate to set
 966:    */
 967:   public void setBlinkRate(int rate)
 968:   {
 969:     if (blinkTimer != null)
 970:       blinkTimer.setDelay(rate);
 971:     blinkRate = rate;
 972:   }
 973: 
 974:   /**
 975:    * Returns the current position of this <code>Caret</code> within the
 976:    * <code>Document</code>.
 977:    *
 978:    * @return the current position of this <code>Caret</code> within the
 979:    *         <code>Document</code>
 980:    */
 981:   public int getDot()
 982:   {
 983:     return dot;
 984:   }
 985: 
 986:   /**
 987:    * Moves the <code>dot</code> location without touching the
 988:    * <code>mark</code>. This is used when making a selection.
 989:    *
 990:    * <p>If the underlying text component has a {@link NavigationFilter}
 991:    * installed the caret will call the corresponding method of that object.</p>
 992:    * 
 993:    * @param dot the location where to move the dot
 994:    *
 995:    * @see #setDot(int)
 996:    */
 997:   public void moveDot(int dot)
 998:   {
 999:     NavigationFilter filter = textComponent.getNavigationFilter();
1000:     if (filter != null)
1001:       filter.moveDot(getBypass(), dot, Bias.Forward);
1002:     else
1003:       moveDotImpl(dot);
1004:   }
1005:   
1006:   void moveDotImpl(int dot)
1007:   {
1008:     if (dot >= 0)
1009:       {
1010:         Document doc = textComponent.getDocument();
1011:         if (doc != null)
1012:           this.dot = Math.min(dot, doc.getLength());
1013:         this.dot = Math.max(this.dot, 0);
1014:         
1015:         handleHighlight();
1016:         appear();
1017:         adjustVisibility(this);
1018:       }
1019:   }
1020: 
1021:   /**
1022:    * Sets the current position of this <code>Caret</code> within the
1023:    * <code>Document</code>. This also sets the <code>mark</code> to the new
1024:    * location.
1025:    * 
1026:    * <p>If the underlying text component has a {@link NavigationFilter}
1027:    * installed the caret will call the corresponding method of that object.</p>
1028:    * 
1029:    * @param dot
1030:    *          the new position to be set
1031:    * @see #moveDot(int)
1032:    */
1033:   public void setDot(int dot)
1034:   {
1035:     NavigationFilter filter = textComponent.getNavigationFilter();
1036:     if (filter != null)
1037:       filter.setDot(getBypass(), dot, Bias.Forward);
1038:     else
1039:       setDotImpl(dot);
1040:   }
1041:   
1042:   void setDotImpl(int dot)
1043:   {
1044:     if (dot >= 0)
1045:       {        
1046:         Document doc = textComponent.getDocument();
1047:         if (doc != null)
1048:           this.dot = Math.min(dot, doc.getLength());
1049:         this.dot = Math.max(this.dot, 0);
1050:         this.mark = this.dot;
1051:         
1052:         clearHighlight();
1053:         appear();
1054:         adjustVisibility(this);
1055:       }
1056:   }
1057:   
1058:   /**
1059:    * Show the caret (may be hidden due blinking) and adjust the timer not to
1060:    * hide it (possibly immediately).
1061:    * 
1062:    * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
1063:    */
1064:   void appear()
1065:   {
1066:     // All machinery is only required if the carret is blinking.
1067:     if (blinkListener != null)
1068:       {
1069:         blinkListener.ignoreNextEvent = true;
1070: 
1071:         // If the caret is visible, erase the current position by repainting
1072:         // over.
1073:         if (visible)
1074:           repaint();
1075: 
1076:         // Draw the caret in the new position.
1077:         visible = true;
1078: 
1079:         Rectangle area = null;
1080:     int dot = getDot();
1081:         try
1082:           {
1083:             area = getComponent().modelToView(dot);
1084:           }
1085:         catch (BadLocationException e)
1086:           {
1087:         AssertionError ae;
1088:         ae = new AssertionError("Unexpected bad caret location: " + dot);
1089:         ae.initCause(e);
1090:         throw ae;
1091:           }
1092:         if (area != null)
1093:           damage(area);
1094:       }
1095:     repaint();
1096:   }  
1097: 
1098:   /**
1099:    * Returns <code>true</code> if this <code>Caret</code> is currently visible,
1100:    * and <code>false</code> if it is not.
1101:    *
1102:    * @return <code>true</code> if this <code>Caret</code> is currently visible,
1103:    *         and <code>false</code> if it is not
1104:    */
1105:   public boolean isVisible()
1106:   {
1107:     return visible;
1108:   }
1109: 
1110:   /**
1111:    * Sets the visibility state of the caret. <code>true</code> shows the
1112:    * <code>Caret</code>, <code>false</code> hides it.
1113:    *
1114:    * @param v the visibility to set
1115:    */  
1116:   public void setVisible(boolean v)
1117:   {
1118:     if (v != visible)
1119:       {
1120:         visible = v;
1121:         updateTimerStatus();
1122:         Rectangle area = null;
1123:     int dot = getDot();
1124:         try
1125:           {            
1126:             area = getComponent().modelToView(dot);
1127:           }
1128:         catch (BadLocationException e)
1129:           {
1130:         AssertionError ae;
1131:         ae = new AssertionError("Unexpected bad caret location: " + dot);
1132:         ae.initCause(e);
1133:         throw ae;
1134:           }
1135:         if (area != null)
1136:           damage(area);
1137:       }
1138:   }
1139: 
1140:   /**
1141:    * Returns the {@link Highlighter.HighlightPainter} that should be used
1142:    * to paint the selection.
1143:    *
1144:    * @return the {@link Highlighter.HighlightPainter} that should be used
1145:    *         to paint the selection
1146:    */
1147:   protected Highlighter.HighlightPainter getSelectionPainter()
1148:   {
1149:     return DefaultHighlighter.DefaultPainter;
1150:   }
1151: 
1152:   /**
1153:    * Updates the carets rectangle properties to the specified rectangle and
1154:    * repaints the caret.
1155:    *
1156:    * @param r the rectangle to set as the caret rectangle
1157:    */
1158:   protected void damage(Rectangle r)
1159:   {
1160:     if (r == null)
1161:       return;
1162:     x = r.x;
1163:     y = r.y;
1164:     width = 1;
1165:     // height is normally set in paint and we leave it untouched. However, we
1166:     // must set a valid value here, since otherwise the painting mechanism
1167:     // sets a zero clip and never calls paint.
1168:     if (height <= 0)
1169:       try
1170:         {
1171:           height = textComponent.modelToView(dot).height;
1172:         }
1173:       catch (BadLocationException ble)
1174:         {
1175:           // Should not happen.
1176:           throw new InternalError("Caret location not within document range.");
1177:         }
1178:       
1179:     repaint();
1180:   }
1181: 
1182:   /**
1183:    * Adjusts the text component so that the caret is visible. This default
1184:    * implementation simply calls
1185:    * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
1186:    * Subclasses may wish to change this.
1187:    */
1188:   protected void adjustVisibility(Rectangle rect)
1189:   {
1190:     getComponent().scrollRectToVisible(rect);
1191:   }
1192: 
1193:   /**
1194:    * Initializes the blink timer.
1195:    */
1196:   private void initBlinkTimer()
1197:   {
1198:     // Setup the blink timer.
1199:     blinkListener = new BlinkTimerListener();
1200:     blinkTimer = new Timer(getBlinkRate(), blinkListener);
1201:     blinkTimer.setRepeats(true);
1202:   }
1203:   
1204: }