blob: 44a7a3874d22eeeb4da740057ed2d562d4989092 [file] [log] [blame]
/*
* @(#)$RCSfile: TextSelection.java,v $ $Revision: 1.2 $ $Date: 2001/10/30 02:42:48 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
/*
7/1/97 - caret blinks
7/3/97 - fAnchor is no longer restricted to the start or end of the selection. {jbr}
Also, removed fVisible - it was identical to enabled().
*/
package com.ibm.richtext.textpanel;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Point;
import java.text.BreakIterator;
import java.text.CharacterIterator;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.awt.event.FocusEvent;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.textformat.TextOffset;
import com.ibm.richtext.textformat.MFormatter;
class TextSelection extends Behavior implements Runnable {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
static final Color HIGHLIGHTCOLOR = Color.pink;
private TextComponent fTextComponent;
private MConstText fText;
private TextOffset fStart;
private TextOffset fLimit;
private TextOffset fAnchor;
private TextOffset fUpDownAnchor = null;
private BreakIterator fBoundaries = null;
private Color fHighlightColor = HIGHLIGHTCOLOR;
private PanelEventBroadcaster fListener;
private RunStrategy fRunStrategy;
private boolean fMouseDown = false;
private boolean fHandlingKeyOrCommand = false;
private boolean fCaretShouldBlink;
private boolean fCaretIsVisible;
private int fCaretCount;
// formerly in base class
private boolean fEnabled;
private MouseEvent fPendingMouseEvent = null;
private static final int kCaretInterval = 500;
public void run() {
final Runnable blinkCaret = new Runnable() {
public void run() {
fCaretIsVisible = !fCaretIsVisible;
Graphics g = fTextComponent.getGraphics();
if (g != null) {
//System.out.println("caretIsVisible: " + fCaretIsVisible);
drawSelection(g, fCaretIsVisible);
}
else {
// Not sure what else to do:
fCaretShouldBlink = false;
}
}
};
// blink caret
while (true) {
synchronized(this) {
while (!fCaretShouldBlink) {
try {
wait();
}
catch(InterruptedException e) {
System.out.println("Caught InterruptedException in caret thread.");
}
}
++fCaretCount;
if (fCaretCount % 2 == 0) {
fRunStrategy.doIt(blinkCaret);
}
}
try {
Thread.sleep(kCaretInterval);
}
catch(InterruptedException e) {
}
}
}
public TextSelection(TextComponent textComponent,
PanelEventBroadcaster listener,
RunStrategy runStrategy) {
fTextComponent = textComponent;
fText = textComponent.getText();
fListener = listener;
fRunStrategy = runStrategy;
fStart = new TextOffset();
fLimit = new TextOffset();
fAnchor = new TextOffset();
fMouseDown = false;
fCaretCount = 0;
fCaretIsVisible = true;
fCaretShouldBlink = false;
setEnabled(false);
Thread caretThread = new Thread(this);
caretThread.setDaemon(true);
caretThread.start();
}
boolean enabled() {
return fEnabled;
}
private void setEnabled(boolean enabled) {
fEnabled = enabled;
}
public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
boolean result;
fHandlingKeyOrCommand = true;
if (event == Behavior.SELECT) {
select((TextRange) what);
result = true;
}
else if (event == Behavior.COPY) {
fTextComponent.getClipboard().setContents(fText.extract(fStart.fOffset, fLimit.fOffset));
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
result = true;
}
else {
result = false;
}
fHandlingKeyOrCommand = false;
return result;
}
protected void advanceToNextBoundary(TextOffset offset) {
// If there's no boundaries object, or if position at the end of the
// document, return the offset unchanged
if (fBoundaries == null) {
return;
}
int position = offset.fOffset;
if (position >= fText.length()) {
return;
}
// If position is at a boundary and offset is before position,
// leave it unchanged. Otherwise move to next boundary.
int nextPos = fBoundaries.following(position);
if (fBoundaries.previous() == position &&
offset.fPlacement==offset.BEFORE_OFFSET) {
return;
}
offset.setOffset(nextPos, TextOffset.AFTER_OFFSET);
}
protected void advanceToPreviousBoundary(TextOffset offset) {
advanceToPreviousBoundary(offset, false);
}
private void advanceToPreviousBoundary(TextOffset offset, boolean alwaysMove) {
// if there's no boundaries object, or if we're sitting at the beginning
// of the document, return the offset unchanged
if (fBoundaries == null) {
return;
}
int position = offset.fOffset;
if (position == 0) {
return;
}
// If position is at a boundary, leave it unchanged. Otherwise
// move to previous boundary.
if (position == fText.length()) {
fBoundaries.last();
}
else {
fBoundaries.following(position);
}
int prevPos = fBoundaries.previous();
if (prevPos == position) {
if (!alwaysMove && offset.fPlacement==offset.AFTER_OFFSET) {
return;
}
prevPos = fBoundaries.previous();
}
// and finally update the real offset with this new position we've found
offset.setOffset(prevPos, TextOffset.AFTER_OFFSET);
}
private void doArrowKey(KeyEvent e, int key) {
// when there's a selection range, the left and up arrow keys place an
// insertion point at the beginning of the range, and the right and down
// keys place an insertion point at the end of the range (unless the shift
// key is down, of course)
if (!fStart.equals(fLimit) && !e.isShiftDown()) {
if (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_UP)
setSelRangeAndDraw(fStart, fStart, fStart);
else
setSelRangeAndDraw(fLimit, fLimit, fLimit);
}
else {
if (!fAnchor.equals(fStart))
fAnchor.assign(fLimit);
TextOffset liveEnd = (fStart.equals(fAnchor)) ? fLimit : fStart;
TextOffset newPos = new TextOffset();
// if the control key is down, the left and right arrow keys move by whole
// word in the appropriate direction (we use a line break object so that we're
// not treating spaces and punctuation as words)
if (e.isControlDown() && (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT)) {
fUpDownAnchor = null;
fBoundaries = BreakIterator.getLineInstance();
fBoundaries.setText(fText.createCharacterIterator());
newPos.assign(liveEnd);
if (key == KeyEvent.VK_RIGHT)
advanceToNextBoundary(newPos);
else
advanceToPreviousBoundary(newPos, true);
}
// if we get down to here, this is a plain-vanilla insertion-point move,
// or the shift key is down and we're extending or shortening the selection
else {
// fUpDownAnchor is used to keep track of the horizontal position
// across a run of up or down arrow keys (this prevents accumulated
// error from destroying our horizontal position)
if (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT)
fUpDownAnchor = null;
else {
if (fUpDownAnchor == null) {
fUpDownAnchor = new TextOffset(liveEnd);
}
}
short direction = MFormatter.eRight; // just to have a default...
switch (key) {
case KeyEvent.VK_UP: direction = MFormatter.eUp; break;
case KeyEvent.VK_DOWN: direction = MFormatter.eDown; break;
case KeyEvent.VK_LEFT: direction = MFormatter.eLeft; break;
case KeyEvent.VK_RIGHT: direction = MFormatter.eRight; break;
}
// use the formatter to determine the actual effect of the arrow key
fTextComponent.findNewInsertionOffset(newPos, fUpDownAnchor, liveEnd, direction);
}
// if the shift key is down, the selection range is from the anchor point
// the site of the last insertion point or the beginning point of the last
// selection drag operation) to the newly-calculated position; if the
// shift key is down, the newly-calculated position is the insertion point position
if (!e.isShiftDown())
setSelRangeAndDraw(newPos, newPos, newPos);
else {
if (newPos.lessThan(fAnchor))
setSelRangeAndDraw(newPos, fAnchor, fAnchor);
else
setSelRangeAndDraw(fAnchor, newPos, fAnchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
}
private void doEndKey(KeyEvent e) {
// ctrl-end moves the insertsion point to the end of the document,
// ctrl-shift-end extends the selection so that it ends at the end
// of the document
TextOffset activeEnd, anchor;
if (fAnchor.equals(fStart)) {
activeEnd = new TextOffset(fStart);
anchor = new TextOffset(fLimit);
}
else {
activeEnd = new TextOffset(fLimit);
anchor = new TextOffset(fStart);
}
if (e.isControlDown()) {
TextOffset end = new TextOffset(fText.length(), TextOffset.BEFORE_OFFSET);
if (e.isShiftDown())
setSelRangeAndDraw(anchor, end, anchor);
else
setSelRangeAndDraw(end, end, end);
}
// end moves the insertion point to the end of the line containing
// the end of the current selection
// shift-end extends the selection to the end of the line containing
// the end of the current selection
else {
int oldOffset = activeEnd.fOffset;
activeEnd.fOffset = fTextComponent.lineRangeLimit(fTextComponent.lineContaining(activeEnd));
activeEnd.fPlacement = TextOffset.BEFORE_OFFSET;
if (fText.paragraphLimit(oldOffset) == activeEnd.fOffset &&
activeEnd.fOffset != fText.length() && activeEnd.fOffset > oldOffset) {
activeEnd.fOffset--;
activeEnd.fPlacement = TextOffset.AFTER_OFFSET;
}
if (!e.isShiftDown())
setSelRangeAndDraw(activeEnd, activeEnd, activeEnd);
else {
if (activeEnd.lessThan(anchor))
setSelRangeAndDraw(activeEnd, anchor, anchor);
else
setSelRangeAndDraw(anchor, activeEnd, anchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
fUpDownAnchor = null;
}
private void doHomeKey(KeyEvent e) {
// ctrl-home moves the insertion point to the beginning of the document,
// ctrl-shift-home extends the selection so that it begins at the beginning
// of the document
TextOffset activeEnd, anchor;
if (fAnchor.equals(fStart)) {
activeEnd = new TextOffset(fStart);
anchor = new TextOffset(fLimit);
}
else {
activeEnd = new TextOffset(fLimit);
anchor = new TextOffset(fStart);
}
if (e.isControlDown()) {
TextOffset start = new TextOffset(0, TextOffset.AFTER_OFFSET);
if (e.isShiftDown())
setSelRangeAndDraw(start, anchor, anchor);
else
setSelRangeAndDraw(start, start, start);
}
// home moves the insertion point to the beginning of the line containing
// the beginning of the current selection
// shift-home extends the selection to the beginning of the line containing
// the beginning of the current selection
else {
activeEnd.fOffset = fTextComponent.lineRangeLow(fTextComponent.lineContaining(activeEnd));
activeEnd.fPlacement = TextOffset.AFTER_OFFSET;
if (!e.isShiftDown())
setSelRangeAndDraw(activeEnd, activeEnd, activeEnd);
else {
if (activeEnd.lessThan(anchor))
setSelRangeAndDraw(activeEnd, anchor, anchor);
else
setSelRangeAndDraw(anchor, activeEnd, anchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
fUpDownAnchor = null;
}
/** draws or erases the current selection
* Draws or erases the highlight region or insertion caret for the current selection
* range.
* @param g The graphics environment to draw into
* @param visible If true, draw the selection; if false, erase it
*/
protected void drawSelection(Graphics g, boolean visible) {
drawSelectionRange(g, fStart, fLimit, visible);
}
/** draws or erases a selection highlight at the specfied positions
* Draws or erases a selection highlight or insertion caret corresponding to
* the specified selecion range
* @param g The graphics environment to draw into. If null, this method does nothing.
* @param start The beginning of the range to highlight
* @param limit The end of the range to highlight
* @param vsible If true, draw; if false, erase
*/
protected void drawSelectionRange( Graphics g,
TextOffset start,
TextOffset limit,
boolean visible) {
if (g == null) {
return;
}
Rectangle selBounds = fTextComponent.getBoundingRect(start, limit);
selBounds.width = Math.max(1, selBounds.width);
selBounds.height = Math.max(1, selBounds.height);
fTextComponent.drawText(g, selBounds, visible, start, limit, fHighlightColor);
}
protected TextOffset getAnchor() {
return fAnchor;
}
public TextOffset getEnd() {
return fLimit;
}
public Color getHighlightColor() {
return fHighlightColor;
}
public TextOffset getStart() {
return fStart;
}
public TextRange getSelectionRange() {
return new TextRange(fStart.fOffset, fLimit.fOffset);
}
public boolean focusGained(FocusEvent e) {
setEnabled(true);
drawSelection(fTextComponent.getGraphics(), true);
restartCaretBlinking(true);
if (fPendingMouseEvent != null) {
mousePressed(fPendingMouseEvent);
fPendingMouseEvent = null;
}
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
return true;
}
public boolean focusLost(FocusEvent e) {
stopCaretBlinking();
setEnabled(false);
drawSelection(fTextComponent.getGraphics(), false);
return true;
}
/**
* Return true if the given key event can affect the selection
* range.
*/
public static boolean keyAffectsSelection(KeyEvent e) {
if (e.getID() != e.KEY_PRESSED) {
return false;
}
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_HOME:
case KeyEvent.VK_END:
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
return true;
default:
return false;
}
}
public boolean keyPressed(KeyEvent e) {
fHandlingKeyOrCommand = true;
int key = e.getKeyCode();
boolean result = true;
switch (key) {
case KeyEvent.VK_HOME:
doHomeKey(e);
break;
case KeyEvent.VK_END:
doEndKey(e);
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
doArrowKey(e, key);
break;
default:
fUpDownAnchor = null;
result = false;
break;
}
fHandlingKeyOrCommand = false;
return result;
}
public boolean mousePressed(MouseEvent e) {
if (!enabled()) {
fPendingMouseEvent = e;
fTextComponent.requestFocus();
return false;
}
if (fMouseDown)
throw new Error("fMouseDown is out of sync with mouse in TextSelection.");
fMouseDown = true;
stopCaretBlinking();
int x = e.getX(), y = e.getY();
boolean wasZeroLength = rangeIsZeroLength(fStart, fLimit, fAnchor);
TextOffset current = fTextComponent.pointToTextOffset(null, x, y, null, true);
TextOffset anchorStart = new TextOffset();
TextOffset anchorEnd = new TextOffset();
fUpDownAnchor = null;
// if we're not extending the selection...
if (!e.isShiftDown()) {
// if there are multiple clicks, create the appopriate type of BreakIterator
// object for finding text boundaries (single clicks don't use a BreakIterator
// object)
if (e.getClickCount() == 2)
fBoundaries = BreakIterator.getWordInstance();
else if (e.getClickCount() == 3)
fBoundaries = BreakIterator.getSentenceInstance();
else
fBoundaries = null;
// if we're using a BreakIterator object, use it to find the nearest boundaries
// on either side of the mouse-click position and make them our anchor range
if (fBoundaries != null)
fBoundaries.setText(fText.createCharacterIterator());
anchorStart.assign(current);
advanceToPreviousBoundary(anchorStart);
anchorEnd.assign(current);
advanceToNextBoundary(anchorEnd);
}
// if we _are_ extending the selection, determine our anchor range as follows:
// fAnchor is the start of the anchor range;
// the next boundary (after fAnchor) is the limit of the anchor range.
else {
if (fBoundaries != null)
fBoundaries.setText(fText.createCharacterIterator());
anchorStart.assign(fAnchor);
anchorEnd.assign(anchorStart);
advanceToNextBoundary(anchorEnd);
}
SelectionDragInteractor interactor = new SelectionDragInteractor(this,
fTextComponent,
fRunStrategy,
anchorStart,
anchorEnd,
current,
x,
y,
wasZeroLength);
interactor.addToOwner(fTextComponent);
return true;
}
public boolean mouseReleased(MouseEvent e) {
fPendingMouseEvent = null;
return false;
}
// drag interactor calls this
void mouseReleased(boolean zeroLengthChange) {
fMouseDown = false;
if (zeroLengthChange) {
fListener.textStateChanged(TextPanelEvent.SELECTION_EMPTY_CHANGED);
}
fListener.textStateChanged(TextPanelEvent.SELECTION_RANGE_CHANGED);
fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
// if caret drawing during mouse drags is supressed, draw caret now.
restartCaretBlinking(true);
}
/** draws the selection
* Provided, of course, that the selection is visible, the adorner is enabled,
* and we're calling it to adorn the view it actually belongs to
* @param g The graphics environment to draw into
* @return true if we actually drew
*/
public boolean paint(Graphics g, Rectangle drawRect) {
// don't draw anything unless we're enabled and the selection is visible
if (!enabled())
return false;
fTextComponent.drawText(g, drawRect, true, fStart, fLimit, fHighlightColor);
return true;
}
/** scrolls the view to reveal the live end of the selection
* (i.e., the end that moves if you use the arrow keys with the shift key down)
*/
public void scrollToShowSelection() {
Rectangle selRect = fTextComponent.getBoundingRect(fStart, fLimit);
fTextComponent.scrollToShow(selRect);
}
/** scrolls the view to reveal the live end of the selection
* (i.e., the end that moves if you use the arrow keys with the shift key down)
*/
public void scrollToShowSelectionEnd() {
TextOffset liveEnd;
// variable not used Point[] points;
Rectangle caret;
if (fAnchor.equals(fStart))
liveEnd = fLimit;
else
liveEnd = fStart;
//points = fTextComponent.textOffsetToPoint(liveEnd);
//caret = new Rectangle(points[0]);
//caret = caret.union(new Rectangle(points[1]));
caret = fTextComponent.getCaretRect(liveEnd);
fTextComponent.scrollToShow(caret);
}
private void select(TextRange range) {
// variable not used int textLength = fTextComponent.getText().length();
TextOffset start = new TextOffset(range.start);
stopCaretBlinking();
setSelRangeAndDraw(start, new TextOffset(range.limit), start);
restartCaretBlinking(true);
}
public void setHighlightColor(Color newColor) {
fHighlightColor = newColor;
if (enabled())
drawSelection(fTextComponent.getGraphics(), true);
}
static boolean rangeIsZeroLength(TextOffset start, TextOffset limit, TextOffset anchor) {
return start.fOffset == limit.fOffset && anchor.fOffset == limit.fOffset;
}
// sigh... look out for aliasing
public void setSelectionRange(TextOffset newStart, TextOffset newLimit, TextOffset newAnchor) {
boolean zeroLengthChange = rangeIsZeroLength(newStart, newLimit, newAnchor) !=
rangeIsZeroLength(fStart, fLimit, fAnchor);
TextOffset tempNewAnchor;
if (newAnchor == fStart || newAnchor == fLimit) {
tempNewAnchor = new TextOffset(newAnchor); // clone in case of aliasing
}
else {
tempNewAnchor = newAnchor;
}
// DEBUG {jbr}
if (newStart.greaterThan(newLimit))
throw new IllegalArgumentException("Selection limit is before selection start.");
if (newLimit != fStart) {
fStart.assign(newStart);
fLimit.assign(newLimit);
}
else {
fLimit.assign(newLimit);
fStart.assign(newStart);
}
fAnchor.assign(tempNewAnchor);
if (fStart.fOffset == fLimit.fOffset) {
fStart.fPlacement = fAnchor.fPlacement;
fLimit.fPlacement = fAnchor.fPlacement;
}
if (!fMouseDown) {
if (zeroLengthChange) {
fListener.textStateChanged(TextPanelEvent.SELECTION_EMPTY_CHANGED);
}
fListener.textStateChanged(TextPanelEvent.SELECTION_RANGE_CHANGED);
if (fHandlingKeyOrCommand) {
fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
}
}
}
private void sortOffsets(TextOffset offsets[]) {
int i, j;
for (i=0; i < offsets.length-1; i++) {
for (j=i+1; j < offsets.length; j++) {
if (offsets[j].lessThan(offsets[i])) {
TextOffset temp = offsets[j];
offsets[j] = offsets[i];
offsets[i] = temp;
}
}
}
// DEBUG {jbr}
for (i=0; i < offsets.length-1; i++)
if (offsets[i].greaterThan(offsets[i+1]))
throw new Error("sortOffsets failed!");
}
private Rectangle getSelectionChangeRect(
TextOffset rangeStart, TextOffset rangeLimit,
TextOffset oldStart, TextOffset oldLimit,
TextOffset newStart, TextOffset newLimit,
boolean drawIfInsPoint) {
if (!rangeStart.equals(rangeLimit))
return fTextComponent.getBoundingRect(rangeStart, rangeLimit);
// here, rangeStart and rangeLimit are equal
if (rangeStart.equals(oldLimit)) {
// range start is OLD insertion point. Redraw if caret is currently visible.
if (fCaretIsVisible)
return fTextComponent.getBoundingRect(rangeStart, rangeStart);
}
else if (rangeStart.equals(newLimit)) {
// range start is NEW insertion point.
if (drawIfInsPoint)
return fTextComponent.getBoundingRect(rangeStart, rangeStart);
}
return null;
}
private static boolean rectanglesOverlapVertically(Rectangle r1, Rectangle r2) {
if (r1 == null || r2 == null) {
return false;
}
return r1.y <= r2.y + r2.height || r2.y <= r1.y + r1.height;
}
// Update to show new selection, redrawing as little as possible
private void updateSelectionDisplay(
TextOffset oldStart, TextOffset oldLimit,
TextOffset newStart, TextOffset newLimit, boolean drawIfInsPoint) {
//System.out.println("newStart:" + newStart + "; newLimit:" + newLimit);
TextOffset off[] = new TextOffset[4];
off[0] = oldStart;
off[1] = oldLimit;
off[2] = newStart;
off[3] = newLimit;
sortOffsets(off);
Rectangle r1 = getSelectionChangeRect(off[0], off[1], oldStart, oldLimit, newStart, newLimit, drawIfInsPoint);
Rectangle r2 = getSelectionChangeRect(off[2], off[3], oldStart, oldLimit, newStart, newLimit, drawIfInsPoint);
boolean drawSelection = drawIfInsPoint || !newStart.equals(newLimit);
if (rectanglesOverlapVertically(r1, r2)) {
fTextComponent.drawText(fTextComponent.getGraphics(), r1.union(r2), drawSelection, newStart, newLimit, fHighlightColor);
}
else {
if (r1 != null)
fTextComponent.drawText(fTextComponent.getGraphics(), r1, drawSelection, newStart, newLimit, fHighlightColor);
if (r2 != null)
fTextComponent.drawText(fTextComponent.getGraphics(), r2, drawSelection, newStart, newLimit, fHighlightColor);
}
}
public void setSelRangeAndDraw(TextOffset newStart, TextOffset newLimit, TextOffset newAnchor) {
// if the old and new selection ranges are the same, don't do anything
if (fStart.equals(newStart) && fLimit.equals(newLimit) && fAnchor.equals(newAnchor))
return;
if (enabled())
stopCaretBlinking();
// update the selection on screen if we're enabled and visible
TextOffset oldStart = new TextOffset(fStart), oldLimit = new TextOffset(fLimit);
setSelectionRange(newStart, newLimit, newAnchor);
if (enabled()) {
// To supress drawing a caret during a mouse drag, pass !fMouseDown instead of true:
updateSelectionDisplay(oldStart, oldLimit, fStart, fLimit, true);
}
if (!fMouseDown && enabled())
restartCaretBlinking(true);
}
public void stopCaretBlinking() {
synchronized(this) {
fCaretShouldBlink = false;
}
}
/**
* Resume blinking the caret, if the selection is an insertion point.
* @param caretIsVisible true if the caret is displayed when this is called.
* This method relies on the client to display (or not display) the caret.
*/
public void restartCaretBlinking(boolean caretIsVisible) {
synchronized(this) {
fCaretShouldBlink = fStart.equals(fLimit);
fCaretCount = 0;
fCaretIsVisible = caretIsVisible;
if (fCaretShouldBlink) {
try {
notify();
}
catch (IllegalMonitorStateException e) {
System.out.println("Caught IllegalMonitorStateException: "+e);
}
}
}
}
public void removeFromOwner() {
stopCaretBlinking();
super.removeFromOwner();
}
}