1914 lines · 66.2 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; 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 java.util.EventListener;import sun.swing.SwingUtilities2; /** * A default implementation of Caret.  The caret is rendered as * a vertical line in the color specified by the CaretColor property * of the associated JTextComponent.  It can blink at the rate specified * by the BlinkRate property. * <p> * This implementation expects two sources of asynchronous notification. * The timer thread fires asynchronously, and causes the caret to simply * repaint the most recent bounding box.  The caret also tracks change * as the document is modified.  Typically this will happen on the * event dispatch thread as a result of some mouse or keyboard event. * The caret behavior on both synchronous and asynchronous documents updates * is controlled by <code>UpdatePolicy</code> property. The repaint of the * new caret location will occur on the event thread in any case, as calls to * <code>modelToView</code> are only safe on the event thread. * <p> * The caret acts as a mouse and focus listener on the text component * it has been installed in, and defines the caret semantics based upon * those events.  The listener methods can be reimplemented to change the * semantics. * By default, the first mouse button will be used to set focus and caret * position.  Dragging the mouse pointer with the first mouse button will * sweep out a selection that is contiguous in the model.  If the associated * text component is editable, the caret will become visible when focus * is gained, and invisible when focus is lost. * <p> * The Highlighter bound to the associated text component is used to * render the selection by default. * Selection appearance can be customized by supplying a * painter to use for the highlights.  By default a painter is used that * will render a solid color as specified in the associated text component * in the <code>SelectionColor</code> property.  This can easily be changed * by reimplementing the * {@link #getSelectionPainter getSelectionPainter} * method. * <p> * A customized caret appearance can be achieved by reimplementing * the paint method.  If the paint method is changed, the damage method * should also be reimplemented to cause a repaint for the area needed * to render the caret.  The caret extends the Rectangle class which * is used to hold the bounding box for where the caret was last rendered. * This enables the caret to repaint in a thread-safe manner when the * caret moves without making a call to modelToView which is unstable * between model updates and view repair (i.e. the order of delivery * to DocumentListeners is not guaranteed). * <p> * The magic caret position is set to null when the caret position changes. * A timer is used to determine the new location (after the caret change). * When the timer fires, if the magic caret position is still null it is * reset to the current caret position. Any actions that change * the caret position and want the magic caret position to remain the * same, must remember the magic caret position, change the cursor, and * then set the magic caret position to its original value. This has the * benefit that only actions that want the magic caret position to persist * (such as open/down) need to know about it. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing.  As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author  Timothy Prinzing * @see     Caret */public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener {     /**     * Indicates that the caret position is to be updated only when     * document changes are performed on the Event Dispatching Thread.     * @see #setUpdatePolicy     * @see #getUpdatePolicy     * @since 1.5     */    public static final int UPDATE_WHEN_ON_EDT = 0;     /**     * Indicates that the caret should remain at the same     * absolute position in the document regardless of any document     * updates, except when the document length becomes less than     * the current caret position due to removal. In that case the caret     * position is adjusted to the end of the document.     *     * @see #setUpdatePolicy     * @see #getUpdatePolicy     * @since 1.5     */    public static final int NEVER_UPDATE = 1;     /**     * Indicates that the caret position is to be <b>always</b>     * updated accordingly to the document changes regardless whether     * the document updates are performed on the Event Dispatching Thread     * or not.     *     * @see #setUpdatePolicy     * @see #getUpdatePolicy     * @since 1.5     */    public static final int ALWAYS_UPDATE = 2;     /**     * Constructs a default caret.     */    public DefaultCaret() {    }     /**     * Sets the caret movement policy on the document updates. Normally     * the caret updates its absolute position within the document on     * insertions occurred before or at the caret position and     * on removals before the caret position. 'Absolute position'     * means here the position relative to the start of the document.     * For example if     * a character is typed within editable text component it is inserted     * at the caret position and the caret moves to the next absolute     * position within the document due to insertion and if     * <code>BACKSPACE</code> is typed then caret decreases its absolute     * position due to removal of a character before it. Sometimes     * it may be useful to turn off the caret position updates so that     * the caret stays at the same absolute position within the     * document position regardless of any document updates.     * <p>     * The following update policies are allowed:     * <ul>     *   <li><code>NEVER_UPDATE</code>: the caret stays at the same     *       absolute position in the document regardless of any document     *       updates, except when document length becomes less than     *       the current caret position due to removal. In that case caret     *       position is adjusted to the end of the document.     *       The caret doesn't try to keep itself visible by scrolling     *       the associated view when using this policy. </li>     *   <li><code>ALWAYS_UPDATE</code>: the caret always tracks document     *       changes. For regular changes it increases its position     *       if an insertion occurs before or at its current position,     *       and decreases position if a removal occurs before     *       its current position. For undo/redo updates it is always     *       moved to the position where update occurred. The caret     *       also tries to keep itself visible by calling     *       <code>adjustVisibility</code> method.</li>     *   <li><code>UPDATE_WHEN_ON_EDT</code>: acts like <code>ALWAYS_UPDATE</code>     *       if the document updates are performed on the Event Dispatching Thread     *       and like <code>NEVER_UPDATE</code> if updates are performed on     *       other thread. </li>     * </ul> <p>     * The default property value is <code>UPDATE_WHEN_ON_EDT</code>.     *     * @param policy one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,     * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>     * @throws IllegalArgumentException if invalid value is passed     *     * @see #getUpdatePolicy     * @see #adjustVisibility     * @see #UPDATE_WHEN_ON_EDT     * @see #NEVER_UPDATE     * @see #ALWAYS_UPDATE     *     * @since 1.5     */    public void setUpdatePolicy(int policy) {        updatePolicy = policy;    }     /**     * Gets the caret movement policy on document updates.     *     * @return one of the following values : <code>UPDATE_WHEN_ON_EDT</code>,     * <code>NEVER_UPDATE</code>, <code>ALWAYS_UPDATE</code>     *     * @see #setUpdatePolicy     * @see #UPDATE_WHEN_ON_EDT     * @see #NEVER_UPDATE     * @see #ALWAYS_UPDATE     *     * @since 1.5     */    public int getUpdatePolicy() {        return updatePolicy;    }     /**     * Gets the text editor component that this caret is     * is bound to.     *     * @return the component     */    protected final JTextComponent getComponent() {        return component;    }     /**     * Cause the caret to be painted.  The repaint     * area is the bounding box of the caret (i.e.     * the caret rectangle or <em>this</em>).     * <p>     * This method is thread safe, although most Swing methods     * are not. Please see     * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How     * to Use Threads</A> for more information.     */    protected final synchronized void repaint() {        if (component != null) {            component.repaint(x, y, width, height);        }    }     /**     * Damages the area surrounding the caret to cause     * it to be repainted in a new location.  If paint()     * 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) {            int damageWidth = getCaretWidth(r.height);            x = r.x - 4 - (damageWidth >> 1);            y = r.y;            width = 9 + damageWidth;            height = r.height;            repaint();        }    }     /**     * 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));        }    }     /**     * Gets the painter for the Highlighter.     *     * @return the painter     */    protected Highlighter.HighlightPainter getSelectionPainter() {        return DefaultHighlighter.DefaultPainter;    }     /**     * Tries to set the position of the caret from     * the coordinates of a mouse event, using viewToModel().     *     * @param e the mouse event     */    protected void positionCaret(MouseEvent e) {        Point pt = new Point(e.getX(), e.getY());        Position.Bias[] biasRet = new Position.Bias[1];        int pos = component.getUI().viewToModel(component, pt, biasRet);        if(biasRet[0] == null)            biasRet[0] = Position.Bias.Forward;        if (pos >= 0) {            setDot(pos, biasRet[0]);        }    }     /**     * Tries to move the position of the caret from     * the coordinates of a mouse event, using viewToModel().     * This will cause a selection if the dot and mark     * are different.     *     * @param e the mouse event     */    protected void moveCaret(MouseEvent e) {        Point pt = new Point(e.getX(), e.getY());        Position.Bias[] biasRet = new Position.Bias[1];        int pos = component.getUI().viewToModel(component, pt, biasRet);        if(biasRet[0] == null)            biasRet[0] = Position.Bias.Forward;        if (pos >= 0) {            moveDot(pos, biasRet[0]);        }    }     // --- FocusListener methods --------------------------     /**     * 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);        }    }     /**     * Called when the component containing the caret loses     * focus.  This is implemented to set the caret to visibility     * to false.     *     * @param e the focus event     * @see FocusListener#focusLost     */    public void focusLost(FocusEvent e) {        setVisible(false);        setSelectionVisible(ownsSelection || e.isTemporary());    }      /**     * Selects word based on the MouseEvent     */    private void selectWord(MouseEvent e) {        if (selectedWordEvent != null            && selectedWordEvent.getX() == e.getX()            && selectedWordEvent.getY() == e.getY()) {            //we already done selection for this            return;        }                    Action a = null;                    ActionMap map = getComponent().getActionMap();                    if (map != null) {                        a = map.get(DefaultEditorKit.selectWordAction);                    }                    if (a == null) {                        if (selectWord == null) {                            selectWord = new DefaultEditorKit.SelectWordAction();                        }                        a = selectWord;                    }                    a.actionPerformed(new ActionEvent(getComponent(),                                                      ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));        selectedWordEvent = e;    }     // --- MouseListener methods -----------------------------------     /**     * Called when the mouse is clicked.  If the click was generated     * from button1, a double click selects a word,     * and a triple click the current line.     *     * @param e the mouse event     * @see MouseListener#mouseClicked     */    public void mouseClicked(MouseEvent e) {        int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);         if (! e.isConsumed()) {            if (SwingUtilities.isLeftMouseButton(e)) {                // mouse 1 behavior                if(nclicks == 1) {                    selectedWordEvent = null;                } else if(nclicks == 2                          && SwingUtilities2.canEventAccessSystemClipboard(e)) {                    selectWord(e);                    selectedWordEvent = null;                } else if(nclicks == 3                          && SwingUtilities2.canEventAccessSystemClipboard(e)) {                    Action a = null;                    ActionMap map = getComponent().getActionMap();                    if (map != null) {                        a = map.get(DefaultEditorKit.selectLineAction);                    }                    if (a == null) {                        if (selectLine == null) {                            selectLine = new DefaultEditorKit.SelectLineAction();                        }                        a = selectLine;                    }                    a.actionPerformed(new ActionEvent(getComponent(),                                                      ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers()));                }            } else if (SwingUtilities.isMiddleMouseButton(e)) {                // mouse 2 behavior                if (nclicks == 1 && component.isEditable() && component.isEnabled()                    && SwingUtilities2.canEventAccessSystemClipboard(e)) {                    // paste system selection, if it exists                    JTextComponent c = (JTextComponent) e.getSource();                    if (c != null) {                        try {                            Toolkit tk = c.getToolkit();                            Clipboard buffer = tk.getSystemSelection();                            if (buffer != null) {                                // platform supports system selections, update it.                                adjustCaret(e);                                TransferHandler th = c.getTransferHandler();                                if (th != null) {                                    Transferable trans = null;                                     try {                                        trans = buffer.getContents(null);                                    } catch (IllegalStateException ise) {                                        // clipboard was unavailable                                        UIManager.getLookAndFeel().provideErrorFeedback(c);                                    }                                     if (trans != null) {                                        th.importData(c, trans);                                    }                                }                                adjustFocus(true);                            }                        } catch (HeadlessException he) {                            // do nothing... there is no system clipboard                        }                    }                }            }        }    }     /**     * If button 1 is pressed, this is implemented to     * request focus on the associated text component,     * and to set the caret position. If the shift key is held down,     * the caret will be moved, potentially resulting in a selection,     * otherwise the     * caret position will be set to the new location.  If the component     * is not enabled, there will be no request for focus.     *     * @param e the mouse event     * @see MouseListener#mousePressed     */    public void mousePressed(MouseEvent e) {        int nclicks = SwingUtilities2.getAdjustedClickCount(getComponent(), e);         if (SwingUtilities.isLeftMouseButton(e)) {            if (e.isConsumed()) {                shouldHandleRelease = true;            } else {                shouldHandleRelease = false;                adjustCaretAndFocus(e);                if (nclicks == 2                    && SwingUtilities2.canEventAccessSystemClipboard(e)) {                    selectWord(e);                }            }        }    } 

Lines 1–500 of 1914.