FileDocCategorySizeDatePackage
DefaultCaret.javaAPI DocJava SE 5 API61456Fri Aug 26 14:58:14 BST 2005javax.swing.text

DefaultCaret

public class DefaultCaret extends Rectangle implements MouseListener, MouseMotionListener, FocusListener, Caret
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.

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 UpdatePolicy property. The repaint of the new caret location will occur on the event thread in any case, as calls to modelToView are only safe on the event thread.

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.

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 SelectionColor property. This can easily be changed by reimplementing the getSelectionHighlighter method.

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).

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.

Warning: 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 JavaBeansTM has been added to the java.beans package. Please see {@link java.beans.XMLEncoder}.

author
Timothy Prinzing
version
1.140 08/09/05
see
Caret

Fields Summary
public static final int
UPDATE_WHEN_ON_EDT
Indicates that the caret position is to be updated only when document changes are performed on the Event Dispatching Thread.
public static final int
NEVER_UPDATE
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.
public static final int
ALWAYS_UPDATE
Indicates that the caret position is to be always updated accordingly to the document changes regardless whether the document updates are performed on the Event Dispatching Thread or not.
protected EventListenerList
listenerList
The event listener list.
protected transient ChangeEvent
changeEvent
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".
JTextComponent
component
int
updatePolicy
boolean
visible
boolean
active
int
dot
int
mark
Object
selectionTag
boolean
selectionVisible
Timer
flasher
Point
magicCaretPosition
transient Position$Bias
dotBias
transient Position$Bias
markBias
boolean
dotLTR
boolean
markLTR
transient Handler
handler
private transient int[]
flagXPoints
private transient int[]
flagYPoints
private transient NavigationFilter$FilterBypass
filterBypass
private static transient Action
selectWord
private static transient Action
selectLine
private boolean
ownsSelection
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
forceCaretPositionChange
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 transient boolean
shouldHandleRelease
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 MouseEvent
selectedWordEvent
holds last MouseEvent which caused the word selection
private int
caretWidth
The width of the caret in pixels.
private float
aspectRatio
Constructors Summary
public DefaultCaret()
Constructs a default caret.


             
      
    
Methods Summary
private boolean_contains(int X, int Y, int W, int H)

        int w = this.width;
        int h = this.height;
        if ((w | h | W | H) < 0) {
            // At least one of the dimensions is negative...
            return false;
        }
        // Note: if any dimension is zero, tests below must return false...
        int x = this.x;
        int y = this.y;
        if (X < x || Y < y) {
            return false;
        }
        if (W > 0) {
            w += x;
            W += X;
            if (W <= X) {
                // X+W overflowed or W was zero, return false if...
                // either original w or W was zero or
                // x+w did not overflow or
                // the overflowed x+w is smaller than the overflowed X+W
                if (w >= x || W > w) return false;
            } else {
                // X+W did not overflow and W was not zero, return false if...
                // original w was zero or
                // x+w did not overflow and x+w is smaller than X+W
                if (w >= x && W > w) return false;
            }
        }
        else if ((x + w) < X) {
            return false;
        }
        if (H > 0) {
            h += y;
            H += Y;
            if (H <= Y) {
                if (h >= y || H > h) return false;
            } else {
                if (h >= y && H > h) return false;
            }
        }
        else if ((y + h) < Y) {
            return false;
        }
        return true;
    
public voidaddChangeListener(javax.swing.event.ChangeListener l)
Adds a listener to track whenever the caret position has been changed.

param
l the listener
see
Caret#addChangeListener

	listenerList.add(ChangeListener.class, l);
    
private voidadjustCaret(java.awt.event.MouseEvent e)
Adjusts the caret location based on the MouseEvent.

	if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0 &&
	    getDot() != -1) {
	    moveCaret(e);
	} else {
	    positionCaret(e);
	}
    
voidadjustCaretAndFocus(java.awt.event.MouseEvent e)

        adjustCaret(e);
        adjustFocus(false);
    
private voidadjustFocus(boolean inWindow)
Adjusts the focus, if necessary.

param
inWindow if true indicates requestFocusInWindow should be used

	if ((component != null) && component.isEnabled() &&
                                   component.isRequestFocusEnabled()) {
            if (inWindow) {
                component.requestFocusInWindow();
            }
            else {
                component.requestFocus();
            }
	}
    
protected voidadjustVisibility(java.awt.Rectangle nloc)
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

        if(component == null) {
            return;
        }
        if (SwingUtilities.isEventDispatchThread()) {
                component.scrollRectToVisible(nloc);
        } else {
            SwingUtilities.invokeLater(new SafeScroller(nloc));
        }
    
voidchangeCaretPosition(int dot, javax.swing.text.Position$Bias dotBias)
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.

	// 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;
        this.dotBias = dotBias;
        dotLTR = isPositionLTR(dot, dotBias);
        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);
    
protected synchronized voiddamage(java.awt.Rectangle r)
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

        if (r != null) {
            int damageWidth = getCaretWidth(r.height);
            x = r.x - 4 - (damageWidth >> 1);
            y = r.y;
            width = 9 + damageWidth;
            height = r.height;
            repaint();
        }
    
public voiddeinstall(javax.swing.text.JTextComponent c)
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 component
see
Caret#deinstall

	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();
	}

	
    
private voidensureValidPosition()
This is invoked after the document changes to verify the current dot/mark is valid. We do this in case the NavigationFilter changed where to position the dot, that resulted in the current location being bogus.

        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, Position.Bias.Forward);
        }
    
public booleanequals(java.lang.Object obj)
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
true if the objects are equal; false otherwise

	return (this == obj);
    
protected voidfireStateChanged()
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

	// 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);
	    }	       
	}
    
public voidfocusGained(java.awt.event.FocusEvent e)
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

	if (component.isEnabled()) {
	    if (component.isEditable()) {
		setVisible(true);
	    }
	    setSelectionVisible(true);
	}
    
public voidfocusLost(java.awt.event.FocusEvent e)
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

	setVisible(false);
        setSelectionVisible(ownsSelection || e.isTemporary());
    
public intgetBlinkRate()
Gets the caret blink rate.

return
the delay in milliseconds. If this is zero the caret will not blink.
see
Caret#getBlinkRate

	return (flasher == null) ? 0 : flasher.getDelay();
    
intgetCaretWidth(int height)

        if (aspectRatio > -1) {
            return (int) (aspectRatio * height) + 1;
        }

        if (caretWidth > -1) {
            return caretWidth;
        }

        return 1;
    
public javax.swing.event.ChangeListener[]getChangeListeners()
Returns an array of all the change listeners registered on this caret.

return
all of this caret's ChangeListeners or an empty array if no change listeners are currently registered
see
#addChangeListener
see
#removeChangeListener
since
1.4

        return (ChangeListener[])listenerList.getListeners(
                ChangeListener.class);
    
private java.awt.datatransfer.ClipboardOwnergetClipboardOwner()

        return handler;
    
protected final javax.swing.text.JTextComponentgetComponent()
Gets the text editor component that this caret is is bound to.

return
the component

	return component;
    
public intgetDot()
Fetches the current position of the caret.

return
the position >= 0
see
Caret#getDot

        return dot;
    
javax.swing.text.Position$BiasgetDotBias()

	return dotBias;
    
private javax.swing.text.NavigationFilter$FilterBypassgetFilterBypass()

        if (filterBypass == null) {
            filterBypass = new DefaultFilterBypass();
        }
        return filterBypass;
    
public T[]getListeners(java.lang.Class listenerType)
Returns an array of all the objects currently registered as FooListeners upon this caret. FooListeners are registered using the addFooListener method.

You can specify the listenerType argument with a class literal, such as FooListener.class. For example, you can query a DefaultCaret c for its change listeners with the following code:

ChangeListener[] cls = (ChangeListener[])(c.getListeners(ChangeListener.class));
If no such listeners exist, this method returns an empty array.

param
listenerType the type of listeners requested; this parameter should specify an interface that descends from java.util.EventListener
return
an array of all objects registered as FooListeners on this component, or an empty array if no such listeners have been added
exception
ClassCastException if listenerType doesn't specify a class or interface that implements java.util.EventListener
see
#getChangeListeners
since
1.3

 
	return listenerList.getListeners(listenerType); 
    
public java.awt.PointgetMagicCaretPosition()
Gets the saved caret position.

return
the position see #setMagicCaretPosition

	return magicCaretPosition;
    
public intgetMark()
Fetches the current position of the mark. If there is a selection, the dot and mark will not be the same.

return
the position >= 0
see
Caret#getMark

        return mark;
    
javax.swing.text.Position$BiasgetMarkBias()

	return markBias;
    
protected javax.swing.text.Highlighter$HighlightPaintergetSelectionPainter()
Gets the painter for the Highlighter.

return
the painter

	return DefaultHighlighter.DefaultPainter;
    
private java.awt.datatransfer.ClipboardgetSystemSelection()

        try {
            return component.getToolkit().getSystemSelection();
        } catch (HeadlessException he) {
            // do nothing... there is no system clipboard
        } catch (SecurityException se) {
            // do nothing... there is no allowed system clipboard
        }
        return null;
    
public intgetUpdatePolicy()
Gets the caret movement policy on document updates.

return
one of the following values : UPDATE_WHEN_ON_EDT, NEVER_UPDATE, ALWAYS_UPDATE
see
#setUpdatePolicy
see
#UPDATE_WHEN_ON_EDT
see
#NEVER_UPDATE
see
#ALWAYS_UPDATE
since
1.5

        return updatePolicy;
    
javax.swing.text.Position$BiasguessBiasForOffset(int offset, javax.swing.text.Position$Bias lastBias, boolean lastLTR)

	// There is an abiguous case here. That if your model looks like:
	// abAB with the cursor at abB]A (visual representation of
	// 3 forward) deleting could either become abB] or
	// ab[B. I'ld actually prefer abB]. But, if I implement that
	// a delete at abBA] would result in aBA] vs a[BA which I 
	// think is totally wrong. To get this right we need to know what
	// was deleted. And we could get this from the bidi structure
	// in the change event. So:
	// PENDING: base this off what was deleted.
	if(lastLTR != isPositionLTR(offset, lastBias)) {
	    lastBias = Position.Bias.Backward;
	}
	else if(lastBias != Position.Bias.Backward &&
		lastLTR != isPositionLTR(offset, Position.Bias.Backward)) {
	    lastBias = Position.Bias.Backward;
	}
	if (lastBias == Position.Bias.Backward && offset > 0) {
	    try {
		Segment s = new Segment();
		component.getDocument().getText(offset - 1, 1, s);
		if (s.count > 0 && s.array[s.offset] == '\n") {
		    lastBias = Position.Bias.Forward;
		}
	    }
	    catch (BadLocationException ble) {}
	}
	return lastBias;
    
voidhandleMoveDot(int dot, javax.swing.text.Position$Bias dotBias)

        changeCaretPosition(dot, dotBias);
	    
        if (selectionVisible) {
            Highlighter h = component.getHighlighter();
            if (h != null) {
                int p0 = Math.min(dot, mark);
                int p1 = Math.max(dot, mark);
                
                // if p0 == p1 then there should be no highlight, remove it if necessary
                if (p0 == p1) {
                    if (selectionTag != null) {
                        h.removeHighlight(selectionTag);
                        selectionTag = null;
                    }
                // otherwise, change or add the highlight
                } else {
                    try {
                        if (selectionTag != null) {
                            h.changeHighlight(selectionTag, p0, p1);
                        } else {
                            Highlighter.HighlightPainter p = getSelectionPainter();
                            selectionTag = h.addHighlight(p0, p1, p);
                        }
                    } catch (BadLocationException e) {
                        throw new StateInvariantError("Bad caret position");
                    }
                }
            }
        }
    
voidhandleSetDot(int dot, javax.swing.text.Position$Bias dotBias)

	// move dot, if it changed
	Document doc = component.getDocument();
	if (doc != null) {
	    dot = Math.min(dot, doc.getLength());
	}
	dot = Math.max(dot, 0);

        // The position (0,Backward) is out of range so disallow it.
        if( dot == 0 )
            dotBias = Position.Bias.Forward;
        
	mark = dot;
	if (this.dot != dot || this.dotBias != dotBias ||
	    selectionTag != null || forceCaretPositionChange) {
	    changeCaretPosition(dot, dotBias);
	}
	this.markBias = this.dotBias;
	this.markLTR = dotLTR;
	Highlighter h = component.getHighlighter();
	if ((h != null) && (selectionTag != null)) {
	    h.removeHighlight(selectionTag);
	    selectionTag = null;
	}
    
public voidinstall(javax.swing.text.JTextComponent c)
Called when the UI is being installed into the interface of a JTextComponent. This can be used to gain access to the model that is being navigated by the implementation of this interface. Sets the dot and mark to 0, and establishes document, property change, focus, mouse, and mouse motion listeners.

param
c the component
see
Caret#install

	component = c;
	Document doc = c.getDocument();
	dot = mark = 0;
	dotLTR = markLTR = true;
	dotBias = markBias = Position.Bias.Forward;
	if (doc != null) {
	    doc.addDocumentListener(handler);
	}
	c.addPropertyChangeListener(handler);
	c.addFocusListener(this);
	c.addMouseListener(this);
	c.addMouseMotionListener(this);

	// if the component already has focus, it won't
	// be notified.
	if (component.hasFocus()) {
	    focusGained(null);
	}

        Number ratio = (Number) c.getClientProperty("caretAspectRatio");
        if (ratio != null) {
            aspectRatio = ratio.floatValue();
        } else {
            aspectRatio = -1;
        }

        Integer width = (Integer) c.getClientProperty("caretWidth");
        if (width != null) {
            caretWidth = width.intValue();
        } else {
            caretWidth = -1;
        }
    
public booleanisActive()
Determines if the caret is currently active.

This method returns whether or not the Caret is currently in a blinking state. It does not provide information as to whether it is currently blinked on or off. To determine if the caret is currently painted use the isVisible method.

return
true if active else false
see
#isVisible
since
1.5

        return active;
    
booleanisDotLeftToRight()

	return dotLTR;
    
booleanisMarkLeftToRight()

	return markLTR;
    
booleanisPositionLTR(int position, javax.swing.text.Position$Bias bias)

	Document doc = component.getDocument();
	if(doc instanceof AbstractDocument ) {
	    if(bias == Position.Bias.Backward && --position < 0)
		position = 0;
	    return ((AbstractDocument)doc).isLeftToRight(position, position);
	}
	return true;
    
public booleanisSelectionVisible()
Checks whether the current selection is visible.

return
true if the selection is visible

	return selectionVisible;
    
public booleanisVisible()
Indicates whether or not the caret is currently visible. As the caret flashes on and off the return value of this will change between true, when the caret is painted, and false, when the caret is not painted. isActive indicates whether or not the caret is in a blinking state, such that it can be visible, and isVisible indicates whether or not the caret is actually visible.

Subclasses that wish to render a different flashing caret should override paint and only paint the caret if this method returns true.

return
true if visible else false
see
Caret#isVisible
see
#isActive

        return visible;
    
public voidmouseClicked(java.awt.event.MouseEvent e)
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

	if (! e.isConsumed()) {
	    int nclicks = e.getClickCount();
	    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
			}
		    }
		}
	    }
	}
    
public voidmouseDragged(java.awt.event.MouseEvent e)
Moves the caret position according to the mouse pointer's current location. This effectively extends the selection. By default, this is only done for mouse button 1.

param
e the mouse event
see
MouseMotionListener#mouseDragged

	if ((! e.isConsumed()) && SwingUtilities.isLeftMouseButton(e)) {
	    moveCaret(e);
	}
    
public voidmouseEntered(java.awt.event.MouseEvent e)
Called when the mouse enters a region.

param
e the mouse event
see
MouseListener#mouseEntered

    
public voidmouseExited(java.awt.event.MouseEvent e)
Called when the mouse exits a region.

param
e the mouse event
see
MouseListener#mouseExited

    
public voidmouseMoved(java.awt.event.MouseEvent e)
Called when the mouse is moved.

param
e the mouse event
see
MouseMotionListener#mouseMoved

    
public voidmousePressed(java.awt.event.MouseEvent e)
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

        if (SwingUtilities.isLeftMouseButton(e)) {
            if (e.isConsumed()) {
                shouldHandleRelease = true;
            } else {
                shouldHandleRelease = false;
                adjustCaretAndFocus(e);
                if (e.getClickCount() == 2
                    && SwingUtilities2.canEventAccessSystemClipboard(e)) {
                    selectWord(e);
                }
            }
        }
    
public voidmouseReleased(java.awt.event.MouseEvent e)
Called when the mouse is released.

param
e the mouse event
see
MouseListener#mouseReleased

        if (!e.isConsumed()
                && shouldHandleRelease
                && SwingUtilities.isLeftMouseButton(e)) {

            adjustCaretAndFocus(e);
        }
    
protected voidmoveCaret(java.awt.event.MouseEvent e)
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

	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]);
	}
    
public voidmoveDot(int dot)
Moves the caret position to some other position.

param
dot the position >= 0
see
Caret#moveDot

	moveDot(dot, Position.Bias.Forward);
    
voidmoveDot(int dot, javax.swing.text.Position$Bias dotBias)

	if (! component.isEnabled()) {
	    // don't allow selection on disabled components.
	    setDot(dot, dotBias);
	    return;
	}
	if (dot != this.dot) {
            NavigationFilter filter = component.getNavigationFilter();

            if (filter != null) {
                filter.moveDot(getFilterBypass(), dot, dotBias);
            }
            else {
                handleMoveDot(dot, dotBias);
            }
        }
    
public voidpaint(java.awt.Graphics g)
Renders the caret as a vertical line. If this is reimplemented the damage method should also be reimplemented as it assumes the shape of the caret is a vertical line. Sets the caret color to the value returned by getCaretColor().

If there are multiple text directions present in the associated document, a flag indicating the caret bias will be rendered. This will occur only if the associated document is a subclass of AbstractDocument and there are multiple bidi levels present in the bidi element structure (i.e. the text has multiple directions associated with it).

param
g the graphics context
see
#damage

	if(isVisible()) {
	    try {
		TextUI mapper = component.getUI();
		Rectangle r = mapper.modelToView(component, dot, dotBias);

                if ((r == null) || ((r.width == 0) && (r.height == 0))) {
                    return;
                }
                if (width > 0 && height > 0 &&
                                !this._contains(r.x, r.y, r.width, r.height)) {
                    // We seem to have gotten out of sync and no longer
                    // contain the right location, adjust accordingly.
                    Rectangle clip = g.getClipBounds();

                    if (clip != null && !clip.contains(this)) {
                        // Clip doesn't contain the old location, force it
                        // to be repainted lest we leave a caret around.
                        repaint();
                    }
                    // This will potentially cause a repaint of something
                    // we're already repainting, but without changing the
                    // semantics of damage we can't really get around this.
                    damage(r);
                }
		g.setColor(component.getCaretColor());
                int paintWidth = getCaretWidth(r.height);
                r.x -= paintWidth  >> 1;
                g.fillRect(r.x, r.y, paintWidth , r.height - 1);

		// see if we should paint a flag to indicate the bias
		// of the caret.  
		// PENDING(prinz) this should be done through
		// protected methods so that alternative LAF 
		// will show bidi information.
		Document doc = component.getDocument();
		if (doc instanceof AbstractDocument) {
		    Element bidi = ((AbstractDocument)doc).getBidiRootElement();
		    if ((bidi != null) && (bidi.getElementCount() > 1)) {
			// there are multiple directions present.
                        flagXPoints[0] = r.x + ((dotLTR) ? paintWidth : 0);
                        flagYPoints[0] = r.y;
                        flagXPoints[1] = flagXPoints[0];
                        flagYPoints[1] = flagYPoints[0] + 4;
                        flagXPoints[2] = flagXPoints[0] + ((dotLTR) ? 4 : -4);
                        flagYPoints[2] = flagYPoints[0];
                        g.fillPolygon(flagXPoints, flagYPoints, 3);
		    }
		}
	    } catch (BadLocationException e) {
		// can't render I guess
		//System.err.println("Can't render cursor");
	    }
	}
    
protected voidpositionCaret(java.awt.event.MouseEvent e)
Tries to set the position of the caret from the coordinates of a mouse event, using viewToModel().

param
e the mouse event

	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]);
	}
    
private voidreadObject(java.io.ObjectInputStream s)

	s.defaultReadObject();
	handler = new Handler();
	if (!s.readBoolean()) {
	    dotBias = Position.Bias.Forward;
	}
	else {
	    dotBias = Position.Bias.Backward;
	}
	if (!s.readBoolean()) {
	    markBias = Position.Bias.Forward;
	}
	else {
	    markBias = Position.Bias.Backward;
	}
    
public voidremoveChangeListener(javax.swing.event.ChangeListener l)
Removes a listener that was tracking caret position changes.

param
l the listener
see
Caret#removeChangeListener

	listenerList.remove(ChangeListener.class, l);
    
protected final synchronized voidrepaint()
Cause the caret to be painted. The repaint area is the bounding box of the caret (i.e. the caret rectangle or this).

This method is thread safe, although most Swing methods are not. Please see Threads and Swing for more information.

	if (component != null) {
	    component.repaint(x, y, width, height);
	}
    
voidrepaintNewCaret()
Repaints the new caret position, with the assumption that this is happening on the event thread so that calling modelToView is safe.

	if (component != null) {
	    TextUI mapper = component.getUI();
	    Document doc = component.getDocument();
	    if ((mapper != null) && (doc != null)) {
		// determine the new location and scroll if
		// not visible.
		Rectangle newLoc;
		try {
		    newLoc = mapper.modelToView(component, this.dot, this.dotBias);
		} catch (BadLocationException e) {
		    newLoc = null;
		}
		if (newLoc != null) {
		    adjustVisibility(newLoc);
		    // If there is no magic caret position, make one
		    if (getMagicCaretPosition() == null) {
			setMagicCaretPosition(new Point(newLoc.x, newLoc.y));
		    }
		}
		
		// repaint the new position
		damage(newLoc);
	    }
	}
    
private voidselectWord(java.awt.event.MouseEvent e)
Selects word based on the MouseEvent

        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;
    
public voidsetBlinkRate(int rate)
Sets the caret blink rate.

param
rate the rate in milliseconds, 0 to stop blinking
see
Caret#setBlinkRate

	if (rate != 0) {
	    if (flasher == null) {
		flasher = new Timer(rate, handler);
	    }
	    flasher.setDelay(rate);
	} else {
	    if (flasher != null) {
		flasher.stop();
		flasher.removeActionListener(handler);
		flasher = null;
	    }
	}
    
public voidsetDot(int dot)
Sets the caret position and mark to some position. This implicitly sets the selection range to zero.

param
dot the position >= 0
see
Caret#setDot

	setDot(dot, Position.Bias.Forward);
    
voidsetDot(int dot, javax.swing.text.Position$Bias dotBias)

        NavigationFilter filter = component.getNavigationFilter();

        if (filter != null) {
            filter.setDot(getFilterBypass(), dot, dotBias);
        }
        else {
            handleSetDot(dot, dotBias);
        }
    
public voidsetMagicCaretPosition(java.awt.Point p)
Saves the current caret position. This is used when caret up/down actions occur, moving between lines that have uneven end positions.

param
p the position
see
#getMagicCaretPosition

	magicCaretPosition = p;
    
public voidsetSelectionVisible(boolean vis)
Changes the selection visibility.

param
vis the new visibility

	if (vis != selectionVisible) {
	    selectionVisible = vis;
	    if (selectionVisible) {
		// show
		Highlighter h = component.getHighlighter();
		if ((dot != mark) && (h != null) && (selectionTag == null)) {
		    int p0 = Math.min(dot, mark);
		    int p1 = Math.max(dot, mark);
		    Highlighter.HighlightPainter p = getSelectionPainter();
		    try {
			selectionTag = h.addHighlight(p0, p1, p);
		    } catch (BadLocationException bl) {
			selectionTag = null;
		    }
		}
	    } else {
		// hide
		if (selectionTag != null) {
		    Highlighter h = component.getHighlighter();
		    h.removeHighlight(selectionTag);
		    selectionTag = null;
		}
	    }
	}
    
public voidsetUpdatePolicy(int policy)
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 BACKSPACE 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.

The following update policies are allowed:

  • NEVER_UPDATE: 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.
  • ALWAYS_UPDATE: 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 adjustVisibility method.
  • UPDATE_WHEN_ON_EDT: acts like ALWAYS_UPDATE if the document updates are performed on the Event Dispatching Thread and like NEVER_UPDATE if updates are performed on other thread.

The default property value is UPDATE_WHEN_ON_EDT.

param
policy one of the following values : UPDATE_WHEN_ON_EDT, NEVER_UPDATE, ALWAYS_UPDATE
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

        updatePolicy = policy;
    
public voidsetVisible(boolean e)
Sets the caret visibility, and repaints the caret. It is important to understand the relationship between this method, isVisible and isActive. Calling this method with a value of true activates the caret blinking. Setting it to false turns it completely off. To determine whether the blinking is active, you should call isActive. In effect, isActive is an appropriate corresponding "getter" method for this one. isVisible can be used to fetch the current visibility status of the caret, meaning whether or not it is currently painted. This status will change as the caret blinks on and off.

Here's a list showing the potential return values of both isActive and isVisible after calling this method:

setVisible(true):

  • isActive(): true
  • isVisible(): true or false depending on whether or not the caret is blinked on or off

setVisible(false):

  • isActive(): false
  • isVisible(): false

param
e the visibility specifier
see
#isActive
see
Caret#setVisible

	// focus lost notification can come in later after the
	// caret has been deinstalled, in which case the component
	// will be null.
	if (component != null) {
            active = e;
            TextUI mapper = component.getUI();
	    if (visible != e) {
                visible = e;
		// repaint the caret
		try {
		    Rectangle loc = mapper.modelToView(component, dot,dotBias);
		    damage(loc);
		} catch (BadLocationException badloc) {
		    // hmm... not legally positioned
		}
	    }
	}
	if (flasher != null) {
	    if (visible) {
		flasher.start();
	    } else {
		flasher.stop();
	    }
	}
    
public java.lang.StringtoString()

        String s = "Dot=(" + dot + ", " + dotBias + ")";
        s += " Mark=(" + mark + ", " + markBias + ")";
        return s;
    
private voidupdateSystemSelection()

        if ( ! SwingUtilities2.canCurrentEventAccessSystemClipboard() ) {
            return;
        }
        if (this.dot != this.mark && component != null) {
            Clipboard clip = getSystemSelection();
            if (clip != null) {
                String selectedText = null;
                if (component instanceof JPasswordField 
                    && component.getClientProperty("JPasswordField.cutCopyAllowed") !=
                    Boolean.TRUE) {
                    //fix for 4793761
                    StringBuffer txt = null;
                    char echoChar = ((JPasswordField)component).getEchoChar();
                    int p0 = Math.min(getDot(), getMark());
                    int p1 = Math.max(getDot(), getMark());
                    for (int i = p0; i < p1; i++) {
                        if (txt == null) {
                            txt = new StringBuffer();
                        }
                        txt.append(echoChar);
                    }
                    selectedText = (txt != null) ? txt.toString() : null;
                } else {
                    selectedText = component.getSelectedText();
                }
                try {
                    clip.setContents(
                        new StringSelection(selectedText), getClipboardOwner());

                    ownsSelection = true;
                } catch (IllegalStateException ise) {
                    // clipboard was unavailable
                    // no need to provide error feedback to user since updating
                    // the system selection is not a user invoked action
                }
            }
        }
    
private voidwriteObject(java.io.ObjectOutputStream s)

	s.defaultWriteObject();
	s.writeBoolean((dotBias == Position.Bias.Backward));
	s.writeBoolean((markBias == Position.Bias.Backward));