📄 configurablecaret.java~1~
字号:
/* * 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -