Source for javax.swing.text.DefaultCaret

   1: /* DefaultCaret.java --
   2:    Copyright (C) 2002, 2004, 2005 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: 
  63: /**
  64:  * The default implementation of the {@link Caret} interface.
  65:  *
  66:  * @author orgininal author unknown
  67:  * @author Roman Kennke (roman@kennke.org)
  68:  */
  69: public class DefaultCaret extends Rectangle
  70:   implements Caret, FocusListener, MouseListener, MouseMotionListener
  71: {
  72: 
  73:   /**
  74:    * Controls the blinking of the caret.
  75:    *
  76:    * @author Roman Kennke (kennke@aicas.com)
  77:    * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  78:    */
  79:   private class BlinkTimerListener implements ActionListener
  80:   {
  81:     /**
  82:      * Forces the next event to be ignored. The next event should be ignored
  83:      * if we force the caret to appear. We do not know how long will it take
  84:      * to fire the comming event; this may be near immediately. Better to leave
  85:      * the caret visible one iteration longer.
  86:      */
  87:     boolean ignoreNextEvent;
  88:     
  89:     /**
  90:      * Receives notification when the blink timer fires and updates the visible
  91:      * state of the caret.
  92:      * 
  93:      * @param event the action event
  94:      */
  95:     public void actionPerformed(ActionEvent event)
  96:     {
  97:       if (ignoreNextEvent)
  98:         ignoreNextEvent = false;
  99:       else
 100:         {
 101:           visible = !visible;
 102:           repaint();
 103:         }
 104:     }
 105:   }
 106: 
 107:   /**
 108:    * Listens for changes in the text component's document and updates the
 109:    * caret accordingly.
 110:    * 
 111:    * @author Roman Kennke (kennke@aicas.com)
 112:    */
 113:   private class DocumentHandler implements DocumentListener
 114:   {
 115:     /**
 116:      * Receives notification that some text attributes have changed. No action
 117:      * is taken here.
 118:      *
 119:      * @param event the document event
 120:      */
 121:     public void changedUpdate(DocumentEvent event)
 122:     {
 123:       // Nothing to do here.
 124:     }
 125: 
 126:     /**
 127:      * Receives notification that some text has been inserted from the text
 128:      * component. The caret is moved forward accordingly.
 129:      *
 130:      * @param event the document event
 131:      */
 132:     public void insertUpdate(DocumentEvent event)
 133:     {
 134:       if (policy == ALWAYS_UPDATE || 
 135:           (SwingUtilities.isEventDispatchThread() && 
 136:            policy == UPDATE_WHEN_ON_EDT))
 137:         {        
 138:           int dot = getDot();
 139:           setDot(dot + event.getLength());
 140:         }
 141:     }
 142: 
 143:     /**
 144:      * Receives notification that some text has been removed into the text
 145:      * component. The caret is moved backwards accordingly.
 146:      *
 147:      * @param event the document event
 148:      */
 149:     public void removeUpdate(DocumentEvent event)
 150:     {
 151:       if (policy == ALWAYS_UPDATE || 
 152:           (SwingUtilities.isEventDispatchThread() && 
 153:            policy == UPDATE_WHEN_ON_EDT))
 154:         {
 155:           int dot = getDot();
 156:           setDot(dot - event.getLength());
 157:         }
 158:       else if (policy == NEVER_UPDATE)
 159:         {
 160:           int docLength = event.getDocument().getLength();
 161:           if (getDot() > docLength)
 162:             setDot(docLength);
 163:         }
 164:     }
 165:   }
 166: 
 167:   /**
 168:    * Listens for property changes on the text document. This is used to add and
 169:    * remove our document listener, if the document of the text component has
 170:    * changed.
 171:    *
 172:    * @author Roman Kennke (kennke@aicas.com)
 173:    */
 174:   private class PropertyChangeHandler implements PropertyChangeListener
 175:   {
 176: 
 177:     /**
 178:      * Receives notification when a property has changed on the text component.
 179:      * This adds/removes our document listener from the text component's
 180:      * document when the document changes.
 181:      *
 182:      * @param e the property change event
 183:      */
 184:     public void propertyChange(PropertyChangeEvent e)
 185:     {
 186:       if (e.getPropertyName().equals("document"))
 187:         {
 188:           Document oldDoc = (Document) e.getOldValue();
 189:           oldDoc.removeDocumentListener(documentListener);
 190:           Document newDoc = (Document) e.getNewValue();
 191:           newDoc.addDocumentListener(documentListener);
 192:         }
 193:     }
 194:     
 195:   }
 196: 
 197:   /** The serialization UID (compatible with JDK1.5). */
 198:   private static final long serialVersionUID = 4325555698756477346L;
 199:   
 200:   /**
 201:    * Indicates the Caret position should always be updated after Document
 202:    * changes even if the updates are not performed on the Event Dispatching
 203:    * thread.
 204:    * 
 205:    * @since 1.5
 206:    */
 207:   public static final int ALWAYS_UPDATE = 2;
 208: 
 209:   /**
 210:    * Indicates the Caret position should not be changed unless the Document
 211:    * length becomes less than the Caret position, in which case the Caret
 212:    * is moved to the end of the Document.
 213:    * 
 214:    * @since 1.5
 215:    */
 216:   public static final int NEVER_UPDATE = 1;
 217:   
 218:   /** 
 219:    * Indicates the Caret position should be updated only if Document changes
 220:    * are made on the Event Dispatcher thread.
 221:    *  
 222:    * @since 1.5
 223:    */
 224:   public static final int UPDATE_WHEN_ON_EDT = 0;
 225:   
 226:   /** Keeps track of the current update policy **/
 227:   int policy = UPDATE_WHEN_ON_EDT;
 228:     
 229:   /**
 230:    * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
 231:    */
 232:   protected ChangeEvent changeEvent = new ChangeEvent(this);
 233: 
 234:   /**
 235:    * Stores all registered event listeners.
 236:    */
 237:   protected EventListenerList listenerList = new EventListenerList();
 238: 
 239:   /**
 240:    * Our document listener.
 241:    */
 242:   DocumentListener documentListener;
 243: 
 244:   /**
 245:    * Our property listener.
 246:    */
 247:   PropertyChangeListener propertyChangeListener;
 248: 
 249:   /**
 250:    * The text component in which this caret is installed.
 251:    */
 252:   private JTextComponent textComponent;
 253: 
 254:   /**
 255:    * Indicates if the selection should be visible or not.
 256:    */
 257:   private boolean selectionVisible = true;
 258: 
 259:   /**
 260:    * The blink rate of this <code>Caret</code>.
 261:    */
 262:   private int blinkRate = 500;
 263: 
 264:   /**
 265:    * The current dot position.
 266:    */
 267:   private int dot = 0;
 268: 
 269:   /**
 270:    * The current mark position.
 271:    */
 272:   private int mark = 0;
 273: 
 274:   /**
 275:    * The current visual caret position.
 276:    */
 277:   private Point magicCaretPosition = null;
 278: 
 279:   /**
 280:    * Indicates if this <code>Caret</code> is currently visible or not. This is
 281:    * package private to avoid an accessor method.
 282:    */
 283:   boolean visible = false;
 284: 
 285:   /**
 286:    * The current highlight entry.
 287:    */
 288:   private Object highlightEntry;
 289: 
 290:   private Timer blinkTimer;
 291:   
 292:   private BlinkTimerListener blinkListener;
 293: 
 294:   /**
 295:    * Creates a new <code>DefaultCaret</code> instance.
 296:    */
 297:   public DefaultCaret()
 298:   {
 299:     // Nothing to do here.
 300:   }
 301: 
 302:   /**
 303:    * Sets the Caret update policy.
 304:    *    
 305:    * @param policy the new policy.  Valid values are:
 306:    * ALWAYS_UPDATE: always update the Caret position, even when Document
 307:    * updates don't occur on the Event Dispatcher thread.
 308:    * NEVER_UPDATE: don't update the Caret position unless the Document
 309:    * length becomes less than the Caret position (then update the
 310:    * Caret to the end of the Document).
 311:    * UPDATE_WHEN_ON_EDT: update the Caret position when the 
 312:    * Document updates occur on the Event Dispatcher thread.  This is the 
 313:    * default.
 314:    * 
 315:    * @since 1.5
 316:    * @throws IllegalArgumentException if policy is not one of the above.
 317:    */
 318:   public void setUpdatePolicy (int policy)
 319:   {
 320:     if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
 321:         && policy != UPDATE_WHEN_ON_EDT)
 322:       throw new 
 323:         IllegalArgumentException
 324:         ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
 325:     this.policy = policy;
 326:   }
 327:   
 328:   /**
 329:    * Gets the caret update policy.
 330:    * 
 331:    * @return the caret update policy.
 332:    * @since 1.5
 333:    */
 334:   public int getUpdatePolicy ()
 335:   {
 336:     return policy;
 337:   }
 338:   
 339:   /**
 340:    * Moves the caret position when the mouse is dragged over the text
 341:    * component, modifying the selection accordingly.
 342:    *
 343:    * @param event the <code>MouseEvent</code> describing the drag operation
 344:    */
 345:   public void mouseDragged(MouseEvent event)
 346:   {
 347:     moveCaret(event);
 348:   }
 349: 
 350:   /**
 351:    * Indicates a mouse movement over the text component. Does nothing here.
 352:    *
 353:    * @param event the <code>MouseEvent</code> describing the mouse operation
 354:    */
 355:   public void mouseMoved(MouseEvent event)
 356:   {
 357:     // Nothing to do here.
 358:   }
 359: 
 360:   /**
 361:    * When the click is received from Button 1 then the following actions
 362:    * are performed here:
 363:    *
 364:    * <ul>
 365:    * <li>If we receive a double click, the caret position (dot) is set
 366:    *   to the position associated to the mouse click and the word at
 367:    *   this location is selected.</li>
 368:    * <li>If we receive a triple click, the caret position (dot) is set
 369:    *   to the position associated to the mouse click and the line at
 370:    *   this location is selected.</li>
 371:    * </ul>
 372:    *
 373:    * @param event the <code>MouseEvent</code> describing the click operation
 374:    */
 375:   public void mouseClicked(MouseEvent event)
 376:   {
 377:     // TODO: Implement double- and triple-click behaviour here.
 378:   }
 379: 
 380:   /**
 381:    * Indicates that the mouse has entered the text component. Nothing is done
 382:    * here.
 383:    *
 384:    * @param event the <code>MouseEvent</code> describing the mouse operation
 385:    */
 386:   public void mouseEntered(MouseEvent event)
 387:   {
 388:     // Nothing to do here.
 389:   }
 390: 
 391:   /**
 392:    * Indicates that the mouse has exited the text component. Nothing is done
 393:    * here.
 394:    *
 395:    * @param event the <code>MouseEvent</code> describing the mouse operation
 396:    */
 397:   public void mouseExited(MouseEvent event)
 398:   {
 399:     // Nothing to do here.
 400:   }
 401: 
 402:   /**
 403:    * If the button 1 is pressed, the caret position is updated to the
 404:    * position of the mouse click and the text component requests the input
 405:    * focus if it is enabled. If the SHIFT key is held down, the caret will
 406:    * be moved, which might select the text between the old and new location.
 407:    *
 408:    * @param event the <code>MouseEvent</code> describing the press operation
 409:    */
 410:   public void mousePressed(MouseEvent event)
 411:   {
 412:     positionCaret(event);
 413:   }
 414: 
 415:   /**
 416:    * Indicates that a mouse button has been released on the text component.
 417:    * Nothing is done here.
 418:    *
 419:    * @param event the <code>MouseEvent</code> describing the mouse operation
 420:    */
 421:   public void mouseReleased(MouseEvent event)
 422:   {
 423:     // Nothing to do here.
 424:   }
 425: 
 426:   /**
 427:    * Sets the caret to <code>visible</code> if the text component is editable.
 428:    *
 429:    * @param event the <code>FocusEvent</code>
 430:    */
 431:   public void focusGained(FocusEvent event)
 432:   {
 433:     setVisible(true);
 434:   }
 435: 
 436:   /**
 437:    * Sets the caret to <code>invisible</code>.
 438:    *
 439:    * @param event the <code>FocusEvent</code>
 440:    */
 441:   public void focusLost(FocusEvent event)
 442:   {
 443:     if (event.isTemporary() == false)
 444:       setVisible(false);
 445:   }
 446: 
 447:   /**
 448:    * Moves the caret to the position specified in the <code>MouseEvent</code>.
 449:    * This will cause a selection if the dot and mark are different.
 450:    *
 451:    * @param event the <code>MouseEvent</code> from which to fetch the position
 452:    */
 453:   protected void moveCaret(MouseEvent event)
 454:   {
 455:     int newDot = getComponent().viewToModel(event.getPoint());
 456:     moveDot(newDot);
 457:   }
 458: 
 459:   /**
 460:    * Repositions the caret to the position specified in the
 461:    * <code>MouseEvent</code>.
 462:    *
 463:    * @param event the <code>MouseEvent</code> from which to fetch the position
 464:    */
 465:   protected void positionCaret(MouseEvent event)
 466:   {
 467:     int newDot = getComponent().viewToModel(event.getPoint());
 468:     setDot(newDot);
 469:   }
 470: 
 471:   /**
 472:    * Deinstalls this <code>Caret</code> from the specified
 473:    * <code>JTextComponent</code>. This removes any listeners that have been
 474:    * registered by this <code>Caret</code>.
 475:    *
 476:    * @param c the text component from which to install this caret
 477:    */
 478:   public void deinstall(JTextComponent c)
 479:   {
 480:     textComponent.removeFocusListener(this);
 481:     textComponent.removeMouseListener(this);
 482:     textComponent.removeMouseMotionListener(this);
 483:     textComponent.getDocument().removeDocumentListener(documentListener);
 484:     documentListener = null;
 485:     textComponent.removePropertyChangeListener(propertyChangeListener);
 486:     propertyChangeListener = null;
 487:     textComponent = null;
 488: 
 489:     // Deinstall blink timer if present.
 490:     if (blinkTimer != null)
 491:       blinkTimer.stop();
 492:     blinkTimer = null;
 493:   }
 494: 
 495:   /**
 496:    * Installs this <code>Caret</code> on the specified
 497:    * <code>JTextComponent</code>. This registers a couple of listeners
 498:    * on the text component.
 499:    *
 500:    * @param c the text component on which to install this caret
 501:    */
 502:   public void install(JTextComponent c)
 503:   {
 504:     textComponent = c;
 505:     textComponent.addFocusListener(this);
 506:     textComponent.addMouseListener(this);
 507:     textComponent.addMouseMotionListener(this);
 508:     propertyChangeListener = new PropertyChangeHandler();
 509:     textComponent.addPropertyChangeListener(propertyChangeListener);
 510:     documentListener = new DocumentHandler();
 511:     textComponent.getDocument().addDocumentListener(documentListener);
 512: 
 513:     repaint();
 514:   }
 515: 
 516:   /**
 517:    * Sets the current visual position of this <code>Caret</code>.
 518:    *
 519:    * @param p the Point to use for the saved location. May be <code>null</code>
 520:    *        to indicate that there is no visual location
 521:    */
 522:   public void setMagicCaretPosition(Point p)
 523:   {
 524:     magicCaretPosition = p;
 525:   }
 526: 
 527:   /**
 528:    * Returns the current visual position of this <code>Caret</code>.
 529:    *
 530:    * @return the current visual position of this <code>Caret</code>
 531:    *
 532:    * @see #setMagicCaretPosition
 533:    */
 534:   public Point getMagicCaretPosition()
 535:   {
 536:     return magicCaretPosition;
 537:   }
 538: 
 539:   /**
 540:    * Returns the current position of the <code>mark</code>. The
 541:    * <code>mark</code> marks the location in the <code>Document</code> that
 542:    * is the end of a selection. If there is no selection, the <code>mark</code>
 543:    * is the same as the <code>dot</code>.
 544:    *
 545:    * @return the current position of the mark
 546:    */
 547:   public int getMark()
 548:   {
 549:     return mark;
 550:   }
 551: 
 552:   private void handleHighlight()
 553:   {
 554:     Highlighter highlighter = textComponent.getHighlighter();
 555:     
 556:     if (highlighter == null)
 557:       return;
 558:     
 559:     int p0 = Math.min(dot, mark);
 560:     int p1 = Math.max(dot, mark);
 561:     
 562:     if (selectionVisible && p0 != p1)
 563:       {
 564:     try
 565:       {
 566:         if (highlightEntry == null)
 567:           highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
 568:         else
 569:           highlighter.changeHighlight(highlightEntry, p0, p1);
 570:       }
 571:     catch (BadLocationException e)
 572:       {
 573:         // This should never happen.
 574:         throw new InternalError();
 575:       }
 576:       }
 577:     else
 578:       {
 579:     if (highlightEntry != null)
 580:       {
 581:         highlighter.removeHighlight(highlightEntry);
 582:         highlightEntry = null;
 583:       }
 584:       }
 585:   }
 586: 
 587:   /**
 588:    * Sets the visiblity state of the selection.
 589:    *
 590:    * @param v <code>true</code> if the selection should be visible,
 591:    *        <code>false</code> otherwise
 592:    */
 593:   public void setSelectionVisible(boolean v)
 594:   {
 595:     if (selectionVisible == v)
 596:       return;
 597:     
 598:     selectionVisible = v;
 599:     handleHighlight();
 600:     repaint();
 601:   }
 602: 
 603:   /**
 604:    * Returns <code>true</code> if the selection is currently visible,
 605:    * <code>false</code> otherwise.
 606:    *
 607:    * @return <code>true</code> if the selection is currently visible,
 608:    *         <code>false</code> otherwise
 609:    */
 610:   public boolean isSelectionVisible()
 611:   {
 612:     return selectionVisible;
 613:   }
 614: 
 615:   /**
 616:    * Causes the <code>Caret</code> to repaint itself.
 617:    */
 618:   protected final void repaint()
 619:   {
 620:     getComponent().repaint(x, y, width, height);
 621:   }
 622: 
 623:   /**
 624:    * Paints this <code>Caret</code> using the specified <code>Graphics</code>
 625:    * context.
 626:    *
 627:    * @param g the graphics context to use
 628:    */
 629:   public void paint(Graphics g)
 630:   {
 631:     JTextComponent comp = getComponent();
 632:     if (comp == null)
 633:       return;
 634: 
 635:     int dot = getDot();
 636:     Rectangle rect = null;
 637: 
 638:     try
 639:       {
 640:         rect = textComponent.modelToView(dot);
 641:       }
 642:     catch (BadLocationException e)
 643:       {
 644:         assert false : "Unexpected bad caret location: " + dot;
 645:         return;
 646:       }
 647: 
 648:     if (rect == null)
 649:       return;
 650: 
 651:     // Check if paint has possibly been called directly, without a previous
 652:     // call to damage(). In this case we need to do some cleanup first.
 653:     if ((x != rect.x) || (y != rect.y))
 654:       {
 655:         repaint(); // Erase previous location of caret.
 656:         x = rect.x;
 657:         y = rect.y;
 658:         width = 1;
 659:         height = rect.height;
 660:       }
 661: 
 662:     // Now draw the caret on the new position if visible.
 663:     if (visible)
 664:       {
 665:         g.setColor(textComponent.getCaretColor());
 666:         g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
 667:       }
 668:   }
 669: 
 670:   /**
 671:    * Returns all registered event listeners of the specified type.
 672:    *
 673:    * @param listenerType the type of listener to return
 674:    *
 675:    * @return all registered event listeners of the specified type
 676:    */
 677:   public EventListener[] getListeners(Class listenerType)
 678:   {
 679:     return listenerList.getListeners(listenerType);
 680:   }
 681: 
 682:   /**
 683:    * Registers a {@link ChangeListener} that is notified whenever that state
 684:    * of this <code>Caret</code> changes.
 685:    *
 686:    * @param listener the listener to register to this caret
 687:    */
 688:   public void addChangeListener(ChangeListener listener)
 689:   {
 690:     listenerList.add(ChangeListener.class, listener);
 691:   }
 692: 
 693:   /**
 694:    * Removes a {@link ChangeListener} from the list of registered listeners.
 695:    *
 696:    * @param listener the listener to remove
 697:    */
 698:   public void removeChangeListener(ChangeListener listener)
 699:   {
 700:     listenerList.remove(ChangeListener.class, listener);
 701:   }
 702: 
 703:   /**
 704:    * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
 705:    *
 706:    * @return all registered {@link ChangeListener}s of this <code>Caret</code>
 707:    */
 708:   public ChangeListener[] getChangeListeners()
 709:   {
 710:     return (ChangeListener[]) getListeners(ChangeListener.class);
 711:   }
 712: 
 713:   /**
 714:    * Notifies all registered {@link ChangeListener}s that the state
 715:    * of this <code>Caret</code> has changed.
 716:    */
 717:   protected void fireStateChanged()
 718:   {
 719:     ChangeListener[] listeners = getChangeListeners();
 720: 
 721:     for (int index = 0; index < listeners.length; ++index)
 722:       listeners[index].stateChanged(changeEvent);
 723:   }
 724: 
 725:   /**
 726:    * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
 727:    * is installed.
 728:    *
 729:    * @return the <code>JTextComponent</code> on which this <code>Caret</code>
 730:    *         is installed
 731:    */
 732:   protected final JTextComponent getComponent()
 733:   {
 734:     return textComponent;
 735:   }
 736: 
 737:   /**
 738:    * Returns the blink rate of this <code>Caret</code> in milliseconds.
 739:    * A value of <code>0</code> means that the caret does not blink.
 740:    *
 741:    * @return the blink rate of this <code>Caret</code> or <code>0</code> if
 742:    *         this caret does not blink
 743:    */
 744:   public int getBlinkRate()
 745:   {
 746:     return blinkRate;
 747:   }
 748: 
 749:   /**
 750:    * Sets the blink rate of this <code>Caret</code> in milliseconds.
 751:    * A value of <code>0</code> means that the caret does not blink.
 752:    *
 753:    * @param rate the new blink rate to set
 754:    */
 755:   public void setBlinkRate(int rate)
 756:   {
 757:     if (blinkTimer != null)
 758:       blinkTimer.setDelay(rate);
 759:     blinkRate = rate;
 760:   }
 761: 
 762:   /**
 763:    * Returns the current position of this <code>Caret</code> within the
 764:    * <code>Document</code>.
 765:    *
 766:    * @return the current position of this <code>Caret</code> within the
 767:    *         <code>Document</code>
 768:    */
 769:   public int getDot()
 770:   {
 771:     return dot;
 772:   }
 773: 
 774:   /**
 775:    * Moves the <code>dot</code> location without touching the
 776:    * <code>mark</code>. This is used when making a selection.
 777:    *
 778:    * @param dot the location where to move the dot
 779:    *
 780:    * @see #setDot(int)
 781:    */
 782:   public void moveDot(int dot)
 783:   {
 784:     if (dot >= 0)
 785:       {
 786:         this.dot = dot;
 787:         handleHighlight();
 788:         adjustVisibility(this);
 789:         appear();
 790:       }
 791:   }
 792: 
 793:   /**
 794:    * Sets the current position of this <code>Caret</code> within the
 795:    * <code>Document</code>. This also sets the <code>mark</code> to the new
 796:    * location.
 797:    * 
 798:    * @param dot
 799:    *          the new position to be set
 800:    * @see #moveDot(int)
 801:    */
 802:   public void setDot(int dot)
 803:   {
 804:     if (dot >= 0)
 805:       {
 806:         this.mark = dot;
 807:         this.dot = dot;
 808:         handleHighlight();
 809:         adjustVisibility(this);
 810:         appear();
 811:       }
 812:   }
 813:   
 814:   /**
 815:    * Show the caret (may be hidden due blinking) and adjust the timer not to
 816:    * hide it (possibly immediately).
 817:    * 
 818:    * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
 819:    */
 820:   void appear()
 821:   {
 822:     // All machinery is only required if the carret is blinking.
 823:     if (blinkListener != null)
 824:       {
 825:         blinkListener.ignoreNextEvent = true;
 826: 
 827:         // If the caret is visible, erase the current position by repainting
 828:         // over.
 829:         if (visible)
 830:           repaint();
 831: 
 832:         // Draw the caret in the new position.
 833:         visible = true;
 834: 
 835:         Rectangle area = null;
 836:         try
 837:           {
 838:             area = getComponent().modelToView(getDot());
 839:           }
 840:         catch (BadLocationException ex)
 841:           {
 842:             assert false : "Unexpected bad caret location: " + getDot();
 843:           }
 844:         if (area != null)
 845:           damage(area);
 846:       }
 847:     repaint();
 848:   }  
 849: 
 850:   /**
 851:    * Returns <code>true</code> if this <code>Caret</code> is currently visible,
 852:    * and <code>false</code> if it is not.
 853:    *
 854:    * @return <code>true</code> if this <code>Caret</code> is currently visible,
 855:    *         and <code>false</code> if it is not
 856:    */
 857:   public boolean isVisible()
 858:   {
 859:     return visible;
 860:   }
 861: 
 862:   /**
 863:    * Sets the visibility state of the caret. <code>true</code> shows the
 864:    * <code>Caret</code>, <code>false</code> hides it.
 865:    *
 866:    * @param v the visibility to set
 867:    */  
 868:   public void setVisible(boolean v)
 869:   {
 870:     if (v != visible)
 871:       {
 872:         visible = v;
 873:         if (visible)
 874:           if (textComponent.isEnabled() && textComponent.isEditable())
 875:             {
 876:               if (blinkTimer == null)
 877:                 initBlinkTimer();
 878:               blinkTimer.start();
 879:             }
 880:         else
 881:           {
 882:             if (blinkTimer != null)
 883:               blinkTimer.stop();
 884:           }
 885:         Rectangle area = null;
 886:         try
 887:           {            
 888:             area = getComponent().modelToView(getDot());
 889:           }
 890:         catch (BadLocationException ex)
 891:           {
 892:             assert false: "Unexpected bad caret location: " + getDot();
 893:           }
 894:         if (area != null)
 895:           damage(area);
 896:       }
 897:   }
 898: 
 899:   /**
 900:    * Returns the {@link Highlighter.HighlightPainter} that should be used
 901:    * to paint the selection.
 902:    *
 903:    * @return the {@link Highlighter.HighlightPainter} that should be used
 904:    *         to paint the selection
 905:    */
 906:   protected Highlighter.HighlightPainter getSelectionPainter()
 907:   {
 908:     return DefaultHighlighter.DefaultPainter;
 909:   }
 910: 
 911:   /**
 912:    * Updates the carets rectangle properties to the specified rectangle and
 913:    * repaints the caret.
 914:    *
 915:    * @param r the rectangle to set as the caret rectangle
 916:    */
 917:   protected void damage(Rectangle r)
 918:   {
 919:     if (r == null)
 920:       return;
 921:     x = r.x;
 922:     y = r.y;
 923:     width = 1;
 924:     // height is normally set in paint and we leave it untouched. However, we
 925:     // must set a valid value here, since otherwise the painting mechanism
 926:     // sets a zero clip and never calls paint.
 927:     if (height <= 0)
 928:       height = getComponent().getHeight();
 929:     repaint();
 930:   }
 931: 
 932:   /**
 933:    * Adjusts the text component so that the caret is visible. This default
 934:    * implementation simply calls
 935:    * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
 936:    * Subclasses may wish to change this.
 937:    */
 938:   protected void adjustVisibility(Rectangle rect)
 939:   {
 940:     getComponent().scrollRectToVisible(rect);
 941:   }
 942: 
 943:   /**
 944:    * Initializes the blink timer.
 945:    */
 946:   private void initBlinkTimer()
 947:   {
 948:     // Setup the blink timer.
 949:     blinkListener = new BlinkTimerListener();
 950:     blinkTimer = new Timer(getBlinkRate(), blinkListener);
 951:     blinkTimer.setRepeats(true);
 952:   }
 953: }