📄 configurablecaret.java
字号:
/*
* 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 + -