mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1288 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1288 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Java
		
	
	
	
/* DefaultCaret.java --
 | 
						|
   Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package javax.swing.text;
 | 
						|
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Point;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.event.ActionEvent;
 | 
						|
import java.awt.event.ActionListener;
 | 
						|
import java.awt.event.FocusEvent;
 | 
						|
import java.awt.event.FocusListener;
 | 
						|
import java.awt.event.MouseEvent;
 | 
						|
import java.awt.event.MouseListener;
 | 
						|
import java.awt.event.MouseMotionListener;
 | 
						|
import java.beans.PropertyChangeEvent;
 | 
						|
import java.beans.PropertyChangeListener;
 | 
						|
import java.util.EventListener;
 | 
						|
 | 
						|
import javax.swing.JComponent;
 | 
						|
import javax.swing.SwingUtilities;
 | 
						|
import javax.swing.Timer;
 | 
						|
import javax.swing.event.ChangeEvent;
 | 
						|
import javax.swing.event.ChangeListener;
 | 
						|
import javax.swing.event.DocumentEvent;
 | 
						|
import javax.swing.event.DocumentListener;
 | 
						|
import javax.swing.event.EventListenerList;
 | 
						|
import javax.swing.text.Position.Bias;
 | 
						|
 | 
						|
/**
 | 
						|
 * The default implementation of the {@link Caret} interface.
 | 
						|
 *
 | 
						|
 * @author original author unknown
 | 
						|
 * @author Roman Kennke (roman@kennke.org)
 | 
						|
 */
 | 
						|
public class DefaultCaret extends Rectangle
 | 
						|
  implements Caret, FocusListener, MouseListener, MouseMotionListener
 | 
						|
{
 | 
						|
 | 
						|
  /** A text component in the current VM which currently has a
 | 
						|
   * text selection or <code>null</code>.
 | 
						|
   */
 | 
						|
  static JTextComponent componentWithSelection;
 | 
						|
 | 
						|
  /** An implementation of NavigationFilter.FilterBypass which delegates
 | 
						|
   * to the corresponding methods of the <code>DefaultCaret</code>.
 | 
						|
   *
 | 
						|
   * @author Robert Schuster (robertschuster@fsfe.org)
 | 
						|
   */
 | 
						|
  class Bypass extends NavigationFilter.FilterBypass
 | 
						|
  {
 | 
						|
 | 
						|
    public Caret getCaret()
 | 
						|
    {
 | 
						|
      return DefaultCaret.this;
 | 
						|
    }
 | 
						|
 | 
						|
    public void moveDot(int dot, Bias bias)
 | 
						|
    {
 | 
						|
      DefaultCaret.this.moveDotImpl(dot);
 | 
						|
    }
 | 
						|
 | 
						|
    public void setDot(int dot, Bias bias)
 | 
						|
    {
 | 
						|
      DefaultCaret.this.setDotImpl(dot);
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Controls the blinking of the caret.
 | 
						|
   *
 | 
						|
   * @author Roman Kennke (kennke@aicas.com)
 | 
						|
   * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
 | 
						|
   */
 | 
						|
  private class BlinkTimerListener implements ActionListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Forces the next event to be ignored. The next event should be ignored
 | 
						|
     * if we force the caret to appear. We do not know how long will it take
 | 
						|
     * to fire the comming event; this may be near immediately. Better to leave
 | 
						|
     * the caret visible one iteration longer.
 | 
						|
     */
 | 
						|
    boolean ignoreNextEvent;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification when the blink timer fires and updates the visible
 | 
						|
     * state of the caret.
 | 
						|
     *
 | 
						|
     * @param event the action event
 | 
						|
     */
 | 
						|
    public void actionPerformed(ActionEvent event)
 | 
						|
    {
 | 
						|
      if (ignoreNextEvent)
 | 
						|
        ignoreNextEvent = false;
 | 
						|
      else
 | 
						|
        {
 | 
						|
          visible = !visible;
 | 
						|
          repaint();
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Listens for changes in the text component's document and updates the
 | 
						|
   * caret accordingly.
 | 
						|
   *
 | 
						|
   * @author Roman Kennke (kennke@aicas.com)
 | 
						|
   */
 | 
						|
  private class DocumentHandler implements DocumentListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Receives notification that some text attributes have changed. No action
 | 
						|
     * is taken here.
 | 
						|
     *
 | 
						|
     * @param event the document event
 | 
						|
     */
 | 
						|
    public void changedUpdate(DocumentEvent event)
 | 
						|
    {
 | 
						|
      // Nothing to do here.
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification that some text has been inserted from the text
 | 
						|
     * component. The caret is moved forward accordingly.
 | 
						|
     *
 | 
						|
     * @param event the document event
 | 
						|
     */
 | 
						|
    public void insertUpdate(DocumentEvent event)
 | 
						|
    {
 | 
						|
      if (policy == ALWAYS_UPDATE ||
 | 
						|
          (SwingUtilities.isEventDispatchThread() &&
 | 
						|
           policy == UPDATE_WHEN_ON_EDT))
 | 
						|
        {
 | 
						|
          int dot = getDot();
 | 
						|
          setDot(dot + event.getLength());
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification that some text has been removed into the text
 | 
						|
     * component. The caret is moved backwards accordingly.
 | 
						|
     *
 | 
						|
     * @param event the document event
 | 
						|
     */
 | 
						|
    public void removeUpdate(DocumentEvent event)
 | 
						|
    {
 | 
						|
      if (policy == ALWAYS_UPDATE
 | 
						|
          || (SwingUtilities.isEventDispatchThread()
 | 
						|
              && policy == UPDATE_WHEN_ON_EDT))
 | 
						|
        {
 | 
						|
          int dot = getDot();
 | 
						|
          setDot(dot - event.getLength());
 | 
						|
        }
 | 
						|
      else if (policy == NEVER_UPDATE
 | 
						|
               || (! SwingUtilities.isEventDispatchThread()
 | 
						|
                   && policy == UPDATE_WHEN_ON_EDT))
 | 
						|
        {
 | 
						|
          int docLength = event.getDocument().getLength();
 | 
						|
          if (getDot() > docLength)
 | 
						|
            setDot(docLength);
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Listens for property changes on the text document. This is used to add and
 | 
						|
   * remove our document listener, if the document of the text component has
 | 
						|
   * changed.
 | 
						|
   *
 | 
						|
   * @author Roman Kennke (kennke@aicas.com)
 | 
						|
   */
 | 
						|
  private class PropertyChangeHandler implements PropertyChangeListener
 | 
						|
  {
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification when a property has changed on the text component.
 | 
						|
     * This adds/removes our document listener from the text component's
 | 
						|
     * document when the document changes.
 | 
						|
     *
 | 
						|
     * @param e the property change event
 | 
						|
     */
 | 
						|
    public void propertyChange(PropertyChangeEvent e)
 | 
						|
    {
 | 
						|
      String name = e.getPropertyName();
 | 
						|
 | 
						|
      if (name.equals("document"))
 | 
						|
        {
 | 
						|
          Document oldDoc = (Document) e.getOldValue();
 | 
						|
          if (oldDoc != null)
 | 
						|
            oldDoc.removeDocumentListener(documentListener);
 | 
						|
 | 
						|
          Document newDoc = (Document) e.getNewValue();
 | 
						|
          if (newDoc != null)
 | 
						|
            newDoc.addDocumentListener(documentListener);
 | 
						|
        }
 | 
						|
      else if (name.equals("editable"))
 | 
						|
        {
 | 
						|
          active = (((Boolean) e.getNewValue()).booleanValue()
 | 
						|
                   && textComponent.isEnabled());
 | 
						|
        }
 | 
						|
      else if (name.equals("enabled"))
 | 
						|
        {
 | 
						|
          active = (((Boolean) e.getNewValue()).booleanValue()
 | 
						|
                   && textComponent.isEditable());
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /** The serialization UID (compatible with JDK1.5). */
 | 
						|
  private static final long serialVersionUID = 4325555698756477346L;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates the Caret position should always be updated after Document
 | 
						|
   * changes even if the updates are not performed on the Event Dispatching
 | 
						|
   * thread.
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public static final int ALWAYS_UPDATE = 2;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates the Caret position should not be changed unless the Document
 | 
						|
   * length becomes less than the Caret position, in which case the Caret
 | 
						|
   * is moved to the end of the Document.
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public static final int NEVER_UPDATE = 1;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates the Caret position should be updated only if Document changes
 | 
						|
   * are made on the Event Dispatcher thread.
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public static final int UPDATE_WHEN_ON_EDT = 0;
 | 
						|
 | 
						|
  /** Keeps track of the current update policy **/
 | 
						|
  int policy = UPDATE_WHEN_ON_EDT;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
 | 
						|
   */
 | 
						|
  protected ChangeEvent changeEvent = new ChangeEvent(this);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Stores all registered event listeners.
 | 
						|
   */
 | 
						|
  protected EventListenerList listenerList = new EventListenerList();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Our document listener.
 | 
						|
   */
 | 
						|
  DocumentListener documentListener;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Our property listener.
 | 
						|
   */
 | 
						|
  PropertyChangeListener propertyChangeListener;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The text component in which this caret is installed.
 | 
						|
   *
 | 
						|
   * (Package private to avoid synthetic accessor method.)
 | 
						|
   */
 | 
						|
  JTextComponent textComponent;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates if the selection should be visible or not.
 | 
						|
   */
 | 
						|
  private boolean selectionVisible = true;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The blink rate of this <code>Caret</code>.
 | 
						|
   */
 | 
						|
  private int blinkRate = 500;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current dot position.
 | 
						|
   */
 | 
						|
  private int dot = 0;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current mark position.
 | 
						|
   */
 | 
						|
  private int mark = 0;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current visual caret position.
 | 
						|
   */
 | 
						|
  private Point magicCaretPosition = null;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates if this <code>Caret</code> is currently visible or not. This is
 | 
						|
   * package private to avoid an accessor method.
 | 
						|
   */
 | 
						|
  boolean visible = false;
 | 
						|
 | 
						|
  /** Indicates whether the text component where the caret is installed is
 | 
						|
   * editable and enabled. If either of these properties is <code>false</code>
 | 
						|
   * the caret is not drawn.
 | 
						|
   */
 | 
						|
  boolean active = true;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current highlight entry.
 | 
						|
   */
 | 
						|
  private Object highlightEntry;
 | 
						|
 | 
						|
  private Timer blinkTimer;
 | 
						|
 | 
						|
  private BlinkTimerListener blinkListener;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A <code>NavigationFilter.FilterBypass</code> instance which
 | 
						|
   * is provided to the a <code>NavigationFilter</code> to
 | 
						|
   * unconditionally set or move the caret.
 | 
						|
   */
 | 
						|
  NavigationFilter.FilterBypass bypass;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new <code>DefaultCaret</code> instance.
 | 
						|
   */
 | 
						|
  public DefaultCaret()
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /** Returns the caret's <code>NavigationFilter.FilterBypass</code> instance
 | 
						|
   * and creates it if it does not yet exist.
 | 
						|
   *
 | 
						|
   * @return The caret's <code>NavigationFilter.FilterBypass</code> instance.
 | 
						|
   */
 | 
						|
  private NavigationFilter.FilterBypass getBypass()
 | 
						|
  {
 | 
						|
    return (bypass == null) ? bypass = new Bypass() : bypass;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the Caret update policy.
 | 
						|
   *
 | 
						|
   * @param policy the new policy.  Valid values are:
 | 
						|
   * ALWAYS_UPDATE: always update the Caret position, even when Document
 | 
						|
   * updates don't occur on the Event Dispatcher thread.
 | 
						|
   * NEVER_UPDATE: don't update the Caret position unless the Document
 | 
						|
   * length becomes less than the Caret position (then update the
 | 
						|
   * Caret to the end of the Document).
 | 
						|
   * UPDATE_WHEN_ON_EDT: update the Caret position when the
 | 
						|
   * Document updates occur on the Event Dispatcher thread.  This is the
 | 
						|
   * default.
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   * @throws IllegalArgumentException if policy is not one of the above.
 | 
						|
   */
 | 
						|
  public void setUpdatePolicy (int policy)
 | 
						|
  {
 | 
						|
    if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
 | 
						|
        && policy != UPDATE_WHEN_ON_EDT)
 | 
						|
      throw new
 | 
						|
        IllegalArgumentException
 | 
						|
        ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
 | 
						|
    this.policy = policy;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the caret update policy.
 | 
						|
   *
 | 
						|
   * @return the caret update policy.
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public int getUpdatePolicy ()
 | 
						|
  {
 | 
						|
    return policy;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Moves the caret position when the mouse is dragged over the text
 | 
						|
   * component, modifying the selectiony.
 | 
						|
   *
 | 
						|
   * <p>When the text component where the caret is installed is disabled,
 | 
						|
   * the selection is not change but you can still scroll the text and
 | 
						|
   * update the caret's location.</p>
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the drag operation
 | 
						|
   */
 | 
						|
  public void mouseDragged(MouseEvent event)
 | 
						|
  {
 | 
						|
    if (event.getButton() == MouseEvent.BUTTON1)
 | 
						|
      {
 | 
						|
        if (textComponent.isEnabled())
 | 
						|
          moveCaret(event);
 | 
						|
        else
 | 
						|
          positionCaret(event);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates a mouse movement over the text component. Does nothing here.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the mouse operation
 | 
						|
   */
 | 
						|
  public void mouseMoved(MouseEvent event)
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * When the click is received from Button 1 then the following actions
 | 
						|
   * are performed here:
 | 
						|
   *
 | 
						|
   * <ul>
 | 
						|
   * <li>If we receive a double click, the caret position (dot) is set
 | 
						|
   *   to the position associated to the mouse click and the word at
 | 
						|
   *   this location is selected. If there is no word at the pointer
 | 
						|
   *   the gap is selected instead.</li>
 | 
						|
   * <li>If we receive a triple click, the caret position (dot) is set
 | 
						|
   *   to the position associated to the mouse click and the line at
 | 
						|
   *   this location is selected.</li>
 | 
						|
   * </ul>
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the click operation
 | 
						|
   */
 | 
						|
  public void mouseClicked(MouseEvent event)
 | 
						|
  {
 | 
						|
    // Do not modify selection if component is disabled.
 | 
						|
    if (!textComponent.isEnabled())
 | 
						|
      return;
 | 
						|
 | 
						|
    int count = event.getClickCount();
 | 
						|
 | 
						|
    if (event.getButton() == MouseEvent.BUTTON1 && count >= 2)
 | 
						|
      {
 | 
						|
        int newDot = getComponent().viewToModel(event.getPoint());
 | 
						|
        JTextComponent t = getComponent();
 | 
						|
 | 
						|
        try
 | 
						|
          {
 | 
						|
            if (count == 3)
 | 
						|
              {
 | 
						|
                setDot(Utilities.getRowStart(t, newDot));
 | 
						|
                moveDot( Utilities.getRowEnd(t, newDot));
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                int wordStart = Utilities.getWordStart(t, newDot);
 | 
						|
 | 
						|
                // When the mouse points at the offset of the first character
 | 
						|
                // in a word Utilities().getPreviousWord will not return that
 | 
						|
                // word but we want to select that. We have to use
 | 
						|
                // Utilities.getWordStart() to get it.
 | 
						|
                if (newDot == wordStart)
 | 
						|
                  {
 | 
						|
                    setDot(wordStart);
 | 
						|
                    moveDot(Utilities.getWordEnd(t, wordStart));
 | 
						|
                  }
 | 
						|
                else
 | 
						|
                  {
 | 
						|
                    int nextWord = Utilities.getNextWord(t, newDot);
 | 
						|
                    int previousWord = Utilities.getPreviousWord(t, newDot);
 | 
						|
                    int previousWordEnd = Utilities.getWordEnd(t, previousWord);
 | 
						|
 | 
						|
                    // If the user clicked in the space between two words,
 | 
						|
                    // then select the space.
 | 
						|
                    if (newDot >= previousWordEnd && newDot <= nextWord)
 | 
						|
                      {
 | 
						|
                        setDot(previousWordEnd);
 | 
						|
                        moveDot(nextWord);
 | 
						|
                      }
 | 
						|
                    // Otherwise select the word under the mouse pointer.
 | 
						|
                    else
 | 
						|
                      {
 | 
						|
                        setDot(previousWord);
 | 
						|
                        moveDot(previousWordEnd);
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
              }
 | 
						|
          }
 | 
						|
        catch(BadLocationException ble)
 | 
						|
          {
 | 
						|
            // TODO: Swallowing ok here?
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates that the mouse has entered the text component. Nothing is done
 | 
						|
   * here.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the mouse operation
 | 
						|
   */
 | 
						|
  public void mouseEntered(MouseEvent event)
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates that the mouse has exited the text component. Nothing is done
 | 
						|
   * here.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the mouse operation
 | 
						|
   */
 | 
						|
  public void mouseExited(MouseEvent event)
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * If the button 1 is pressed, the caret position is updated to the
 | 
						|
   * position of the mouse click and the text component requests the input
 | 
						|
   * focus if it is enabled. If the SHIFT key is held down, the caret will
 | 
						|
   * be moved, which might select the text between the old and new location.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the press operation
 | 
						|
   */
 | 
						|
  public void mousePressed(MouseEvent event)
 | 
						|
  {
 | 
						|
 | 
						|
    // The implementation assumes that consuming the event makes the AWT event
 | 
						|
    // mechanism forget about this event instance and not transfer focus.
 | 
						|
    // By observing how the RI reacts the following behavior has been
 | 
						|
    // implemented (in regard to text components):
 | 
						|
    // - a left-click moves the caret
 | 
						|
    // - a left-click when shift is held down expands the selection
 | 
						|
    // - a right-click or click with any additional mouse button
 | 
						|
    //   on a text component is ignored
 | 
						|
    // - a middle-click positions the caret and pastes the clipboard
 | 
						|
    //   contents.
 | 
						|
    // - a middle-click when shift is held down is ignored
 | 
						|
 | 
						|
    if (SwingUtilities.isLeftMouseButton(event))
 | 
						|
      {
 | 
						|
        // Handle the caret.
 | 
						|
        if (event.isShiftDown() && getDot() != -1)
 | 
						|
          {
 | 
						|
            moveCaret(event);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            positionCaret(event);
 | 
						|
          }
 | 
						|
 | 
						|
        // Handle the focus.
 | 
						|
        if (textComponent != null && textComponent.isEnabled()
 | 
						|
            && textComponent.isRequestFocusEnabled())
 | 
						|
          {
 | 
						|
            textComponent.requestFocus();
 | 
						|
          }
 | 
						|
 | 
						|
        // TODO: Handle double click for selecting words.
 | 
						|
      }
 | 
						|
    else if(event.getButton() == MouseEvent.BUTTON2)
 | 
						|
      {
 | 
						|
        // Special handling for X11-style pasting.
 | 
						|
        if (! event.isShiftDown())
 | 
						|
          {
 | 
						|
            positionCaret(event);
 | 
						|
            textComponent.paste();
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates that a mouse button has been released on the text component.
 | 
						|
   * Nothing is done here.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> describing the mouse operation
 | 
						|
   */
 | 
						|
  public void mouseReleased(MouseEvent event)
 | 
						|
  {
 | 
						|
    // Nothing to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the caret to <code>visible</code> if the text component is editable.
 | 
						|
   *
 | 
						|
   * @param event the <code>FocusEvent</code>
 | 
						|
   */
 | 
						|
  public void focusGained(FocusEvent event)
 | 
						|
  {
 | 
						|
    if (textComponent.isEditable())
 | 
						|
      {
 | 
						|
        setVisible(true);
 | 
						|
        updateTimerStatus();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the caret to <code>invisible</code>.
 | 
						|
   *
 | 
						|
   * @param event the <code>FocusEvent</code>
 | 
						|
   */
 | 
						|
  public void focusLost(FocusEvent event)
 | 
						|
  {
 | 
						|
    if (textComponent.isEditable() && event.isTemporary() == false)
 | 
						|
      {
 | 
						|
        setVisible(false);
 | 
						|
 | 
						|
        // Stop the blinker, if running.
 | 
						|
        if (blinkTimer != null && blinkTimer.isRunning())
 | 
						|
          blinkTimer.stop();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Install (if not present) and start the timer, if the caret must blink. The
 | 
						|
   * caret does not blink if it is invisible, or the component is disabled or
 | 
						|
   * not editable.
 | 
						|
   */
 | 
						|
  private void updateTimerStatus()
 | 
						|
  {
 | 
						|
    if (textComponent.isEnabled() && textComponent.isEditable())
 | 
						|
      {
 | 
						|
        if (blinkTimer == null)
 | 
						|
          initBlinkTimer();
 | 
						|
        if (!blinkTimer.isRunning())
 | 
						|
          blinkTimer.start();
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (blinkTimer != null)
 | 
						|
          blinkTimer.stop();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Moves the caret to the position specified in the <code>MouseEvent</code>.
 | 
						|
   * This will cause a selection if the dot and mark are different.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> from which to fetch the position
 | 
						|
   */
 | 
						|
  protected void moveCaret(MouseEvent event)
 | 
						|
  {
 | 
						|
    int newDot = getComponent().viewToModel(event.getPoint());
 | 
						|
    moveDot(newDot);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Repositions the caret to the position specified in the
 | 
						|
   * <code>MouseEvent</code>.
 | 
						|
   *
 | 
						|
   * @param event the <code>MouseEvent</code> from which to fetch the position
 | 
						|
   */
 | 
						|
  protected void positionCaret(MouseEvent event)
 | 
						|
  {
 | 
						|
    int newDot = getComponent().viewToModel(event.getPoint());
 | 
						|
    setDot(newDot);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Deinstalls this <code>Caret</code> from the specified
 | 
						|
   * <code>JTextComponent</code>. This removes any listeners that have been
 | 
						|
   * registered by this <code>Caret</code>.
 | 
						|
   *
 | 
						|
   * @param c the text component from which to install this caret
 | 
						|
   */
 | 
						|
  public void deinstall(JTextComponent c)
 | 
						|
  {
 | 
						|
    textComponent.removeFocusListener(this);
 | 
						|
    textComponent.removeMouseListener(this);
 | 
						|
    textComponent.removeMouseMotionListener(this);
 | 
						|
    textComponent.getDocument().removeDocumentListener(documentListener);
 | 
						|
    documentListener = null;
 | 
						|
    textComponent.removePropertyChangeListener(propertyChangeListener);
 | 
						|
    propertyChangeListener = null;
 | 
						|
    textComponent = null;
 | 
						|
 | 
						|
    // Deinstall blink timer if present.
 | 
						|
    if (blinkTimer != null)
 | 
						|
      blinkTimer.stop();
 | 
						|
    blinkTimer = null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Installs this <code>Caret</code> on the specified
 | 
						|
   * <code>JTextComponent</code>. This registers a couple of listeners
 | 
						|
   * on the text component.
 | 
						|
   *
 | 
						|
   * @param c the text component on which to install this caret
 | 
						|
   */
 | 
						|
  public void install(JTextComponent c)
 | 
						|
  {
 | 
						|
    textComponent = c;
 | 
						|
    textComponent.addFocusListener(this);
 | 
						|
    textComponent.addMouseListener(this);
 | 
						|
    textComponent.addMouseMotionListener(this);
 | 
						|
    propertyChangeListener = new PropertyChangeHandler();
 | 
						|
    textComponent.addPropertyChangeListener(propertyChangeListener);
 | 
						|
    documentListener = new DocumentHandler();
 | 
						|
 | 
						|
    Document doc = textComponent.getDocument();
 | 
						|
    if (doc != null)
 | 
						|
      doc.addDocumentListener(documentListener);
 | 
						|
 | 
						|
    active = textComponent.isEditable() && textComponent.isEnabled();
 | 
						|
 | 
						|
    repaint();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current visual position of this <code>Caret</code>.
 | 
						|
   *
 | 
						|
   * @param p the Point to use for the saved location. May be <code>null</code>
 | 
						|
   *        to indicate that there is no visual location
 | 
						|
   */
 | 
						|
  public void setMagicCaretPosition(Point p)
 | 
						|
  {
 | 
						|
    magicCaretPosition = p;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current visual position of this <code>Caret</code>.
 | 
						|
   *
 | 
						|
   * @return the current visual position of this <code>Caret</code>
 | 
						|
   *
 | 
						|
   * @see #setMagicCaretPosition
 | 
						|
   */
 | 
						|
  public Point getMagicCaretPosition()
 | 
						|
  {
 | 
						|
    return magicCaretPosition;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current position of the <code>mark</code>. The
 | 
						|
   * <code>mark</code> marks the location in the <code>Document</code> that
 | 
						|
   * is the end of a selection. If there is no selection, the <code>mark</code>
 | 
						|
   * is the same as the <code>dot</code>.
 | 
						|
   *
 | 
						|
   * @return the current position of the mark
 | 
						|
   */
 | 
						|
  public int getMark()
 | 
						|
  {
 | 
						|
    return mark;
 | 
						|
  }
 | 
						|
 | 
						|
  private void clearHighlight()
 | 
						|
  {
 | 
						|
    Highlighter highlighter = textComponent.getHighlighter();
 | 
						|
 | 
						|
    if (highlighter == null)
 | 
						|
      return;
 | 
						|
 | 
						|
    if (selectionVisible)
 | 
						|
      {
 | 
						|
    try
 | 
						|
      {
 | 
						|
        if (highlightEntry != null)
 | 
						|
          highlighter.changeHighlight(highlightEntry, 0, 0);
 | 
						|
 | 
						|
        // Free the global variable which stores the text component with an active
 | 
						|
        // selection.
 | 
						|
        if (componentWithSelection == textComponent)
 | 
						|
          componentWithSelection = null;
 | 
						|
      }
 | 
						|
    catch (BadLocationException e)
 | 
						|
      {
 | 
						|
        // This should never happen.
 | 
						|
        throw new InternalError();
 | 
						|
      }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
    if (highlightEntry != null)
 | 
						|
      {
 | 
						|
        highlighter.removeHighlight(highlightEntry);
 | 
						|
        highlightEntry = null;
 | 
						|
      }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  private void handleHighlight()
 | 
						|
  {
 | 
						|
    Highlighter highlighter = textComponent.getHighlighter();
 | 
						|
 | 
						|
    if (highlighter == null)
 | 
						|
      return;
 | 
						|
 | 
						|
    int p0 = Math.min(dot, mark);
 | 
						|
    int p1 = Math.max(dot, mark);
 | 
						|
 | 
						|
    if (selectionVisible)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            if (highlightEntry == null)
 | 
						|
              highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
 | 
						|
            else
 | 
						|
              highlighter.changeHighlight(highlightEntry, p0, p1);
 | 
						|
 | 
						|
            // If another component currently has a text selection clear that selection
 | 
						|
            // first.
 | 
						|
            if (componentWithSelection != null)
 | 
						|
              if (componentWithSelection != textComponent)
 | 
						|
                {
 | 
						|
                  Caret c = componentWithSelection.getCaret();
 | 
						|
                  c.setDot(c.getDot());
 | 
						|
                }
 | 
						|
            componentWithSelection = textComponent;
 | 
						|
 | 
						|
          }
 | 
						|
        catch (BadLocationException e)
 | 
						|
          {
 | 
						|
            // This should never happen.
 | 
						|
            throw new InternalError();
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (highlightEntry != null)
 | 
						|
          {
 | 
						|
            highlighter.removeHighlight(highlightEntry);
 | 
						|
            highlightEntry = null;
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the visiblity state of the selection.
 | 
						|
   *
 | 
						|
   * @param v <code>true</code> if the selection should be visible,
 | 
						|
   *        <code>false</code> otherwise
 | 
						|
   */
 | 
						|
  public void setSelectionVisible(boolean v)
 | 
						|
  {
 | 
						|
    if (selectionVisible == v)
 | 
						|
      return;
 | 
						|
 | 
						|
    selectionVisible = v;
 | 
						|
    handleHighlight();
 | 
						|
    repaint();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns <code>true</code> if the selection is currently visible,
 | 
						|
   * <code>false</code> otherwise.
 | 
						|
   *
 | 
						|
   * @return <code>true</code> if the selection is currently visible,
 | 
						|
   *         <code>false</code> otherwise
 | 
						|
   */
 | 
						|
  public boolean isSelectionVisible()
 | 
						|
  {
 | 
						|
    return selectionVisible;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Causes the <code>Caret</code> to repaint itself.
 | 
						|
   */
 | 
						|
  protected final void repaint()
 | 
						|
  {
 | 
						|
    getComponent().repaint(x, y, width, height);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Paints this <code>Caret</code> using the specified <code>Graphics</code>
 | 
						|
   * context.
 | 
						|
   *
 | 
						|
   * @param g the graphics context to use
 | 
						|
   */
 | 
						|
  public void paint(Graphics g)
 | 
						|
  {
 | 
						|
    JTextComponent comp = getComponent();
 | 
						|
    if (comp == null)
 | 
						|
      return;
 | 
						|
 | 
						|
    // Make sure the dot has a sane position.
 | 
						|
    dot = Math.min(dot, textComponent.getDocument().getLength());
 | 
						|
    dot = Math.max(dot, 0);
 | 
						|
 | 
						|
    Rectangle rect = null;
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        rect = textComponent.modelToView(dot);
 | 
						|
      }
 | 
						|
    catch (BadLocationException e)
 | 
						|
      {
 | 
						|
        // Let's ignore that. This shouldn't really occur. But if it
 | 
						|
        // does (it seems that this happens when the model is mutating),
 | 
						|
        // it causes no real damage. Uncomment this for debugging.
 | 
						|
        // e.printStackTrace();
 | 
						|
      }
 | 
						|
 | 
						|
    if (rect == null)
 | 
						|
      return;
 | 
						|
 | 
						|
    // Check if paint has possibly been called directly, without a previous
 | 
						|
    // call to damage(). In this case we need to do some cleanup first.
 | 
						|
    if ((x != rect.x) || (y != rect.y))
 | 
						|
      {
 | 
						|
        repaint(); // Erase previous location of caret.
 | 
						|
        x = rect.x;
 | 
						|
        y = rect.y;
 | 
						|
        width = 1;
 | 
						|
        height = rect.height;
 | 
						|
      }
 | 
						|
 | 
						|
    // Now draw the caret on the new position if visible.
 | 
						|
    if (visible && active)
 | 
						|
      {
 | 
						|
        g.setColor(textComponent.getCaretColor());
 | 
						|
        g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns all registered event listeners of the specified type.
 | 
						|
   *
 | 
						|
   * @param listenerType the type of listener to return
 | 
						|
   *
 | 
						|
   * @return all registered event listeners of the specified type
 | 
						|
   */
 | 
						|
  public <T extends EventListener> T[] getListeners(Class<T> listenerType)
 | 
						|
  {
 | 
						|
    return listenerList.getListeners(listenerType);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Registers a {@link ChangeListener} that is notified whenever that state
 | 
						|
   * of this <code>Caret</code> changes.
 | 
						|
   *
 | 
						|
   * @param listener the listener to register to this caret
 | 
						|
   */
 | 
						|
  public void addChangeListener(ChangeListener listener)
 | 
						|
  {
 | 
						|
    listenerList.add(ChangeListener.class, listener);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Removes a {@link ChangeListener} from the list of registered listeners.
 | 
						|
   *
 | 
						|
   * @param listener the listener to remove
 | 
						|
   */
 | 
						|
  public void removeChangeListener(ChangeListener listener)
 | 
						|
  {
 | 
						|
    listenerList.remove(ChangeListener.class, listener);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
 | 
						|
   *
 | 
						|
   * @return all registered {@link ChangeListener}s of this <code>Caret</code>
 | 
						|
   */
 | 
						|
  public ChangeListener[] getChangeListeners()
 | 
						|
  {
 | 
						|
    return (ChangeListener[]) getListeners(ChangeListener.class);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notifies all registered {@link ChangeListener}s that the state
 | 
						|
   * of this <code>Caret</code> has changed.
 | 
						|
   */
 | 
						|
  protected void fireStateChanged()
 | 
						|
  {
 | 
						|
    ChangeListener[] listeners = getChangeListeners();
 | 
						|
 | 
						|
    for (int index = 0; index < listeners.length; ++index)
 | 
						|
      listeners[index].stateChanged(changeEvent);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
 | 
						|
   * is installed.
 | 
						|
   *
 | 
						|
   * @return the <code>JTextComponent</code> on which this <code>Caret</code>
 | 
						|
   *         is installed
 | 
						|
   */
 | 
						|
  protected final JTextComponent getComponent()
 | 
						|
  {
 | 
						|
    return textComponent;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the blink rate of this <code>Caret</code> in milliseconds.
 | 
						|
   * A value of <code>0</code> means that the caret does not blink.
 | 
						|
   *
 | 
						|
   * @return the blink rate of this <code>Caret</code> or <code>0</code> if
 | 
						|
   *         this caret does not blink
 | 
						|
   */
 | 
						|
  public int getBlinkRate()
 | 
						|
  {
 | 
						|
    return blinkRate;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the blink rate of this <code>Caret</code> in milliseconds.
 | 
						|
   * A value of <code>0</code> means that the caret does not blink.
 | 
						|
   *
 | 
						|
   * @param rate the new blink rate to set
 | 
						|
   */
 | 
						|
  public void setBlinkRate(int rate)
 | 
						|
  {
 | 
						|
    if (blinkTimer != null)
 | 
						|
      blinkTimer.setDelay(rate);
 | 
						|
    blinkRate = rate;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current position of this <code>Caret</code> within the
 | 
						|
   * <code>Document</code>.
 | 
						|
   *
 | 
						|
   * @return the current position of this <code>Caret</code> within the
 | 
						|
   *         <code>Document</code>
 | 
						|
   */
 | 
						|
  public int getDot()
 | 
						|
  {
 | 
						|
    return dot;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Moves the <code>dot</code> location without touching the
 | 
						|
   * <code>mark</code>. This is used when making a selection.
 | 
						|
   *
 | 
						|
   * <p>If the underlying text component has a {@link NavigationFilter}
 | 
						|
   * installed the caret will call the corresponding method of that object.</p>
 | 
						|
   *
 | 
						|
   * @param dot the location where to move the dot
 | 
						|
   *
 | 
						|
   * @see #setDot(int)
 | 
						|
   */
 | 
						|
  public void moveDot(int dot)
 | 
						|
  {
 | 
						|
    NavigationFilter filter = textComponent.getNavigationFilter();
 | 
						|
    if (filter != null)
 | 
						|
      filter.moveDot(getBypass(), dot, Bias.Forward);
 | 
						|
    else
 | 
						|
      moveDotImpl(dot);
 | 
						|
  }
 | 
						|
 | 
						|
  void moveDotImpl(int dot)
 | 
						|
  {
 | 
						|
    if (dot >= 0)
 | 
						|
      {
 | 
						|
        Document doc = textComponent.getDocument();
 | 
						|
        if (doc != null)
 | 
						|
          this.dot = Math.min(dot, doc.getLength());
 | 
						|
        this.dot = Math.max(this.dot, 0);
 | 
						|
 | 
						|
        handleHighlight();
 | 
						|
 | 
						|
        appear();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current position of this <code>Caret</code> within the
 | 
						|
   * <code>Document</code>. This also sets the <code>mark</code> to the new
 | 
						|
   * location.
 | 
						|
   *
 | 
						|
   * <p>If the underlying text component has a {@link NavigationFilter}
 | 
						|
   * installed the caret will call the corresponding method of that object.</p>
 | 
						|
   *
 | 
						|
   * @param dot
 | 
						|
   *          the new position to be set
 | 
						|
   * @see #moveDot(int)
 | 
						|
   */
 | 
						|
  public void setDot(int dot)
 | 
						|
  {
 | 
						|
    NavigationFilter filter = textComponent.getNavigationFilter();
 | 
						|
    if (filter != null)
 | 
						|
      filter.setDot(getBypass(), dot, Bias.Forward);
 | 
						|
    else
 | 
						|
      setDotImpl(dot);
 | 
						|
  }
 | 
						|
 | 
						|
  void setDotImpl(int dot)
 | 
						|
  {
 | 
						|
    if (dot >= 0)
 | 
						|
      {
 | 
						|
        Document doc = textComponent.getDocument();
 | 
						|
        if (doc != null)
 | 
						|
          this.dot = Math.min(dot, doc.getLength());
 | 
						|
        this.dot = Math.max(this.dot, 0);
 | 
						|
        this.mark = this.dot;
 | 
						|
 | 
						|
        clearHighlight();
 | 
						|
 | 
						|
        appear();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Show the caret (may be hidden due blinking) and adjust the timer not to
 | 
						|
   * hide it (possibly immediately).
 | 
						|
   *
 | 
						|
   * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
 | 
						|
   */
 | 
						|
  void appear()
 | 
						|
  {
 | 
						|
    // All machinery is only required if the carret is blinking.
 | 
						|
    if (blinkListener != null)
 | 
						|
      {
 | 
						|
        blinkListener.ignoreNextEvent = true;
 | 
						|
 | 
						|
        // If the caret is visible, erase the current position by repainting
 | 
						|
        // over.
 | 
						|
        if (visible)
 | 
						|
          repaint();
 | 
						|
 | 
						|
        // Draw the caret in the new position.
 | 
						|
        visible = true;
 | 
						|
 | 
						|
        Rectangle area = null;
 | 
						|
        int dot = getDot();
 | 
						|
        try
 | 
						|
          {
 | 
						|
            area = getComponent().modelToView(dot);
 | 
						|
          }
 | 
						|
        catch (BadLocationException e)
 | 
						|
          {
 | 
						|
            // Let's ignore that. This shouldn't really occur. But if it
 | 
						|
            // does (it seems that this happens when the model is mutating),
 | 
						|
            // it causes no real damage. Uncomment this for debugging.
 | 
						|
            // e.printStackTrace();
 | 
						|
          }
 | 
						|
        if (area != null)
 | 
						|
          {
 | 
						|
            adjustVisibility(area);
 | 
						|
            if (getMagicCaretPosition() == null)
 | 
						|
              setMagicCaretPosition(new Point(area.x, area.y));
 | 
						|
            damage(area);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    repaint();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns <code>true</code> if this <code>Caret</code> is blinking,
 | 
						|
   * and <code>false</code> if not. The returned value is independent of
 | 
						|
   * the visiblity of this <code>Caret</code> as returned by {@link #isVisible()}.
 | 
						|
   *
 | 
						|
   * @return <code>true</code> if this <code>Caret</code> is blinking,
 | 
						|
   *         and <code>false</code> if not.
 | 
						|
   * @see #isVisible()
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public boolean isActive()
 | 
						|
  {
 | 
						|
    if (blinkTimer != null)
 | 
						|
      return blinkTimer.isRunning();
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns <code>true</code> if this <code>Caret</code> is currently visible,
 | 
						|
   * and <code>false</code> if it is not.
 | 
						|
   *
 | 
						|
   * @return <code>true</code> if this <code>Caret</code> is currently visible,
 | 
						|
   *         and <code>false</code> if it is not
 | 
						|
   */
 | 
						|
  public boolean isVisible()
 | 
						|
  {
 | 
						|
    return visible && active;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the visibility state of the caret. <code>true</code> shows the
 | 
						|
   * <code>Caret</code>, <code>false</code> hides it.
 | 
						|
   *
 | 
						|
   * @param v the visibility to set
 | 
						|
   */
 | 
						|
  public void setVisible(boolean v)
 | 
						|
  {
 | 
						|
    if (v != visible)
 | 
						|
      {
 | 
						|
        visible = v;
 | 
						|
        updateTimerStatus();
 | 
						|
        Rectangle area = null;
 | 
						|
        int dot = getDot();
 | 
						|
        try
 | 
						|
          {
 | 
						|
            area = getComponent().modelToView(dot);
 | 
						|
          }
 | 
						|
        catch (BadLocationException e)
 | 
						|
          {
 | 
						|
            AssertionError ae;
 | 
						|
            ae = new AssertionError("Unexpected bad caret location: " + dot);
 | 
						|
            ae.initCause(e);
 | 
						|
            throw ae;
 | 
						|
          }
 | 
						|
        if (area != null)
 | 
						|
          damage(area);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the {@link Highlighter.HighlightPainter} that should be used
 | 
						|
   * to paint the selection.
 | 
						|
   *
 | 
						|
   * @return the {@link Highlighter.HighlightPainter} that should be used
 | 
						|
   *         to paint the selection
 | 
						|
   */
 | 
						|
  protected Highlighter.HighlightPainter getSelectionPainter()
 | 
						|
  {
 | 
						|
    return DefaultHighlighter.DefaultPainter;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Updates the carets rectangle properties to the specified rectangle and
 | 
						|
   * repaints the caret.
 | 
						|
   *
 | 
						|
   * @param r the rectangle to set as the caret rectangle
 | 
						|
   */
 | 
						|
  protected void damage(Rectangle r)
 | 
						|
  {
 | 
						|
    if (r == null)
 | 
						|
      return;
 | 
						|
    x = r.x;
 | 
						|
    y = r.y;
 | 
						|
    width = 1;
 | 
						|
    // height is normally set in paint and we leave it untouched. However, we
 | 
						|
    // must set a valid value here, since otherwise the painting mechanism
 | 
						|
    // sets a zero clip and never calls paint.
 | 
						|
    if (height <= 0)
 | 
						|
      try
 | 
						|
        {
 | 
						|
          height = textComponent.modelToView(dot).height;
 | 
						|
        }
 | 
						|
      catch (BadLocationException ble)
 | 
						|
        {
 | 
						|
          // Should not happen.
 | 
						|
          throw new InternalError("Caret location not within document range.");
 | 
						|
        }
 | 
						|
 | 
						|
    repaint();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adjusts the text component so that the caret is visible. This default
 | 
						|
   * implementation simply calls
 | 
						|
   * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
 | 
						|
   * Subclasses may wish to change this.
 | 
						|
   */
 | 
						|
  protected void adjustVisibility(Rectangle rect)
 | 
						|
  {
 | 
						|
    getComponent().scrollRectToVisible(rect);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Initializes the blink timer.
 | 
						|
   */
 | 
						|
  private void initBlinkTimer()
 | 
						|
  {
 | 
						|
    // Setup the blink timer.
 | 
						|
    blinkListener = new BlinkTimerListener();
 | 
						|
    blinkTimer = new Timer(getBlinkRate(), blinkListener);
 | 
						|
    blinkTimer.setRepeats(true);
 | 
						|
  }
 | 
						|
 | 
						|
}
 |