Package eas.miscellaneous.jedit

Source Code of eas.miscellaneous.jedit.JEditTextArea

/*
* JEditTextArea.java - jEdit's text component
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/

package eas.miscellaneous.jedit;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import javax.swing.text.Utilities;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;

import eas.miscellaneous.jedit.tokenmarker.Token;
import eas.miscellaneous.jedit.tokenmarker.TokenMarker;

/**
* jEdit's text area component. It is more suited for editing program
* source code than JEditorPane, because it drops the unnecessary features
* (images, variable-width lines, and so on) and adds a whole bunch of
* useful goodies such as:
* <ul>
* <li>More flexible key binding scheme
* <li>Supports macro recorders
* <li>Rectangular selection
* <li>Bracket highlighting
* <li>Syntax highlighting
* <li>Command repetition
* <li>Block caret can be enabled
* </ul>
* It is also faster and doesn't have as many problems. It can be used
* in other applications; the only other part of jEdit it depends on is
* the syntax package.<p>
*
* To use it in your app, treat it like any other component, for example:
* <pre>JEditTextArea ta = new JEditTextArea();
* ta.setTokenMarker(new JavaTokenMarker());
* ta.setText("public class Test {\n"
*     + "    public static void main(String[] args) {\n"
*     + "        System.out.println(\"Hello World\");\n"
*     + "    }\n"
*     + "}");</pre>
*
* @author Slava Pestov
* @version $Id: JEditTextArea.java,v 1.36 1999/12/13 03:40:30 sp Exp $
*/
@SuppressWarnings("all")
public class JEditTextArea extends JComponent
{
  /**
   * Adding components with this name to the text area will place
   * them left of the horizontal scroll bar. In jEdit, the status
   * bar is added this way.
   */
  public static String LEFT_OF_SCROLLBAR = "los";

  /**
   * Creates a new JEditTextArea with the default settings.
   */
  public JEditTextArea()
  {
    this(TextAreaDefaults.getDefaults());
  }

  /**
   * Creates a new JEditTextArea with the specified settings.
   * @param defaults The default settings
   */
  public JEditTextArea(TextAreaDefaults defaults)
  {
    // Enable the necessary events
    enableEvents(AWTEvent.KEY_EVENT_MASK);

    // Initialize some misc. stuff
    painter = new TextAreaPainter(this,defaults);
    documentHandler = new DocumentHandler();
    listenerList = new EventListenerList();
    caretEvent = new MutableCaretEvent();
    lineSegment = new Segment();
    bracketLine = bracketPosition = -1;
    blink = true;

    // Initialize the GUI
    setLayout(new ScrollLayout());
    add(CENTER,painter);
    add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL));
    add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL));

    // Add some event listeners
    vertical.addAdjustmentListener(new AdjustHandler());
    horizontal.addAdjustmentListener(new AdjustHandler());
    painter.addComponentListener(new ComponentHandler());
    painter.addMouseListener(new MouseHandler());
    painter.addMouseMotionListener(new DragHandler());
    addFocusListener(new FocusHandler());

    // Load the defaults
    setInputHandler(defaults.inputHandler);
    setDocument(defaults.document);
    editable = defaults.editable;
    caretVisible = defaults.caretVisible;
    caretBlinks = defaults.caretBlinks;
    electricScroll = defaults.electricScroll;

    popup = defaults.popup;

    // We don't seem to get the initial focus event?
    focusedComponent = this;
  }

  /**
   * Returns if this component can be traversed by pressing
   * the Tab key. This returns false.
   */
  public final boolean isManagingFocus()
  {
    return true;
  }

  /**
   * Returns the object responsible for painting this text area.
   */
  public final TextAreaPainter getPainter()
  {
    return painter;
  }

  /**
   * Returns the input handler.
   */
  public final InputHandler getInputHandler()
  {
    return inputHandler;
  }

  /**
   * Sets the input handler.
   * @param inputHandler The new input handler
   */
  public void setInputHandler(InputHandler inputHandler)
  {
    this.inputHandler = inputHandler;
  }

  /**
   * Returns true if the caret is blinking, false otherwise.
   */
  public final boolean isCaretBlinkEnabled()
  {
    return caretBlinks;
  }

  /**
   * Toggles caret blinking.
   * @param caretBlinks True if the caret should blink, false otherwise
   */
  public void setCaretBlinkEnabled(boolean caretBlinks)
  {
    this.caretBlinks = caretBlinks;
    if(!caretBlinks)
      blink = false;

    painter.invalidateSelectedLines();
  }

  /**
   * Returns true if the caret is visible, false otherwise.
   */
  public final boolean isCaretVisible()
  {
    return (!caretBlinks || blink) && caretVisible;
  }

  /**
   * Sets if the caret should be visible.
   * @param caretVisible True if the caret should be visible, false
   * otherwise
   */
  public void setCaretVisible(boolean caretVisible)
  {
    this.caretVisible = caretVisible;
    blink = true;

    painter.invalidateSelectedLines();
  }

  /**
   * Blinks the caret.
   */
  public final void blinkCaret()
  {
    if(caretBlinks)
    {
      blink = !blink;
      painter.invalidateSelectedLines();
    }
    else
      blink = true;
  }

  /**
   * Returns the number of lines from the top and button of the
   * text area that are always visible.
   */
  public final int getElectricScroll()
  {
    return electricScroll;
  }

  /**
   * Sets the number of lines from the top and bottom of the text
   * area that are always visible
   * @param electricScroll The number of lines always visible from
   * the top or bottom
   */
  public final void setElectricScroll(int electricScroll)
  {
    this.electricScroll = electricScroll;
  }

  /**
   * Updates the state of the scroll bars. This should be called
   * if the number of lines in the document changes, or when the
   * size of the text are changes.
   */
  public void updateScrollBars()
  {
    if(vertical != null && visibleLines != 0)
    {
      vertical.setValues(firstLine,visibleLines,0,getLineCount());
      vertical.setUnitIncrement(2);
      vertical.setBlockIncrement(visibleLines);
    }

    int width = painter.getWidth();
    if(horizontal != null && width != 0)
    {
      horizontal.setValues(-horizontalOffset,width,0,width * 5);
      horizontal.setUnitIncrement(painter.getFontMetrics()
        .charWidth('w'));
      horizontal.setBlockIncrement(width / 2);
    }
  }

  /**
   * Returns the line displayed at the text area's origin.
   */
  public final int getFirstLine()
  {
    return firstLine;
  }

  /**
   * Sets the line displayed at the text area's origin without
   * updating the scroll bars.
   */
  public void setFirstLine(int firstLine)
  {
    if(firstLine == this.firstLine)
      return;
    int oldFirstLine = this.firstLine;
    this.firstLine = firstLine;
    if(firstLine != vertical.getValue())
      updateScrollBars();
    painter.repaint();
  }

  /**
   * Returns the number of lines visible in this text area.
   */
  public final int getVisibleLines()
  {
    return visibleLines;
  }

  /**
   * Recalculates the number of visible lines. This should not
   * be called directly.
   */
  public final void recalculateVisibleLines()
  {
    if(painter == null)
      return;
    int height = painter.getHeight();
    int lineHeight = painter.getFontMetrics().getHeight();
    int oldVisibleLines = visibleLines;
    visibleLines = height / lineHeight;
    updateScrollBars();
  }

  /**
   * Returns the horizontal offset of drawn lines.
   */
  public final int getHorizontalOffset()
  {
    return horizontalOffset;
  }

  /**
   * Sets the horizontal offset of drawn lines. This can be used to
   * implement horizontal scrolling.
   * @param horizontalOffset offset The new horizontal offset
   */
  public void setHorizontalOffset(int horizontalOffset)
  {
    if(horizontalOffset == this.horizontalOffset)
      return;
    this.horizontalOffset = horizontalOffset;
    if(horizontalOffset != horizontal.getValue())
      updateScrollBars();
    painter.repaint();
  }

  /**
   * A fast way of changing both the first line and horizontal
   * offset.
   * @param firstLine The new first line
   * @param horizontalOffset The new horizontal offset
   * @return True if any of the values were changed, false otherwise
   */
  public boolean setOrigin(int firstLine, int horizontalOffset)
  {
    boolean changed = false;
    int oldFirstLine = this.firstLine;

    if(horizontalOffset != this.horizontalOffset)
    {
      this.horizontalOffset = horizontalOffset;
      changed = true;
    }

    if(firstLine != this.firstLine)
    {
      this.firstLine = firstLine;
      changed = true;
    }

    if(changed)
    {
      updateScrollBars();
      painter.repaint();
    }

    return changed;
  }

  /**
   * Ensures that the caret is visible by scrolling the text area if
   * necessary.
   * @return True if scrolling was actually performed, false if the
   * caret was already visible
   */
  public boolean scrollToCaret()
  {
    int line = getCaretLine();
    int lineStart = getLineStartOffset(line);
    int offset = Math.max(0,Math.min(getLineLength(line) - 1,
      getCaretPosition() - lineStart));

    return scrollTo(line,offset);
  }

  /**
   * Ensures that the specified line and offset is visible by scrolling
   * the text area if necessary.
   * @param line The line to scroll to
   * @param offset The offset in the line to scroll to
   * @return True if scrolling was actually performed, false if the
   * line and offset was already visible
   */
  public boolean scrollTo(int line, int offset)
  {
    // visibleLines == 0 before the component is realized
    // we can't do any proper scrolling then, so we have
    // this hack...
    if(visibleLines == 0)
    {
      setFirstLine(Math.max(0,line - electricScroll));
      return true;
    }

    int newFirstLine = firstLine;
    int newHorizontalOffset = horizontalOffset;

    if(line < firstLine + electricScroll)
    {
      newFirstLine = Math.max(0,line - electricScroll);
    }
    else if(line + electricScroll >= firstLine + visibleLines)
    {
      newFirstLine = (line - visibleLines) + electricScroll + 1;
      if(newFirstLine + visibleLines >= getLineCount())
        newFirstLine = getLineCount() - visibleLines;
      if(newFirstLine < 0)
        newFirstLine = 0;
    }

    int x = _offsetToX(line,offset);
    int width = painter.getFontMetrics().charWidth('w');

    if(x < 0)
    {
      newHorizontalOffset = Math.min(0,horizontalOffset
        - x + width + 5);
    }
    else if(x + width >= painter.getWidth())
    {
      newHorizontalOffset = horizontalOffset +
        (painter.getWidth() - x) - width - 5;
    }

    return setOrigin(newFirstLine,newHorizontalOffset);
  }

  /**
   * Converts a line index to a y co-ordinate.
   * @param line The line
   */
  public int lineToY(int line)
  {
    FontMetrics fm = painter.getFontMetrics();
    return (line - firstLine) * fm.getHeight()
      - (fm.getLeading() + fm.getMaxDescent());
  }

  /**
   * Converts a y co-ordinate to a line index.
   * @param y The y co-ordinate
   */
  public int yToLine(int y)
  {
    FontMetrics fm = painter.getFontMetrics();
    int height = fm.getHeight();
    return Math.max(0,Math.min(getLineCount() - 1,
      y / height + firstLine));
  }

  /**
   * Converts an offset in a line into an x co-ordinate. This is a
   * slow version that can be used any time.
   * @param line The line
   * @param offset The offset, from the start of the line
   */
  public final int offsetToX(int line, int offset)
  {
    // don't use cached tokens
    painter.currentLineTokens = null;
    return _offsetToX(line,offset);
  }

  /**
   * Converts an offset in a line into an x co-ordinate. This is a
   * fast version that should only be used if no changes were made
   * to the text since the last repaint.
   * @param line The line
   * @param offset The offset, from the start of the line
   */
  public int _offsetToX(int line, int offset)
  {
    TokenMarker tokenMarker = getTokenMarker();

    /* Use painter's cached info for speed */
    FontMetrics fm = painter.getFontMetrics();

    getLineText(line,lineSegment);

    int segmentOffset = lineSegment.offset;
    int x = horizontalOffset;

    /* If syntax coloring is disabled, do simple translation */
    if(tokenMarker == null)
    {
      lineSegment.count = offset;
      return x + Utilities.getTabbedTextWidth(lineSegment,
        fm,x,painter,0);
    }
    /* If syntax coloring is enabled, we have to do this because
     * tokens can vary in width */
    else
    {
      Token tokens;
      if(painter.currentLineIndex == line
        && painter.currentLineTokens != null)
        tokens = painter.currentLineTokens;
      else
      {
        painter.currentLineIndex = line;
        tokens = painter.currentLineTokens
          = tokenMarker.markTokens(lineSegment,line);
      }

      Toolkit toolkit = painter.getToolkit();
      Font defaultFont = painter.getFont();
      SyntaxStyle[] styles = painter.getStyles();

      for(;;)
      {
        byte id = tokens.id;
        if(id == Token.END)
        {
          return x;
        }

        if(id == Token.NULL)
          fm = painter.getFontMetrics();
        else
          fm = styles[id].getFontMetrics(defaultFont);

        int length = tokens.length;

        if(offset + segmentOffset < lineSegment.offset + length)
        {
          lineSegment.count = offset - (lineSegment.offset - segmentOffset);
          return x + Utilities.getTabbedTextWidth(
            lineSegment,fm,x,painter,0);
        }
        else
        {
          lineSegment.count = length;
          x += Utilities.getTabbedTextWidth(
            lineSegment,fm,x,painter,0);
          lineSegment.offset += length;
        }
        tokens = tokens.next;
      }
    }
  }

  /**
   * Converts an x co-ordinate to an offset within a line.
   * @param line The line
   * @param x The x co-ordinate
   */
  public int xToOffset(int line, int x)
  {
    TokenMarker tokenMarker = getTokenMarker();

    /* Use painter's cached info for speed */
    FontMetrics fm = painter.getFontMetrics();

    getLineText(line,lineSegment);

    char[] segmentArray = lineSegment.array;
    int segmentOffset = lineSegment.offset;
    int segmentCount = lineSegment.count;

    int width = horizontalOffset;

    if(tokenMarker == null)
    {
      for(int i = 0; i < segmentCount; i++)
      {
        char c = segmentArray[i + segmentOffset];
        int charWidth;
        if(c == '\t')
          charWidth = (int)painter.nextTabStop(width,i)
            - width;
        else
          charWidth = fm.charWidth(c);

        if(painter.isBlockCaretEnabled())
        {
          if(x - charWidth <= width)
            return i;
        }
        else
        {
          if(x - charWidth / 2 <= width)
            return i;
        }

        width += charWidth;
      }

      return segmentCount;
    }
    else
    {
      Token tokens;
      if(painter.currentLineIndex == line && painter
        .currentLineTokens != null)
        tokens = painter.currentLineTokens;
      else
      {
        painter.currentLineIndex = line;
        tokens = painter.currentLineTokens
          = tokenMarker.markTokens(lineSegment,line);
      }

      int offset = 0;
      Toolkit toolkit = painter.getToolkit();
      Font defaultFont = painter.getFont();
      SyntaxStyle[] styles = painter.getStyles();

      for(;;)
      {
        byte id = tokens.id;
        if(id == Token.END)
          return offset;

        if(id == Token.NULL)
          fm = painter.getFontMetrics();
        else
          fm = styles[id].getFontMetrics(defaultFont);

        int length = tokens.length;

        for(int i = 0; i < length; i++)
        {
          char c = segmentArray[segmentOffset + offset + i];
          int charWidth;
          if(c == '\t')
            charWidth = (int)painter.nextTabStop(width,offset + i)
              - width;
          else
            charWidth = fm.charWidth(c);

          if(painter.isBlockCaretEnabled())
          {
            if(x - charWidth <= width)
              return offset + i;
          }
          else
          {
            if(x - charWidth / 2 <= width)
              return offset + i;
          }

          width += charWidth;
        }

        offset += length;
        tokens = tokens.next;
      }
    }
  }

  /**
   * Converts a point to an offset, from the start of the text.
   * @param x The x co-ordinate of the point
   * @param y The y co-ordinate of the point
   */
  public int xyToOffset(int x, int y)
  {
    int line = yToLine(y);
    int start = getLineStartOffset(line);
    return start + xToOffset(line,x);
  }

  /**
   * Returns the document this text area is editing.
   */
  public final SyntaxDocument getDocument()
  {
    return document;
  }

  /**
   * Sets the document this text area is editing.
   * @param document The document
   */
  public void setDocument(SyntaxDocument document)
  {
    if(this.document == document)
      return;
    if(this.document != null)
      this.document.removeDocumentListener(documentHandler);
    this.document = document;

    document.addDocumentListener(documentHandler);

    select(0,0);
    updateScrollBars();
    painter.repaint();
  }

  /**
   * Returns the document's token marker. Equivalent to calling
   * <code>getDocument().getTokenMarker()</code>.
   */
  public final TokenMarker getTokenMarker()
  {
    return document.getTokenMarker();
  }

  /**
   * Sets the document's token marker. Equivalent to caling
   * <code>getDocument().setTokenMarker()</code>.
   * @param tokenMarker The token marker
   */
  public final void setTokenMarker(TokenMarker tokenMarker)
  {
    document.setTokenMarker(tokenMarker);
  }

  /**
   * Returns the length of the document. Equivalent to calling
   * <code>getDocument().getLength()</code>.
   */
  public final int getDocumentLength()
  {
    return document.getLength();
  }

  /**
   * Returns the number of lines in the document.
   */
  public final int getLineCount()
  {
    return document.getDefaultRootElement().getElementCount();
  }

  /**
   * Returns the line containing the specified offset.
   * @param offset The offset
   */
  public final int getLineOfOffset(int offset)
  {
    return document.getDefaultRootElement().getElementIndex(offset);
  }

  /**
   * Returns the start offset of the specified line.
   * @param line The line
   * @return The start offset of the specified line, or -1 if the line is
   * invalid
   */
  public int getLineStartOffset(int line)
  {
    Element lineElement = document.getDefaultRootElement()
      .getElement(line);
    if(lineElement == null)
      return -1;
    else
      return lineElement.getStartOffset();
  }

  /**
   * Returns the end offset of the specified line.
   * @param line The line
   * @return The end offset of the specified line, or -1 if the line is
   * invalid.
   */
  public int getLineEndOffset(int line)
  {
    Element lineElement = document.getDefaultRootElement()
      .getElement(line);
    if(lineElement == null)
      return -1;
    else
      return lineElement.getEndOffset();
  }

  /**
   * Returns the length of the specified line.
   * @param line The line
   */
  public int getLineLength(int line)
  {
    Element lineElement = document.getDefaultRootElement()
      .getElement(line);
    if(lineElement == null)
      return -1;
    else
      return lineElement.getEndOffset()
        - lineElement.getStartOffset() - 1;
  }

  /**
   * Returns the entire text of this text area.
   */
  public String getText()
  {
    try
    {
      return document.getText(0,document.getLength());
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
      return null;
    }
  }

  /**
   * Sets the entire text of this text area.
   */
  public void setText(String text)
  {
    try
    {
      document.beginCompoundEdit();
      document.remove(0,document.getLength());
      document.insertString(0,text,null);
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
    }
    finally
    {
      document.endCompoundEdit();
    }
  }

  /**
   * Returns the specified substring of the document.
   * @param start The start offset
   * @param len The length of the substring
   * @return The substring, or null if the offsets are invalid
   */
  public final String getText(int start, int len)
  {
    try
    {
      return document.getText(start,len);
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
      return null;
    }
  }

  /**
   * Copies the specified substring of the document into a segment.
   * If the offsets are invalid, the segment will contain a null string.
   * @param start The start offset
   * @param len The length of the substring
   * @param segment The segment
   */
  public final void getText(int start, int len, Segment segment)
  {
    try
    {
      document.getText(start,len,segment);
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
      segment.offset = segment.count = 0;
    }
  }

  /**
   * Returns the text on the specified line.
   * @param lineIndex The line
   * @return The text, or null if the line is invalid
   */
  public final String getLineText(int lineIndex)
  {
    int start = getLineStartOffset(lineIndex);
    return getText(start,getLineEndOffset(lineIndex) - start - 1);
  }

  /**
   * Copies the text on the specified line into a segment. If the line
   * is invalid, the segment will contain a null string.
   * @param lineIndex The line
   */
  public final void getLineText(int lineIndex, Segment segment)
  {
    int start = getLineStartOffset(lineIndex);
    getText(start,getLineEndOffset(lineIndex) - start - 1,segment);
  }

  /**
   * Returns the selection start offset.
   */
  public final int getSelectionStart()
  {
    return selectionStart;
  }

  /**
   * Returns the offset where the selection starts on the specified
   * line.
   */
  public int getSelectionStart(int line)
  {
    if(line == selectionStartLine)
      return selectionStart;
    else if(rectSelect)
    {
      Element map = document.getDefaultRootElement();
      int start = selectionStart - map.getElement(selectionStartLine)
        .getStartOffset();

      Element lineElement = map.getElement(line);
      int lineStart = lineElement.getStartOffset();
      int lineEnd = lineElement.getEndOffset() - 1;
      return Math.min(lineEnd,lineStart + start);
    }
    else
      return getLineStartOffset(line);
  }

  /**
   * Returns the selection start line.
   */
  public final int getSelectionStartLine()
  {
    return selectionStartLine;
  }

  /**
   * Sets the selection start. The new selection will be the new
   * selection start and the old selection end.
   * @param selectionStart The selection start
   * @see #select(int,int)
   */
  public final void setSelectionStart(int selectionStart)
  {
    select(selectionStart,selectionEnd);
  }

  /**
   * Returns the selection end offset.
   */
  public final int getSelectionEnd()
  {
    return selectionEnd;
  }

  /**
   * Returns the offset where the selection ends on the specified
   * line.
   */
  public int getSelectionEnd(int line)
  {
    if(line == selectionEndLine)
      return selectionEnd;
    else if(rectSelect)
    {
      Element map = document.getDefaultRootElement();
      int end = selectionEnd - map.getElement(selectionEndLine)
        .getStartOffset();

      Element lineElement = map.getElement(line);
      int lineStart = lineElement.getStartOffset();
      int lineEnd = lineElement.getEndOffset() - 1;
      return Math.min(lineEnd,lineStart + end);
    }
    else
      return getLineEndOffset(line) - 1;
  }

  /**
   * Returns the selection end line.
   */
  public final int getSelectionEndLine()
  {
    return selectionEndLine;
  }

  /**
   * Sets the selection end. The new selection will be the old
   * selection start and the bew selection end.
   * @param selectionEnd The selection end
   * @see #select(int,int)
   */
  public final void setSelectionEnd(int selectionEnd)
  {
    select(selectionStart,selectionEnd);
  }

  /**
   * Returns the caret position. This will either be the selection
   * start or the selection end, depending on which direction the
   * selection was made in.
   */
  public final int getCaretPosition()
  {
    return (biasLeft ? selectionStart : selectionEnd);
  }

  /**
   * Returns the caret line.
   */
  public final int getCaretLine()
  {
    return (biasLeft ? selectionStartLine : selectionEndLine);
  }

  /**
   * Returns the mark position. This will be the opposite selection
   * bound to the caret position.
   * @see #getCaretPosition()
   */
  public final int getMarkPosition()
  {
    return (biasLeft ? selectionEnd : selectionStart);
  }

  /**
   * Returns the mark line.
   */
  public final int getMarkLine()
  {
    return (biasLeft ? selectionEndLine : selectionStartLine);
  }

  /**
   * Sets the caret position. The new selection will consist of the
   * caret position only (hence no text will be selected)
   * @param caret The caret position
   * @see #select(int,int)
   */
  public final void setCaretPosition(int caret)
  {
    select(caret,caret);
  }

  /**
   * Selects all text in the document.
   */
  public final void selectAll()
  {
    select(0,getDocumentLength());
  }

  /**
   * Moves the mark to the caret position.
   */
  public final void selectNone()
  {
    select(getCaretPosition(),getCaretPosition());
  }

  /**
   * Selects from the start offset to the end offset. This is the
   * general selection method used by all other selecting methods.
   * The caret position will be start if start &lt; end, and end
   * if end &gt; start.
   * @param start The start offset
   * @param end The end offset
   */
  public void select(int start, int end)
  {
    int newStart, newEnd;
    boolean newBias;
    if(start <= end)
    {
      newStart = start;
      newEnd = end;
      newBias = false;
    }
    else
    {
      newStart = end;
      newEnd = start;
      newBias = true;
    }

    if(newStart < 0 || newEnd > getDocumentLength())
    {
      throw new IllegalArgumentException("Bounds out of"
        + " range: " + newStart + "," +
        newEnd);
    }

    // If the new position is the same as the old, we don't
    // do all this crap, however we still do the stuff at
    // the end (clearing magic position, scrolling)
    if(newStart != selectionStart || newEnd != selectionEnd
      || newBias != biasLeft)
    {
      int newStartLine = getLineOfOffset(newStart);
      int newEndLine = getLineOfOffset(newEnd);

      if(painter.isBracketHighlightEnabled())
      {
        if(bracketLine != -1)
          painter.invalidateLine(bracketLine);
        updateBracketHighlight(end);
        if(bracketLine != -1)
          painter.invalidateLine(bracketLine);
      }

      painter.invalidateLineRange(selectionStartLine,selectionEndLine);
      painter.invalidateLineRange(newStartLine,newEndLine);

      document.addUndoableEdit(new CaretUndo(
        selectionStart,selectionEnd));

      selectionStart = newStart;
      selectionEnd = newEnd;
      selectionStartLine = newStartLine;
      selectionEndLine = newEndLine;
      biasLeft = newBias;

      fireCaretEvent();
    }

    // When the user is typing, etc, we don't want the caret
    // to blink
    blink = true;
    caretTimer.restart();

    // Disable rectangle select if selection start = selection end
    if(selectionStart == selectionEnd)
      rectSelect = false;

    // Clear the `magic' caret position used by up/down
    magicCaret = -1;

    scrollToCaret();
  }

  /**
   * Returns the selected text, or null if no selection is active.
   */
  public final String getSelectedText()
  {
    if(selectionStart == selectionEnd)
      return null;

    if(rectSelect)
    {
      // Return each row of the selection on a new line

      Element map = document.getDefaultRootElement();

      int start = selectionStart - map.getElement(selectionStartLine)
        .getStartOffset();
      int end = selectionEnd - map.getElement(selectionEndLine)
        .getStartOffset();

      // Certain rectangles satisfy this condition...
      if(end < start)
      {
        int tmp = end;
        end = start;
        start = tmp;
      }

      StringBuffer buf = new StringBuffer();
      Segment seg = new Segment();

      for(int i = selectionStartLine; i <= selectionEndLine; i++)
      {
        Element lineElement = map.getElement(i);
        int lineStart = lineElement.getStartOffset();
        int lineEnd = lineElement.getEndOffset() - 1;
        int lineLen = lineEnd - lineStart;

        lineStart = Math.min(lineStart + start,lineEnd);
        lineLen = Math.min(end - start,lineEnd - lineStart);

        getText(lineStart,lineLen,seg);
        buf.append(seg.array,seg.offset,seg.count);

        if(i != selectionEndLine)
          buf.append('\n');
      }

      return buf.toString();
    }
    else
    {
      return getText(selectionStart,
        selectionEnd - selectionStart);
    }
  }

  /**
   * Replaces the selection with the specified text.
   * @param selectedText The replacement text for the selection
   */
  public void setSelectedText(String selectedText)
  {
    if(!editable)
    {
      throw new InternalError("Text component"
        + " read only");
    }

    document.beginCompoundEdit();

    try
    {
      if(rectSelect)
      {
        Element map = document.getDefaultRootElement();

        int start = selectionStart - map.getElement(selectionStartLine)
          .getStartOffset();
        int end = selectionEnd - map.getElement(selectionEndLine)
          .getStartOffset();

        // Certain rectangles satisfy this condition...
        if(end < start)
        {
          int tmp = end;
          end = start;
          start = tmp;
        }

        int lastNewline = 0;
        int currNewline = 0;

        for(int i = selectionStartLine; i <= selectionEndLine; i++)
        {
          Element lineElement = map.getElement(i);
          int lineStart = lineElement.getStartOffset();
          int lineEnd = lineElement.getEndOffset() - 1;
          int rectStart = Math.min(lineEnd,lineStart + start);

          document.remove(rectStart,Math.min(lineEnd - rectStart,
            end - start));

          if(selectedText == null)
            continue;

          currNewline = selectedText.indexOf('\n',lastNewline);
          if(currNewline == -1)
            currNewline = selectedText.length();

          document.insertString(rectStart,selectedText
            .substring(lastNewline,currNewline),null);

          lastNewline = Math.min(selectedText.length(),
            currNewline + 1);
        }

        if(selectedText != null &&
          currNewline != selectedText.length())
        {
          int offset = map.getElement(selectionEndLine)
            .getEndOffset() - 1;
          document.insertString(offset,"\n",null);
          document.insertString(offset + 1,selectedText
            .substring(currNewline + 1),null);
        }
      }
      else
      {
        document.remove(selectionStart,
          selectionEnd - selectionStart);
        if(selectedText != null)
        {
          document.insertString(selectionStart,
            selectedText,null);
        }
      }
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
      throw new InternalError("Cannot replace"
        + " selection");
    }
    // No matter what happends... stops us from leaving document
    // in a bad state
    finally
    {
      document.endCompoundEdit();
    }

    setCaretPosition(selectionEnd);
  }

  /**
   * Returns true if this text area is editable, false otherwise.
   */
  public final boolean isEditable()
  {
    return editable;
  }

  /**
   * Sets if this component is editable.
   * @param editable True if this text area should be editable,
   * false otherwise
   */
  public final void setEditable(boolean editable)
  {
    this.editable = editable;
  }

  /**
   * Returns the right click popup menu.
   */
  public final JPopupMenu getRightClickPopup()
  {
    return popup;
  }

  /**
   * Sets the right click popup menu.
   * @param popup The popup
   */
  public final void setRightClickPopup(JPopupMenu popup)
  {
    this.popup = popup;
  }

  /**
   * Returns the `magic' caret position. This can be used to preserve
   * the column position when moving up and down lines.
   */
  public final int getMagicCaretPosition()
  {
    return magicCaret;
  }

  /**
   * Sets the `magic' caret position. This can be used to preserve
   * the column position when moving up and down lines.
   * @param magicCaret The magic caret position
   */
  public final void setMagicCaretPosition(int magicCaret)
  {
    this.magicCaret = magicCaret;
  }

  /**
   * Similar to <code>setSelectedText()</code>, but overstrikes the
   * appropriate number of characters if overwrite mode is enabled.
   * @param str The string
   * @see #setSelectedText(String)
   * @see #isOverwriteEnabled()
   */
  public void overwriteSetSelectedText(String str)
  {
    // Don't overstrike if there is a selection
    if(!overwrite || selectionStart != selectionEnd)
    {
      setSelectedText(str);
      return;
    }

    // Don't overstrike if we're on the end of
    // the line
    int caret = getCaretPosition();
    int caretLineEnd = getLineEndOffset(getCaretLine());
    if(caretLineEnd - caret <= str.length())
    {
      setSelectedText(str);
      return;
    }

    document.beginCompoundEdit();

    try
    {
      document.remove(caret,str.length());
      document.insertString(caret,str,null);
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
    }
    finally
    {
      document.endCompoundEdit();
    }
  }

  /**
   * Returns true if overwrite mode is enabled, false otherwise.
   */
  public final boolean isOverwriteEnabled()
  {
    return overwrite;
  }

  /**
   * Sets if overwrite mode should be enabled.
   * @param overwrite True if overwrite mode should be enabled,
   * false otherwise.
   */
  public final void setOverwriteEnabled(boolean overwrite)
  {
    this.overwrite = overwrite;
    painter.invalidateSelectedLines();
  }

  /**
   * Returns true if the selection is rectangular, false otherwise.
   */
  public final boolean isSelectionRectangular()
  {
    return rectSelect;
  }

  /**
   * Sets if the selection should be rectangular.
   * @param overwrite True if the selection should be rectangular,
   * false otherwise.
   */
  public final void setSelectionRectangular(boolean rectSelect)
  {
    this.rectSelect = rectSelect;
    painter.invalidateSelectedLines();
  }

  /**
   * Returns the position of the highlighted bracket (the bracket
   * matching the one before the caret)
   */
  public final int getBracketPosition()
  {
    return bracketPosition;
  }

  /**
   * Returns the line of the highlighted bracket (the bracket
   * matching the one before the caret)
   */
  public final int getBracketLine()
  {
    return bracketLine;
  }

  /**
   * Adds a caret change listener to this text area.
   * @param listener The listener
   */
  public final void addCaretListener(CaretListener listener)
  {
    listenerList.add(CaretListener.class,listener);
  }

  /**
   * Removes a caret change listener from this text area.
   * @param listener The listener
   */
  public final void removeCaretListener(CaretListener listener)
  {
    listenerList.remove(CaretListener.class,listener);
  }

  /**
   * Deletes the selected text from the text area and places it
   * into the clipboard.
   */
  public void cut()
  {
    if(editable)
    {
      copy();
      setSelectedText("");
    }
  }

  /**
   * Places the selected text into the clipboard.
   */
  public void copy()
  {
    if(selectionStart != selectionEnd)
    {
      Clipboard clipboard = getToolkit().getSystemClipboard();

      String selection = getSelectedText();

      int repeatCount = inputHandler.getRepeatCount();
      StringBuffer buf = new StringBuffer();
      for(int i = 0; i < repeatCount; i++)
        buf.append(selection);

      clipboard.setContents(new StringSelection(buf.toString()),null);
    }
  }

  /**
   * Inserts the clipboard contents into the text.
   */
  public void paste()
  {
    if(editable)
    {
      Clipboard clipboard = getToolkit().getSystemClipboard();
      try
      {
        // The MacOS MRJ doesn't convert \r to \n,
        // so do it here
        String selection = ((String)clipboard
          .getContents(this).getTransferData(
          DataFlavor.stringFlavor))
          .replace('\r','\n');

        int repeatCount = inputHandler.getRepeatCount();
        StringBuffer buf = new StringBuffer();
        for(int i = 0; i < repeatCount; i++)
          buf.append(selection);
        selection = buf.toString();
        setSelectedText(selection);
      }
      catch(Exception e)
      {
        getToolkit().beep();
        System.err.println("Clipboard does not"
          + " contain a string");
      }
    }
  }

  /**
   * Called by the AWT when this component is removed from it's parent.
   * This stops clears the currently focused component.
   */
  public void removeNotify()
  {
    super.removeNotify();
    if(focusedComponent == this)
      focusedComponent = null;
  }

  /**
   * Forwards key events directly to the input handler.
   * This is slightly faster than using a KeyListener
   * because some Swing overhead is avoided.
   */
  public void processKeyEvent(KeyEvent evt)
  {
    if(inputHandler == null)
      return;
    switch(evt.getID())
    {
    case KeyEvent.KEY_TYPED:
      inputHandler.keyTyped(evt);
      break;
    case KeyEvent.KEY_PRESSED:
      inputHandler.keyPressed(evt);
      break;
    case KeyEvent.KEY_RELEASED:
      inputHandler.keyReleased(evt);
      break;
    }
  }

  // protected members
  protected static String CENTER = "center";
  protected static String RIGHT = "right";
  protected static String BOTTOM = "bottom";

  protected static JEditTextArea focusedComponent;
  protected static Timer caretTimer;
 
  protected TextAreaPainter painter;

  protected JPopupMenu popup;

  protected EventListenerList listenerList;
  protected MutableCaretEvent caretEvent;

  protected boolean caretBlinks;
  protected boolean caretVisible;
  protected boolean blink;

  protected boolean editable;

  protected int firstLine;
  protected int visibleLines;
  protected int electricScroll;

  protected int horizontalOffset;
 
  protected JScrollBar vertical;
  protected JScrollBar horizontal;
  protected boolean scrollBarsInitialized;

  protected InputHandler inputHandler;
  protected SyntaxDocument document;
  protected DocumentHandler documentHandler;

  protected Segment lineSegment;

  protected int selectionStart;
  protected int selectionStartLine;
  protected int selectionEnd;
  protected int selectionEndLine;
  protected boolean biasLeft;

  protected int bracketPosition;
  protected int bracketLine;

  protected int magicCaret;
  protected boolean overwrite;
  protected boolean rectSelect;

  protected void fireCaretEvent()
  {
    Object[] listeners = listenerList.getListenerList();
    for(int i = listeners.length - 2; i >= 0; i--)
    {
      if(listeners[i] == CaretListener.class)
      {
        ((CaretListener)listeners[i+1]).caretUpdate(caretEvent);
      }
    }
  }

  protected void updateBracketHighlight(int newCaretPosition)
  {
    if(newCaretPosition == 0)
    {
      bracketPosition = bracketLine = -1;
      return;
    }

    try
    {
      int offset = TextUtilities.findMatchingBracket(
        document,newCaretPosition - 1);
      if(offset != -1)
      {
        bracketLine = getLineOfOffset(offset);
        bracketPosition = offset - getLineStartOffset(bracketLine);
        return;
      }
    }
    catch(BadLocationException bl)
    {
      bl.printStackTrace();
    }

    bracketLine = bracketPosition = -1;
  }

  protected void documentChanged(DocumentEvent evt)
  {
    DocumentEvent.ElementChange ch = evt.getChange(
      document.getDefaultRootElement());

    int count;
    if(ch == null)
      count = 0;
    else
      count = ch.getChildrenAdded().length -
        ch.getChildrenRemoved().length;

    int line = getLineOfOffset(evt.getOffset());
    if(count == 0)
    {
      painter.invalidateLine(line);
    }
    // do magic stuff
    else if(line < firstLine)
    {
      setFirstLine(firstLine + count);
    }
    // end of magic stuff
    else
    {
      painter.invalidateLineRange(line,firstLine + visibleLines);
      updateScrollBars();
    }
  }

  class ScrollLayout implements LayoutManager
  {
    public void addLayoutComponent(String name, Component comp)
    {
      if(name.equals(CENTER))
        center = comp;
      else if(name.equals(RIGHT))
        right = comp;
      else if(name.equals(BOTTOM))
        bottom = comp;
      else if(name.equals(LEFT_OF_SCROLLBAR))
        leftOfScrollBar.addElement(comp);
    }

    public void removeLayoutComponent(Component comp)
    {
      if(center == comp)
        center = null;
      if(right == comp)
        right = null;
      if(bottom == comp)
        bottom = null;
      else
        leftOfScrollBar.removeElement(comp);
    }

    public Dimension preferredLayoutSize(Container parent)
    {
      Dimension dim = new Dimension();
      Insets insets = getInsets();
      dim.width = insets.left + insets.right;
      dim.height = insets.top + insets.bottom;

      Dimension centerPref = center.getPreferredSize();
      dim.width += centerPref.width;
      dim.height += centerPref.height;
      Dimension rightPref = right.getPreferredSize();
      dim.width += rightPref.width;
      Dimension bottomPref = bottom.getPreferredSize();
      dim.height += bottomPref.height;

      return dim;
    }

    public Dimension minimumLayoutSize(Container parent)
    {
      Dimension dim = new Dimension();
      Insets insets = getInsets();
      dim.width = insets.left + insets.right;
      dim.height = insets.top + insets.bottom;

      Dimension centerPref = center.getMinimumSize();
      dim.width += centerPref.width;
      dim.height += centerPref.height;
      Dimension rightPref = right.getMinimumSize();
      dim.width += rightPref.width;
      Dimension bottomPref = bottom.getMinimumSize();
      dim.height += bottomPref.height;

      return dim;
    }

    public void layoutContainer(Container parent)
    {
      Dimension size = parent.getSize();
      Insets insets = parent.getInsets();
      int itop = insets.top;
      int ileft = insets.left;
      int ibottom = insets.bottom;
      int iright = insets.right;

      int rightWidth = right.getPreferredSize().width;
      int bottomHeight = bottom.getPreferredSize().height;
      int centerWidth = size.width - rightWidth - ileft - iright;
      int centerHeight = size.height - bottomHeight - itop - ibottom;

      center.setBounds(
        ileft,
        itop,
        centerWidth,
        centerHeight);

      right.setBounds(
        ileft + centerWidth,
        itop,
        rightWidth,
        centerHeight);

      // Lay out all status components, in order
      Enumeration status = leftOfScrollBar.elements();
      while(status.hasMoreElements())
      {
        Component comp = (Component)status.nextElement();
        Dimension dim = comp.getPreferredSize();
        comp.setBounds(ileft,
          itop + centerHeight,
          dim.width,
          bottomHeight);
        ileft += dim.width;
      }

      bottom.setBounds(
        ileft,
        itop + centerHeight,
        size.width - rightWidth - ileft - iright,
        bottomHeight);
    }

    // private members
    private Component center;
    private Component right;
    private Component bottom;
    private Vector leftOfScrollBar = new Vector();
  }

  static class CaretBlinker implements ActionListener
  {
    public void actionPerformed(ActionEvent evt)
    {
      if(focusedComponent != null
        && focusedComponent.hasFocus())
        focusedComponent.blinkCaret();
    }
  }

  class MutableCaretEvent extends CaretEvent
  {
    MutableCaretEvent()
    {
      super(JEditTextArea.this);
    }

    public int getDot()
    {
      return getCaretPosition();
    }

    public int getMark()
    {
      return getMarkPosition();
    }
  }

  class AdjustHandler implements AdjustmentListener
  {
    public void adjustmentValueChanged(final AdjustmentEvent evt)
    {
      if(!scrollBarsInitialized)
        return;

      // If this is not done, mousePressed events accumilate
      // and the result is that scrolling doesn't stop after
      // the mouse is released
      SwingUtilities.invokeLater(new Runnable() {
        public void run()
        {
          if(evt.getAdjustable() == vertical)
            setFirstLine(vertical.getValue());
          else
            setHorizontalOffset(-horizontal.getValue());
        }
      });
    }
  }

  class ComponentHandler extends ComponentAdapter
  {
    public void componentResized(ComponentEvent evt)
    {
      recalculateVisibleLines();
      scrollBarsInitialized = true;
    }
  }

  class DocumentHandler implements DocumentListener
  {
    public void insertUpdate(DocumentEvent evt)
    {
      documentChanged(evt);

      int offset = evt.getOffset();
      int length = evt.getLength();

      int newStart;
      int newEnd;

      if(selectionStart > offset || (selectionStart
        == selectionEnd && selectionStart == offset))
        newStart = selectionStart + length;
      else
        newStart = selectionStart;

      if(selectionEnd >= offset)
        newEnd = selectionEnd + length;
      else
        newEnd = selectionEnd;

      select(newStart,newEnd);
    }
 
    public void removeUpdate(DocumentEvent evt)
    {
      documentChanged(evt);

      int offset = evt.getOffset();
      int length = evt.getLength();

      int newStart;
      int newEnd;

      if(selectionStart > offset)
      {
        if(selectionStart > offset + length)
          newStart = selectionStart - length;
        else
          newStart = offset;
      }
      else
        newStart = selectionStart;

      if(selectionEnd > offset)
      {
        if(selectionEnd > offset + length)
          newEnd = selectionEnd - length;
        else
          newEnd = offset;
      }
      else
        newEnd = selectionEnd;

      select(newStart,newEnd);
    }

    public void changedUpdate(DocumentEvent evt)
    {
    }
  }

  class DragHandler implements MouseMotionListener
  {
    public void mouseDragged(MouseEvent evt)
    {
      if(popup != null && popup.isVisible())
        return;

      setSelectionRectangular((evt.getModifiers()
        & InputEvent.CTRL_MASK) != 0);
      select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY()));
    }

    public void mouseMoved(MouseEvent evt) {}
  }

  class FocusHandler implements FocusListener
  {
    public void focusGained(FocusEvent evt)
    {
      setCaretVisible(true);
      focusedComponent = JEditTextArea.this;
    }

    public void focusLost(FocusEvent evt)
    {
      setCaretVisible(false);
      focusedComponent = null;
    }
  }

  class MouseHandler extends MouseAdapter
  {
    public void mousePressed(MouseEvent evt)
    {
      requestFocus();

      // Focus events not fired sometimes?
      setCaretVisible(true);
      focusedComponent = JEditTextArea.this;

      if((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0
        && popup != null)
      {
        popup.show(painter,evt.getX(),evt.getY());
        return;
      }

      int line = yToLine(evt.getY());
      int offset = xToOffset(line,evt.getX());
      int dot = getLineStartOffset(line) + offset;

      switch(evt.getClickCount())
      {
      case 1:
        doSingleClick(evt,line,offset,dot);
        break;
      case 2:
        // It uses the bracket matching stuff, so
        // it can throw a BLE
        try
        {
          doDoubleClick(evt,line,offset,dot);
        }
        catch(BadLocationException bl)
        {
          bl.printStackTrace();
        }
        break;
      case 3:
        doTripleClick(evt,line,offset,dot);
        break;
      }
    }

    private void doSingleClick(MouseEvent evt, int line,
      int offset, int dot)
    {
      if((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0)
      {
        rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0;
        select(getMarkPosition(),dot);
      }
      else
        setCaretPosition(dot);
    }

    private void doDoubleClick(MouseEvent evt, int line,
      int offset, int dot) throws BadLocationException
    {
      // Ignore empty lines
      if(getLineLength(line) == 0)
        return;

      try
      {
        int bracket = TextUtilities.findMatchingBracket(
          document,Math.max(0,dot - 1));
        if(bracket != -1)
        {
          int mark = getMarkPosition();
          // Hack
          if(bracket > mark)
          {
            bracket++;
            mark--;
          }
          select(mark,bracket);
          return;
        }
      }
      catch(BadLocationException bl)
      {
        bl.printStackTrace();
      }

      // Ok, it's not a bracket... select the word
      String lineText = getLineText(line);
      char ch = lineText.charAt(Math.max(0,offset - 1));

      String noWordSep = (String)document.getProperty("noWordSep");
      if(noWordSep == null)
        noWordSep = "";

      // If the user clicked on a non-letter char,
      // we select the surrounding non-letters
      boolean selectNoLetter = (!Character
        .isLetterOrDigit(ch)
        && noWordSep.indexOf(ch) == -1);

      int wordStart = 0;

      for(int i = offset - 1; i >= 0; i--)
      {
        ch = lineText.charAt(i);
        if(selectNoLetter ^ (!Character
          .isLetterOrDigit(ch) &&
          noWordSep.indexOf(ch) == -1))
        {
          wordStart = i + 1;
          break;
        }
      }

      int wordEnd = lineText.length();
      for(int i = offset; i < lineText.length(); i++)
      {
        ch = lineText.charAt(i);
        if(selectNoLetter ^ (!Character
          .isLetterOrDigit(ch) &&
          noWordSep.indexOf(ch) == -1))
        {
          wordEnd = i;
          break;
        }
      }

      int lineStart = getLineStartOffset(line);
      select(lineStart + wordStart,lineStart + wordEnd);

      /*
      String lineText = getLineText(line);
      String noWordSep = (String)document.getProperty("noWordSep");
      int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep);
      int wordEnd = TextUtilities.findWordEnd(lineText,offset,noWordSep);

      int lineStart = getLineStartOffset(line);
      select(lineStart + wordStart,lineStart + wordEnd);
      */
    }

    private void doTripleClick(MouseEvent evt, int line,
      int offset, int dot)
    {
      select(getLineStartOffset(line),getLineEndOffset(line)-1);
    }
  }

  class CaretUndo extends AbstractUndoableEdit
  {
    private int start;
    private int end;

    CaretUndo(int start, int end)
    {
      this.start = start;
      this.end = end;
    }

    public boolean isSignificant()
    {
      return false;
    }

    public String getPresentationName()
    {
      return "caret move";
    }

    public void undo() throws CannotUndoException
    {
      super.undo();

      select(start,end);
    }

    public void redo() throws CannotRedoException
    {
      super.redo();

      select(start,end);
    }

    public boolean addEdit(UndoableEdit edit)
    {
      if(edit instanceof CaretUndo)
      {
        CaretUndo cedit = (CaretUndo)edit;
        start = cedit.start;
        end = cedit.end;
        cedit.die();

        return true;
      }
      else
        return false;
    }
  }

  static
  {
    caretTimer = new Timer(500,new CaretBlinker());
    caretTimer.setInitialDelay(500);
    caretTimer.start();
  }
}
TOP

Related Classes of eas.miscellaneous.jedit.JEditTextArea

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.