⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 configurablecaret.java

📁 具有不同语法高亮的编辑器实例
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
 * 12/21/2004
 *
 * ConfigurableCaret.java - The caret used by RTextArea.
 * Copyright (C) 2004 Robert Futrell
 * email@address.com
 * www.website.com
 *
 * This program 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
 * of the License, or any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.fife.ui.rtextarea;

import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.beans.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.text.*;
import java.util.EventListener;

/**
 * The caret used by <code>RTextArea</code>.  This caret has all of the
 * properties that <code>javax.swing.text.DefaultCaret</code> does, as
 * well as adding the following nicities:
 *
 * <ul>
 *   <li>This caret can paint itself several different ways:
 *      <ol>
 *         <li>As a vertical line (like <code>DefaultCaret</code>)</li>
 *         <li>As an underline</li>
 *         <li>As a "block caret"</li>
 *         <li>As a rectangle around the current character</li>
 *      </ol></li>
 *   <li>On Microsoft Windows and other operating systems that do not
 *       support system selection (i.e., selecting text, then pasting
 *       via the middle mouse button), clicking the middle mouse button
 *       will cause a regular paste operation to occur.  On systems
 *       that support system selection (i.e., all UNIX variants),
 *       the middle mouse button will behave normally.</li>
 *   <li>The caret moves to the undo/redo location when an undo or
 *       redo occurs (<code>DefaultCaret</code> doesn't do this
 *       in J2SE1.4.x but does in 1.5+).</li>
 * </ul>
 *
 * @author Robert Futrell
 * @version 0.5
 */
public class ConfigurableCaret
    extends Rectangle
    implements Caret, FocusListener, MouseListener, MouseMotionListener {

  /**
   *
   */
  private static final long serialVersionUID = -5964038283301633119L;

  /**
   * The minimum value of a caret style.
   */
  public static final int MIN_STYLE = 0;

  /**
   * The vertical line style.
   */
  public static final int VERTICAL_LINE_STYLE = 0;

  /**
   * The horizontal line style.
   */
  public static final int UNDERLINE_STYLE = 1;

  /**
   * The block style.
   */
  public static final int BLOCK_STYLE = 2;

  /**
   * The block border style.
   */
  public static final int BLOCK_BORDER_STYLE = 3;

  /**
   * The maximum value of a caret style.
   */
  public static final int MAX_STYLE = BLOCK_BORDER_STYLE;

  /**
   * The event listener list.
   */
  protected EventListenerList listenerList = new EventListenerList();

  /**
   * The change event for the model.
   * Only one ChangeEvent is needed per model instance since the
   * event's only (read-only) state is the source property.  The source
   * of events generated here is always "this".
   */
  protected transient ChangeEvent changeEvent = null;

  // package-private to avoid inner classes private member
  // access bug
  RTextArea component;

  boolean visible;
  boolean active;
  int dot;
  int mark;
  List selectionTagList;
  boolean selectionVisible;
  Timer flasher;
  Point magicCaretPosition;
  Object selectionTag;
  transient Handler handler = new Handler();
  private transient NavigationFilter.FilterBypass filterBypass;
  static private transient Action selectWord = null;
  static private transient Action selectLine = null;

  /**
   * This is used to indicate if the caret currently owns the selection.
   * This is always false if the system does not support the system
   * clipboard.
   */
  private boolean ownsSelection;

  /**
   * If this is true, the location of the dot is updated regardless of
   * the current location. This is set in the DocumentListener
   * such that even if the model location of dot hasn't changed (perhaps do
   * to a forward delete) the visual location is updated.
   */
  private boolean forceCaretPositionChange;

  /**
   * Whether or not mouseReleased should adjust the caret and focus.
   * This flag is set by mousePressed if it wanted to adjust the caret
   * and focus but couldn't because of a possible DnD operation.
   */
  private transient boolean shouldHandleRelease;

  /**
   * holds last MouseEvent which caused the word selection
   */
  private transient MouseEvent selectedWordEvent = null;

  /**
   * Used for fastest-possible retrieval of the character at the
   * caret's position in the document.
   */
  private Segment seg;

  /**
   * Whether the caret is a vertical line, a horizontal line, or a block.
   */
  private int style;

  /**
   * The selection painter.  By default this paints selections with the
   * text area's selection color.
   */
  private Highlighter.HighlightPainter selectionPainter;

      /*****************************************************************************/

  /**
   * Creates the caret using <code>VERTICAL_LINE_STYLE</code>.
   */
  public ConfigurableCaret() {
    this(VERTICAL_LINE_STYLE);
  }

      /*****************************************************************************/

  /**
   * Constructs a new <code>ConfigurableCaret</code>.
   *
   * @param style The style to use when painting the caret.  If this isn't
   *              one of <code>VERTICAL_LINE_STYLE</code>,
   *              <code>UNDERLINE_STYLE</code>, or <code>BLOCK_STYLE</code>,
   *              then <code>VERTICAL_LINE_STYLE</code> is used.
   */
  public ConfigurableCaret(int style) {
    super();
    seg = new Segment();
    setStyle(style);
    selectionPainter = new ChangableHighlightPainter();
  }

      /*****************************************************************************/

  /**
   * Adds a listener to track whenever the caret position has
   * been changed.
   *
   * @param l the listener
   * @see Caret#addChangeListener
   */
  public void addChangeListener(ChangeListener l) {
    listenerList.add(ChangeListener.class, l);
  }

      /*****************************************************************************/

  /**
   * Adjusts the caret location based on the MouseEvent.
   */
  private void adjustCaret(MouseEvent e) {
    if ( (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0 && getDot() != -1) {
      moveCaret(e);
    }
    else {
      positionCaret(e);
    }
  }

      /*****************************************************************************/

  void adjustCaretAndFocus(MouseEvent e) {
    adjustCaret(e);
    adjustFocus(false);
  }

      /*****************************************************************************/

  /**
   * Adjusts the focus, if necessary.
   *
   * @param inWindow if true indicates requestFocusInWindow should be used
   */
  private void adjustFocus(boolean inWindow) {
    if ( (component != null) && component.isEnabled() &&
        component.isRequestFocusEnabled()) {
      if (inWindow) {
        component.requestFocusInWindow();
      }
      else {
        component.requestFocus();
      }
    }
  }

      /*****************************************************************************/

  /**
   * Scrolls the associated view (if necessary) to make
   * the caret visible.  Since how this should be done
   * is somewhat of a policy, this method can be
   * reimplemented to change the behavior.  By default
   * the scrollRectToVisible method is called on the
   * associated component.
   *
   * @param nloc the new position to scroll to
   */
  protected void adjustVisibility(Rectangle nloc) {
    if (component == null) {
      return;
    }
    if (SwingUtilities.isEventDispatchThread()) {
      component.scrollRectToVisible(nloc);
    }
    else {
      SwingUtilities.invokeLater(new SafeScroller(nloc));
    }
  }

      /*****************************************************************************/

  /**
   * Sets the caret position (dot) to a new location.  This
   * causes the old and new location to be repainted.  It
   * also makes sure that the caret is within the visible
   * region of the view, if the view is scrollable.
   */
  void changeCaretPosition(int dot) {

    // repaint the old position and set the new value of
    // the dot.
    repaint();

    // Make sure the caret is visible if this window has the focus.
    if (flasher != null && flasher.isRunning()) {
      visible = true;
      flasher.restart();
    }

    // notify listeners at the caret moved
    this.dot = dot;
    fireStateChanged();

    updateSystemSelection();

    setMagicCaretPosition(null);

    // We try to repaint the caret later, since things
    // may be unstable at the time this is called
    // (i.e. we don't want to depend upon notification
    // order or the fact that this might happen on
    // an unsafe thread).
    Runnable callRepaintNewCaret = new Runnable() {
      public void run() {
        repaintNewCaret();
      }
    };
    SwingUtilities.invokeLater(callRepaintNewCaret);

  }

      /*****************************************************************************/

  /**
   * Damages the area surrounding the caret to cause it to be repainted in
   * a new location.  If <code>paint()</code> is reimplemented, this method
   * should also be reimplemented.  This method should update the caret
   * bounds (x, y, width, and height).
   *
   * @param r  the current location of the caret
   * @see #paint
   */
  protected synchronized void damage(Rectangle r) {
    if (r != null) {
      x = r.x - 1;
      y = r.y;
      width = r.width + 4;
      height = r.height;
      repaint();
    }
  }

      /*****************************************************************************/

  /**
   * Called when the UI is being removed from the
   * interface of a JTextComponent.  This is used to
   * unregister any listeners that were attached.
   *
   * @param c The text component.  If this is not an
   *          <code>RTextArea</code>, an <code>Exception</code>
   *          will be thrown.
   * @see Caret#deinstall
   */
  public void deinstall(JTextComponent c) {
    if (! (c instanceof RTextArea)) {
      throw new IllegalArgumentException(
          "c must be instance of RTextArea");
    }
    c.removeMouseListener(this);
    c.removeMouseMotionListener(this);
    c.removeFocusListener(this);
    c.removePropertyChangeListener(handler);
    Document doc = c.getDocument();
    if (doc != null) {
      doc.removeDocumentListener(handler);
    }
    synchronized (this) {
      component = null;
    }
    if (flasher != null) {
      flasher.stop();
    }
  }

      /*****************************************************************************/

  /**
   * This is invoked after the document changes to verify the current
   * dot/mark is valid. We do this in case the <code>NavigationFilter</code>
   * changed where to position the dot, that resulted in the current location
   * being bogus.
   */
  private void ensureValidPosition() {
    int length = component.getDocument().getLength();
    if (dot > length || mark > length) {
      // Current location is bogus and filter likely vetoed the
      // change, force the reset without giving the filter a
      // chance at changing it.
      handleSetDot(length);
    }
  }

      /*****************************************************************************/

  /**
   * Compares this object to the specified object.
   * The superclass behavior of comparing rectangles
   * is not desired, so this is changed to the Object
   * behavior.
   *
   * @param     obj   the object to compare this font with
   * @return    <code>true</code> if the objects are equal;
   *            <code>false</code> otherwise
   */
  public boolean equals(Object obj) {
    return (this == obj);
  }

      /*****************************************************************************/

  /**
   * Notifies all listeners that have registered interest for
   * notification on this event type.  The event instance
   * is lazily created using the parameters passed into
   * the fire method.  The listener list is processed last to first.
   *
   * @see EventListenerList
   */
  protected void fireStateChanged() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
      if (listeners[i] == ChangeListener.class) {
        // Lazily create the event:
        if (changeEvent == null) {
          changeEvent = new ChangeEvent(this);
        }
        ( (ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
      }
    }
  }

      /*****************************************************************************/

  /**
   * Called when the component containing the caret gains
   * focus.  This is implemented to set the caret to visible
   * if the component is editable.
   *
   * @param e the focus event
   * @see FocusListener#focusGained
   */
  public void focusGained(FocusEvent e) {
    if (component.isEnabled()) {
      if (component.isEditable()) {
        setVisible(true);
      }
      setSelectionVisible(true);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -