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.FocusEvent;
  44: import java.awt.event.FocusListener;
  45: import java.awt.event.MouseEvent;
  46: import java.awt.event.MouseListener;
  47: import java.awt.event.MouseMotionListener;
  48: import java.util.EventListener;
  49: 
  50: import javax.swing.event.ChangeEvent;
  51: import javax.swing.event.ChangeListener;
  52: import javax.swing.event.EventListenerList;
  53: 
  54: /**
  55:  * The default implementation of the {@link Caret} interface.
  56:  *
  57:  * @author orgininal author unknown
  58:  * @author Roman Kennke (roman@kennke.org)
  59:  */
  60: public class DefaultCaret extends Rectangle
  61:   implements Caret, FocusListener, MouseListener, MouseMotionListener
  62: {
  63:   /**
  64:    * The serial version UID for DefaultCaret.
  65:    */
  66:   private static final long serialVersionUID = 228155774675466193L;
  67: 
  68:   /**
  69:    * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
  70:    */
  71:   protected ChangeEvent changeEvent = new ChangeEvent(this);
  72: 
  73:   /**
  74:    * Stores all registered event listeners.
  75:    */
  76:   protected EventListenerList listenerList = new EventListenerList();
  77: 
  78:   /**
  79:    * The text component in which this caret is installed.
  80:    */
  81:   private JTextComponent textComponent;
  82: 
  83:   /**
  84:    * Indicates if the selection should be visible or not.
  85:    */
  86:   private boolean selectionVisible = true;
  87: 
  88:   /**
  89:    * The blink rate of this <code>Caret</code>.
  90:    */
  91:   private int blinkRate = 500;
  92: 
  93:   /**
  94:    * The current dot position.
  95:    */
  96:   private int dot = 0;
  97: 
  98:   /**
  99:    * The current mark position.
 100:    */
 101:   private int mark = 0;
 102: 
 103:   /**
 104:    * The current visual caret position.
 105:    */
 106:   private Point magicCaretPosition = null;
 107: 
 108:   /**
 109:    * Indicates if this <code>Caret</code> is currently visible or not.
 110:    */
 111:   private boolean visible = true;
 112: 
 113:   /**
 114:    * The current highlight entry.
 115:    */
 116:   private Object highlightEntry;
 117: 
 118:   /**
 119:    * Moves the caret position when the mouse is dragged over the text
 120:    * component, modifying the selection accordingly.
 121:    *
 122:    * @param event the <code>MouseEvent</code> describing the drag operation
 123:    */
 124:   public void mouseDragged(MouseEvent event)
 125:   {
 126:     // FIXME: Implement this properly.
 127:   }
 128: 
 129:   /**
 130:    * Indicates a mouse movement over the text component. Does nothing here.
 131:    *
 132:    * @param event the <code>MouseEvent</code> describing the mouse operation
 133:    */
 134:   public void mouseMoved(MouseEvent event)
 135:   {
 136:     // Nothing to do here.
 137:   }
 138: 
 139:   /**
 140:    * When the click is received from Button 1 then the following actions
 141:    * are performed here:
 142:    *
 143:    * <ul>
 144:    * <li>If we receive a double click, the caret position (dot) is set
 145:    *   to the position associated to the mouse click and the word at
 146:    *   this location is selected.</li>
 147:    * <li>If we receive a triple click, the caret position (dot) is set
 148:    *   to the position associated to the mouse click and the line at
 149:    *   this location is selected.</li>
 150:    * </ul>
 151:    *
 152:    * @param event the <code>MouseEvent</code> describing the click operation
 153:    */
 154:   public void mouseClicked(MouseEvent event)
 155:   {
 156:     // FIXME: Implement this properly.
 157:   }
 158: 
 159:   /**
 160:    * Indicates that the mouse has entered the text component. Nothing is done
 161:    * here.
 162:    *
 163:    * @param event the <code>MouseEvent</code> describing the mouse operation
 164:    */
 165:   public void mouseEntered(MouseEvent event)
 166:   {
 167:     // Nothing to do here.
 168:   }
 169: 
 170:   /**
 171:    * Indicates that the mouse has exited the text component. Nothing is done
 172:    * here.
 173:    *
 174:    * @param event the <code>MouseEvent</code> describing the mouse operation
 175:    */
 176:   public void mouseExited(MouseEvent event)
 177:   {
 178:   }
 179: 
 180:   /**
 181:    * If the button 1 is pressed, the caret position is updated to the
 182:    * position of the mouse click and the text component requests the input
 183:    * focus if it is enabled. If the SHIFT key is held down, the caret will
 184:    * be moved, which might select the text between the old and new location.
 185:    *
 186:    * @param event the <code>MouseEvent</code> describing the press operation
 187:    */
 188:   public void mousePressed(MouseEvent event)
 189:   {
 190:     // FIXME: Implement this properly.
 191:   }
 192: 
 193:   /**
 194:    * Indicates that a mouse button has been released on the text component.
 195:    * Nothing is done here.
 196:    *
 197:    * @param event the <code>MouseEvent</code> describing the mouse operation
 198:    */
 199:   public void mouseReleased(MouseEvent event)
 200:   {
 201:     // Nothing to do here.
 202:   }
 203: 
 204:   /**
 205:    * Sets the caret to <code>visible</code> if the text component is editable.
 206:    *
 207:    * @param event the <code>FocusEvent</code>
 208:    */
 209:   public void focusGained(FocusEvent event)
 210:   {
 211:   }
 212: 
 213:   /**
 214:    * Sets the caret to <code>invisible</code>.
 215:    *
 216:    * @param event the <code>FocusEvent</code>
 217:    */
 218:   public void focusLost(FocusEvent event)
 219:   {
 220:   }
 221: 
 222:   /**
 223:    * Moves the caret to the position specified in the <code>MouseEvent</code>.
 224:    * This will cause a selection if the dot and mark are different.
 225:    *
 226:    * @param event the <code>MouseEvent</code> from which to fetch the position
 227:    */
 228:   protected void moveCaret(MouseEvent event)
 229:   {
 230:     // FIXME: Implement this properly.
 231:   }
 232: 
 233:   /**
 234:    * Repositions the caret to the position specified in the
 235:    * <code>MouseEvent</code>.
 236:    *
 237:    * @param event the <code>MouseEvent</code> from which to fetch the position
 238:    */
 239:   protected void positionCaret(MouseEvent event)
 240:   {
 241:     // FIXME: Implement this properly.
 242:   }
 243: 
 244:   /**
 245:    * Deinstalls this <code>Caret</code> from the specified
 246:    * <code>JTextComponent</code>. This removes any listeners that have been
 247:    * registered by this <code>Caret</code>.
 248:    *
 249:    * @param c the text component from which to install this caret
 250:    */
 251:   public void deinstall(JTextComponent c)
 252:   {
 253:     textComponent.removeFocusListener(this);
 254:     textComponent.removeMouseListener(this);
 255:     textComponent.removeMouseMotionListener(this);
 256:     textComponent = null;
 257:   }
 258: 
 259:   /**
 260:    * Installs this <code>Caret</code> on the specified
 261:    * <code>JTextComponent</code>. This registers a couple of listeners
 262:    * on the text component.
 263:    *
 264:    * @param c the text component on which to install this caret
 265:    */
 266:   public void install(JTextComponent c)
 267:   {
 268:     textComponent = c;
 269:     textComponent.addFocusListener(this);
 270:     textComponent.addMouseListener(this);
 271:     textComponent.addMouseMotionListener(this);
 272:     repaint();
 273:   }
 274: 
 275:   /**
 276:    * Sets the current visual position of this <code>Caret</code>.
 277:    *
 278:    * @param p the Point to use for the saved location. May be <code>null</code>
 279:    *        to indicate that there is no visual location
 280:    */
 281:   public void setMagicCaretPosition(Point p)
 282:   {
 283:     magicCaretPosition = p;
 284:   }
 285: 
 286:   /**
 287:    * Returns the current visual position of this <code>Caret</code>.
 288:    *
 289:    * @return the current visual position of this <code>Caret</code>
 290:    *
 291:    * @see #setMagicCaretPosition
 292:    */
 293:   public Point getMagicCaretPosition()
 294:   {
 295:     return magicCaretPosition;
 296:   }
 297: 
 298:   /**
 299:    * Returns the current position of the <code>mark</code>. The
 300:    * <code>mark</code> marks the location in the <code>Document</code> that
 301:    * is the end of a selection. If there is no selection, the <code>mark</code>
 302:    * is the same as the <code>dot</code>.
 303:    *
 304:    * @return the current position of the mark
 305:    */
 306:   public int getMark()
 307:   {
 308:     return mark;
 309:   }
 310: 
 311:   private void handleHighlight()
 312:   {
 313:     Highlighter highlighter = textComponent.getHighlighter();
 314:     
 315:     if (highlighter == null)
 316:       return;
 317:     
 318:     int p0 = Math.min(dot, mark);
 319:     int p1 = Math.max(dot, mark);
 320:     
 321:     if (selectionVisible && p0 != p1)
 322:       {
 323:     try
 324:       {
 325:         if (highlightEntry == null)
 326:           highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
 327:         else
 328:           highlighter.changeHighlight(highlightEntry, p0, p1);
 329:       }
 330:     catch (BadLocationException e)
 331:       {
 332:         // This should never happen.
 333:         throw new InternalError();
 334:       }
 335:       }
 336:     else
 337:       {
 338:     if (highlightEntry != null)
 339:       {
 340:         highlighter.removeHighlight(highlightEntry);
 341:         highlightEntry = null;
 342:       }
 343:       }
 344:   }
 345: 
 346:   /**
 347:    * Sets the visiblity state of the selection.
 348:    *
 349:    * @param v <code>true</code> if the selection should be visible,
 350:    *        <code>false</code> otherwise
 351:    */
 352:   public void setSelectionVisible(boolean v)
 353:   {
 354:     if (selectionVisible == v)
 355:       return;
 356:     
 357:     selectionVisible = v;
 358:     handleHighlight();
 359:     repaint();
 360:   }
 361: 
 362:   /**
 363:    * Returns <code>true</code> if the selection is currently visible,
 364:    * <code>false</code> otherwise.
 365:    *
 366:    * @return <code>true</code> if the selection is currently visible,
 367:    *         <code>false</code> otherwise
 368:    */
 369:   public boolean isSelectionVisible()
 370:   {
 371:     return selectionVisible;
 372:   }
 373: 
 374:   /**
 375:    * Causes the <code>Caret</code> to repaint itself.
 376:    */
 377:   protected final void repaint()
 378:   {
 379:     // FIXME: Is this good? This possibly causes alot of the component
 380:     // hierarchy to be repainted on every caret blink.
 381:     if (textComponent != null)
 382:       textComponent.repaint();
 383:   }
 384: 
 385:   /**
 386:    * Paints this <code>Caret</code> using the specified <code>Graphics</code>
 387:    * context.
 388:    *
 389:    * @param g the graphics context to use
 390:    */
 391:   public void paint(Graphics g)
 392:   {
 393:     if (textComponent == null)
 394:       return;
 395: 
 396:     int dot = getDot();
 397:     Rectangle rect = null;
 398: 
 399:     try
 400:       {
 401:     rect = textComponent.modelToView(dot);
 402:       }
 403:     catch (BadLocationException e)
 404:       {
 405:     // This should never happen as dot should be always valid.
 406:     return;
 407:       }
 408: 
 409:     if (rect == null)
 410:       return;
 411:     
 412:     // First we need to delete the old caret.
 413:     // FIXME: Implement deleting of old caret.
 414:     
 415:     // Now draw the caret on the new position if visible.
 416:     if (visible)
 417:       {
 418:     g.setColor(textComponent.getCaretColor());
 419:     g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
 420:       }
 421:   }
 422: 
 423:   /**
 424:    * Returns all registered event listeners of the specified type.
 425:    *
 426:    * @param listenerType the type of listener to return
 427:    *
 428:    * @return all registered event listeners of the specified type
 429:    */
 430:   public EventListener[] getListeners(Class listenerType)
 431:   {
 432:     return listenerList.getListeners(listenerType);
 433:   }
 434: 
 435:   /**
 436:    * Registers a {@link ChangeListener} that is notified whenever that state
 437:    * of this <code>Caret</code> changes.
 438:    *
 439:    * @param listener the listener to register to this caret
 440:    */
 441:   public void addChangeListener(ChangeListener listener)
 442:   {
 443:     listenerList.add(ChangeListener.class, listener);
 444:   }
 445: 
 446:   /**
 447:    * Removes a {@link ChangeListener} from the list of registered listeners.
 448:    *
 449:    * @param listener the listener to remove
 450:    */
 451:   public void removeChangeListener(ChangeListener listener)
 452:   {
 453:     listenerList.remove(ChangeListener.class, listener);
 454:   }
 455: 
 456:   /**
 457:    * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
 458:    *
 459:    * @return all registered {@link ChangeListener}s of this <code>Caret</code>
 460:    */
 461:   public ChangeListener[] getChangeListeners()
 462:   {
 463:     return (ChangeListener[]) getListeners(ChangeListener.class);
 464:   }
 465: 
 466:   /**
 467:    * Notifies all registered {@link ChangeListener}s that the state
 468:    * of this <code>Caret</code> has changed.
 469:    */
 470:   protected void fireStateChanged()
 471:   {
 472:     ChangeListener[] listeners = getChangeListeners();
 473: 
 474:     for (int index = 0; index < listeners.length; ++index)
 475:       listeners[index].stateChanged(changeEvent);
 476:   }
 477: 
 478:   /**
 479:    * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
 480:    * is installed.
 481:    *
 482:    * @return the <code>JTextComponent</code> on which this <code>Caret</code>
 483:    *         is installed
 484:    */
 485:   protected final JTextComponent getComponent()
 486:   {
 487:     return textComponent;
 488:   }
 489: 
 490:   /**
 491:    * Returns the blink rate of this <code>Caret</code> in milliseconds.
 492:    * A value of <code>0</code> means that the caret does not blink.
 493:    *
 494:    * @return the blink rate of this <code>Caret</code> or <code>0</code> if
 495:    *         this caret does not blink
 496:    */
 497:   public int getBlinkRate()
 498:   {
 499:     return blinkRate;
 500:   }
 501: 
 502:   /**
 503:    * Sets the blink rate of this <code>Caret</code> in milliseconds.
 504:    * A value of <code>0</code> means that the caret does not blink.
 505:    *
 506:    * @param rate the new blink rate to set
 507:    */
 508:   public void setBlinkRate(int rate)
 509:   {
 510:     blinkRate = rate;
 511:   }
 512: 
 513:   /**
 514:    * Returns the current position of this <code>Caret</code> within the
 515:    * <code>Document</code>.
 516:    *
 517:    * @return the current position of this <code>Caret</code> within the
 518:    *         <code>Document</code>
 519:    */
 520:   public int getDot()
 521:   {
 522:     return dot;
 523:   }
 524: 
 525:   /**
 526:    * Moves the <code>dot</code> location without touching the
 527:    * <code>mark</code>. This is used when making a selection.
 528:    *
 529:    * @param dot the location where to move the dot
 530:    *
 531:    * @see #setDot(int)
 532:    */
 533:   public void moveDot(int dot)
 534:   {
 535:     this.dot = dot;
 536:     handleHighlight();
 537:     repaint();
 538:   }
 539: 
 540:   /**
 541:    * Sets the current position of this <code>Caret</code> within the
 542:    * <code>Document</code>. This also sets the <code>mark</code> to the
 543:    * new location.
 544:    *
 545:    * @param dot the new position to be set
 546:    *
 547:    * @see #moveDot(int)
 548:    */
 549:   public void setDot(int dot)
 550:   {
 551:     this.dot = dot;
 552:     this.mark = dot;
 553:     handleHighlight();
 554:     repaint();
 555:   }
 556: 
 557:   /**
 558:    * Returns <code>true</code> if this <code>Caret</code> is currently visible,
 559:    * and <code>false</code> if it is not.
 560:    *
 561:    * @return <code>true</code> if this <code>Caret</code> is currently visible,
 562:    *         and <code>false</code> if it is not
 563:    */
 564:   public boolean isVisible()
 565:   {
 566:     return visible;
 567:   }
 568: 
 569:   /**
 570:    * Sets the visibility state of the caret. <code>true</code> shows the
 571:    * <code>Caret</code>, <code>false</code> hides it.
 572:    *
 573:    * @param v the visibility to set
 574:    */  
 575:   public void setVisible(boolean v)
 576:   {
 577:     visible = v;
 578:     repaint();
 579:   }
 580: 
 581:   /**
 582:    * Returns the {@link Highlighter.HighlightPainter} that should be used
 583:    * to paint the selection.
 584:    *
 585:    * @return the {@link Highlighter.HighlightPainter} that should be used
 586:    *         to paint the selection
 587:    */
 588:   protected Highlighter.HighlightPainter getSelectionPainter()
 589:   {
 590:     return DefaultHighlighter.DefaultPainter;
 591:   }
 592: }