You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by ca...@apache.org on 2007/11/13 01:40:58 UTC

svn commit: r594367 [8/9] - in /xmlgraphics/batik/trunk: ./ resources/org/apache/batik/apps/svgbrowser/resources/ resources/org/apache/batik/util/gui/resources/ sources-1.3/org/apache/batik/util/ sources-1.3/org/apache/batik/util/gui/ sources-1.4/org/a...

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/JEditTextArea.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/JEditTextArea.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/JEditTextArea.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/JEditTextArea.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,2128 @@
+/*
+ * 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 org.gjt.sp.jedit.textarea;
+
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.undo.*;
+import javax.swing.*;
+import java.awt.datatransfer.*;
+import java.awt.event.*;
+import java.awt.*;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * 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 $
+ */
+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();
+	}
+}

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/KeywordMap.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/KeywordMap.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/KeywordMap.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/KeywordMap.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,139 @@
+/*
+ * KeywordMap.java - Fast keyword->id map
+ * Copyright (C) 1998, 1999 Slava Pestov
+ * Copyright (C) 1999 Mike Dillon
+ *
+ * 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 org.gjt.sp.jedit.textarea;
+
+import javax.swing.text.Segment;
+
+/**
+ * A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
+ * to values. However, the `keys' are Swing segments. This allows lookups of
+ * text substrings without the overhead of creating a new string object.
+ * <p>
+ * This class is used by <code>CTokenMarker</code> to map keywords to ids.
+ *
+ * @author Slava Pestov, Mike Dillon
+ * @version $Id: KeywordMap.java,v 1.16 1999/12/13 03:40:30 sp Exp $
+ */
+public class KeywordMap
+{
+	/**
+	 * Creates a new <code>KeywordMap</code>.
+	 * @param ignoreCase True if keys are case insensitive
+	 */
+	public KeywordMap(boolean ignoreCase)
+	{
+		this(ignoreCase, 52);
+		this.ignoreCase = ignoreCase;
+	}
+
+	/**
+	 * Creates a new <code>KeywordMap</code>.
+	 * @param ignoreCase True if the keys are case insensitive
+	 * @param mapLength The number of `buckets' to create.
+	 * A value of 52 will give good performance for most maps.
+	 */
+	public KeywordMap(boolean ignoreCase, int mapLength)
+	{
+		this.mapLength = mapLength;
+		this.ignoreCase = ignoreCase;
+		map = new Keyword[mapLength];
+	}
+
+	/**
+	 * Looks up a key.
+	 * @param text The text segment
+	 * @param offset The offset of the substring within the text segment
+	 * @param length The length of the substring
+	 */
+	public byte lookup(Segment text, int offset, int length)
+	{
+		if(length == 0)
+			return Token.NULL;
+		Keyword k = map[getSegmentMapKey(text, offset, length)];
+		while(k != null)
+		{
+			if(length != k.keyword.length)
+			{
+				k = k.next;
+				continue;
+			}
+			if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
+				k.keyword))
+				return k.id;
+			k = k.next;
+		}
+		return Token.NULL;
+	}
+
+	/**
+	 * Adds a key-value mapping.
+	 * @param keyword The key
+	 * @Param id The value
+	 */
+	public void add(String keyword, byte id)
+	{
+		int key = getStringMapKey(keyword);
+		map[key] = new Keyword(keyword.toCharArray(),id,map[key]);
+	}
+
+	/**
+	 * Returns true if the keyword map is set to be case insensitive,
+	 * false otherwise.
+	 */
+	public boolean getIgnoreCase()
+	{
+		return ignoreCase;
+	}
+
+	/**
+	 * Sets if the keyword map should be case insensitive.
+	 * @param ignoreCase True if the keyword map should be case
+	 * insensitive, false otherwise
+	 */
+	public void setIgnoreCase(boolean ignoreCase)
+	{
+		this.ignoreCase = ignoreCase;
+	}
+
+	// protected members
+	protected int mapLength;
+
+	protected int getStringMapKey(String s)
+	{
+		return (Character.toUpperCase(s.charAt(0)) +
+				Character.toUpperCase(s.charAt(s.length()-1)))
+				% mapLength;
+	}
+
+	protected int getSegmentMapKey(Segment s, int off, int len)
+	{
+		return (Character.toUpperCase(s.array[off]) +
+				Character.toUpperCase(s.array[off + len - 1]))
+				% mapLength;
+	}
+
+	// private members
+	class Keyword
+	{
+		public Keyword(char[] keyword, byte id, Keyword next)
+		{
+			this.keyword = keyword;
+			this.id = id;
+			this.next = next;
+		}
+
+		public char[] keyword;
+		public byte id;
+		public Keyword next;
+	}
+
+	private Keyword[] map;
+	private boolean ignoreCase;
+}

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxDocument.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxDocument.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxDocument.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxDocument.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,165 @@
+/*
+ * SyntaxDocument.java - Document that can be tokenized
+ * 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 org.gjt.sp.jedit.textarea;
+
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * A document implementation that can be tokenized by the syntax highlighting
+ * system.
+ *
+ * @author Slava Pestov
+ * @version $Id: SyntaxDocument.java,v 1.14 1999/12/13 03:40:30 sp Exp $
+ */
+public class SyntaxDocument extends PlainDocument
+{
+	/**
+	 * Returns the token marker that is to be used to split lines
+	 * of this document up into tokens. May return null if this
+	 * document is not to be colorized.
+	 */
+	public TokenMarker getTokenMarker()
+	{
+		return tokenMarker;
+	}
+
+	/**
+	 * Sets the token marker that is to be used to split lines of
+	 * this document up into tokens. May throw an exception if
+	 * this is not supported for this type of document.
+	 * @param tm The new token marker
+	 */
+	public void setTokenMarker(TokenMarker tm)
+	{
+		tokenMarker = tm;
+		if(tm == null)
+			return;
+		tokenMarker.insertLines(0,getDefaultRootElement()
+			.getElementCount());
+		tokenizeLines();
+	}
+
+	/**
+	 * Reparses the document, by passing all lines to the token
+	 * marker. This should be called after the document is first
+	 * loaded.
+	 */
+	public void tokenizeLines()
+	{
+		tokenizeLines(0,getDefaultRootElement().getElementCount());
+	}
+
+	/**
+	 * Reparses the document, by passing the specified lines to the
+	 * token marker. This should be called after a large quantity of
+	 * text is first inserted.
+	 * @param start The first line to parse
+	 * @param len The number of lines, after the first one to parse
+	 */
+	public void tokenizeLines(int start, int len)
+	{
+		if(tokenMarker == null || !tokenMarker.supportsMultilineTokens())
+			return;
+
+		Segment lineSegment = new Segment();
+		Element map = getDefaultRootElement();
+
+		len += start;
+
+		try
+		{
+			for(int i = start; i < len; i++)
+			{
+				Element lineElement = map.getElement(i);
+				int lineStart = lineElement.getStartOffset();
+				getText(lineStart,lineElement.getEndOffset()
+					- lineStart - 1,lineSegment);
+				tokenMarker.markTokens(lineSegment,i);
+			}
+		}
+		catch(BadLocationException bl)
+		{
+			bl.printStackTrace();
+		}
+	}
+
+	/**
+	 * Starts a compound edit that can be undone in one operation.
+	 * Subclasses that implement undo should override this method;
+	 * this class has no undo functionality so this method is
+	 * empty.
+	 */
+	public void beginCompoundEdit() {}
+
+	/**
+	 * Ends a compound edit that can be undone in one operation.
+	 * Subclasses that implement undo should override this method;
+	 * this class has no undo functionality so this method is
+	 * empty.
+	 */
+	public void endCompoundEdit() {}
+
+	/**
+	 * Adds an undoable edit to this document's undo list. The edit
+	 * should be ignored if something is currently being undone.
+	 * @param edit The undoable edit
+	 *
+	 * @since jEdit 2.2pre1
+	 */
+	public void addUndoableEdit(UndoableEdit edit) {}
+
+	// protected members
+	protected TokenMarker tokenMarker;
+
+	/**
+	 * We overwrite this method to update the token marker
+	 * state immediately so that any event listeners get a
+	 * consistent token marker.
+	 */
+	protected void fireInsertUpdate(DocumentEvent evt)
+	{
+		if(tokenMarker != null)
+		{
+			DocumentEvent.ElementChange ch = evt.getChange(
+				getDefaultRootElement());
+			if(ch != null)
+			{
+				tokenMarker.insertLines(ch.getIndex() + 1,
+					ch.getChildrenAdded().length -
+					ch.getChildrenRemoved().length);
+			}
+		}
+
+		super.fireInsertUpdate(evt);
+	}
+	
+	/**
+	 * We overwrite this method to update the token marker
+	 * state immediately so that any event listeners get a
+	 * consistent token marker.
+	 */
+	protected void fireRemoveUpdate(DocumentEvent evt)
+	{
+		if(tokenMarker != null)
+		{
+			DocumentEvent.ElementChange ch = evt.getChange(
+				getDefaultRootElement());
+			if(ch != null)
+			{
+				tokenMarker.deleteLines(ch.getIndex() + 1,
+					ch.getChildrenRemoved().length -
+					ch.getChildrenAdded().length);
+			}
+		}
+
+		super.fireRemoveUpdate(evt);
+	}
+}

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxStyle.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxStyle.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxStyle.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxStyle.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,136 @@
+/*
+ * SyntaxStyle.java - A simple text style class
+ * 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 org.gjt.sp.jedit.textarea;
+
+import java.awt.*;
+import java.util.StringTokenizer;
+
+/**
+ * A simple text style class. It can specify the color, italic flag,
+ * and bold flag of a run of text.
+ * @author Slava Pestov
+ * @version $Id: SyntaxStyle.java,v 1.6 1999/12/13 03:40:30 sp Exp $
+ */
+public class SyntaxStyle
+{
+	/**
+	 * Creates a new SyntaxStyle.
+	 * @param color The text color
+	 * @param italic True if the text should be italics
+	 * @param bold True if the text should be bold
+	 */
+	public SyntaxStyle(Color color, boolean italic, boolean bold)
+	{
+		this.color = color;
+		this.italic = italic;
+		this.bold = bold;
+	}
+
+	/**
+	 * Returns the color specified in this style.
+	 */
+	public Color getColor()
+	{
+		return color;
+	}
+
+	/**
+	 * Returns true if no font styles are enabled.
+	 */
+	public boolean isPlain()
+	{
+		return !(bold || italic);
+	}
+
+	/**
+	 * Returns true if italics is enabled for this style.
+	 */
+	public boolean isItalic()
+	{
+		return italic;
+	}
+
+	/**
+	 * Returns true if boldface is enabled for this style.
+	 */
+	public boolean isBold()
+	{
+		return bold;
+	}
+
+	/**
+	 * Returns the specified font, but with the style's bold and
+	 * italic flags applied.
+	 */
+	public Font getStyledFont(Font font)
+	{
+		if(font == null)
+			throw new NullPointerException("font param must not"
+				+ " be null");
+		if(font.equals(lastFont))
+			return lastStyledFont;
+		lastFont = font;
+		lastStyledFont = new Font(font.getFamily(),
+			(bold ? Font.BOLD : 0)
+			| (italic ? Font.ITALIC : 0),
+			font.getSize());
+		return lastStyledFont;
+	}
+
+	/**
+	 * Returns the font metrics for the styled font.
+	 */
+	public FontMetrics getFontMetrics(Font font)
+	{
+		if(font == null)
+			throw new NullPointerException("font param must not"
+				+ " be null");
+		if(font.equals(lastFont) && fontMetrics != null)
+			return fontMetrics;
+		lastFont = font;
+		lastStyledFont = new Font(font.getFamily(),
+			(bold ? Font.BOLD : 0)
+			| (italic ? Font.ITALIC : 0),
+			font.getSize());
+		fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(
+			lastStyledFont);
+		return fontMetrics;
+	}
+
+	/**
+	 * Sets the foreground color and font of the specified graphics
+	 * context to that specified in this style.
+	 * @param gfx The graphics context
+	 * @param font The font to add the styles to
+	 */
+	public void setGraphicsFlags(Graphics gfx, Font font)
+	{
+		Font _font = getStyledFont(font);
+		gfx.setFont(_font);
+		gfx.setColor(color);
+	}
+
+	/**
+	 * Returns a string representation of this object.
+	 */
+	public String toString()
+	{
+		return getClass().getName() + "[color=" + color +
+			(italic ? ",italic" : "") +
+			(bold ? ",bold" : "") + "]";
+	}
+
+	// private members
+	private Color color;
+	private boolean italic;
+	private boolean bold;
+	private Font lastFont;
+	private Font lastStyledFont;
+	private FontMetrics fontMetrics;
+}

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxUtilities.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxUtilities.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxUtilities.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/SyntaxUtilities.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,158 @@
+/*
+ * SyntaxUtilities.java - Utility functions used by syntax colorizing
+ * 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 org.gjt.sp.jedit.textarea;
+
+import javax.swing.text.*;
+import java.awt.*;
+
+/**
+ * Class with several utility functions used by jEdit's syntax colorizing
+ * subsystem.
+ *
+ * @author Slava Pestov
+ * @version $Id: SyntaxUtilities.java,v 1.9 1999/12/13 03:40:30 sp Exp $
+ */
+public class SyntaxUtilities
+{
+	/**
+	 * Checks if a subregion of a <code>Segment</code> is equal to a
+	 * string.
+	 * @param ignoreCase True if case should be ignored, false otherwise
+	 * @param text The segment
+	 * @param offset The offset into the segment
+	 * @param match The string to match
+	 */
+	public static boolean regionMatches(boolean ignoreCase, Segment text,
+					    int offset, String match)
+	{
+		int length = offset + match.length();
+		char[] textArray = text.array;
+		if(length > text.offset + text.count)
+			return false;
+		for(int i = offset, j = 0; i < length; i++, j++)
+		{
+			char c1 = textArray[i];
+			char c2 = match.charAt(j);
+			if(ignoreCase)
+			{
+				c1 = Character.toUpperCase(c1);
+				c2 = Character.toUpperCase(c2);
+			}
+			if(c1 != c2)
+				return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Checks if a subregion of a <code>Segment</code> is equal to a
+	 * character array.
+	 * @param ignoreCase True if case should be ignored, false otherwise
+	 * @param text The segment
+	 * @param offset The offset into the segment
+	 * @param match The character array to match
+	 */
+	public static boolean regionMatches(boolean ignoreCase, Segment text,
+					    int offset, char[] match)
+	{
+		int length = offset + match.length;
+		char[] textArray = text.array;
+		if(length > text.offset + text.count)
+			return false;
+		for(int i = offset, j = 0; i < length; i++, j++)
+		{
+			char c1 = textArray[i];
+			char c2 = match[j];
+			if(ignoreCase)
+			{
+				c1 = Character.toUpperCase(c1);
+				c2 = Character.toUpperCase(c2);
+			}
+			if(c1 != c2)
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Returns the default style table. This can be passed to the
+	 * <code>setStyles()</code> method of <code>SyntaxDocument</code>
+	 * to use the default syntax styles.
+	 */
+	public static SyntaxStyle[] getDefaultSyntaxStyles()
+	{
+		SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
+
+		styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,true);
+		styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,true);
+		styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true);
+		styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,true);
+		styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false);
+		styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x659600),false,false);
+		styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false);
+		styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true);
+		styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true);
+		styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true);
+		styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true);
+
+		return styles;
+	}
+
+	/**
+	 * Paints the specified line onto the graphics context. Note that this
+	 * method munges the offset and count values of the segment.
+	 * @param line The line segment
+	 * @param tokens The token list for the line
+	 * @param styles The syntax style list
+	 * @param expander The tab expander used to determine tab stops. May
+	 * be null
+	 * @param gfx The graphics context
+	 * @param x The x co-ordinate
+	 * @param y The y co-ordinate
+	 * @return The x co-ordinate, plus the width of the painted string
+	 */
+	public static int paintSyntaxLine(Segment line, Token tokens,
+		SyntaxStyle[] styles, TabExpander expander, Graphics gfx,
+		int x, int y)
+	{
+		Font defaultFont = gfx.getFont();
+		Color defaultColor = gfx.getColor();
+
+		int offset = 0;
+		for(;;)
+		{
+			byte id = tokens.id;
+			if(id == Token.END)
+				break;
+
+			int length = tokens.length;
+			if(id == Token.NULL)
+			{
+				if(!defaultColor.equals(gfx.getColor()))
+					gfx.setColor(defaultColor);
+				if(!defaultFont.equals(gfx.getFont()))
+					gfx.setFont(defaultFont);
+			}
+			else
+				styles[id].setGraphicsFlags(gfx,defaultFont);
+
+			line.count = length;
+			x = Utilities.drawTabbedText(line,x,y,gfx,expander,0);
+			line.offset += length;
+			offset += length;
+
+			tokens = tokens.next;
+		}
+
+		return x;
+	}
+
+	// private members
+	private SyntaxUtilities() {}
+}

Added: xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/TextAreaDefaults.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/TextAreaDefaults.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/TextAreaDefaults.java (added)
+++ xmlgraphics/batik/trunk/sources/org/gjt/sp/jedit/textarea/TextAreaDefaults.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,83 @@
+/*
+ * TextAreaDefaults.java - Encapsulates default values for various settings
+ * 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 org.gjt.sp.jedit.textarea;
+
+import javax.swing.JPopupMenu;
+import java.awt.Color;
+
+/**
+ * Encapsulates default settings for a text area. This can be passed
+ * to the constructor once the necessary fields have been filled out.
+ * The advantage of doing this over calling lots of set() methods after
+ * creating the text area is that this method is faster.
+ */
+public class TextAreaDefaults
+{
+	private static TextAreaDefaults DEFAULTS;
+
+	public InputHandler inputHandler;
+	public SyntaxDocument document;
+	public boolean editable;
+
+	public boolean caretVisible;
+	public boolean caretBlinks;
+	public boolean blockCaret;
+	public int electricScroll;
+
+	public int cols;
+	public int rows;
+	public SyntaxStyle[] styles;
+	public Color caretColor;
+	public Color selectionColor;
+	public Color lineHighlightColor;
+	public boolean lineHighlight;
+	public Color bracketHighlightColor;
+	public boolean bracketHighlight;
+	public Color eolMarkerColor;
+	public boolean eolMarkers;
+	public boolean paintInvalid;
+
+	public JPopupMenu popup;
+
+	/**
+	 * Returns a new TextAreaDefaults object with the default values filled
+	 * in.
+	 */
+	public static TextAreaDefaults getDefaults()
+	{
+		if(DEFAULTS == null)
+		{
+			DEFAULTS = new TextAreaDefaults();
+
+			DEFAULTS.inputHandler = new DefaultInputHandler();
+			DEFAULTS.inputHandler.addDefaultKeyBindings();
+			DEFAULTS.document = new SyntaxDocument();
+			DEFAULTS.editable = true;
+
+			DEFAULTS.caretVisible = true;
+			DEFAULTS.caretBlinks = true;
+			DEFAULTS.electricScroll = 3;
+
+			DEFAULTS.cols = 80;
+			DEFAULTS.rows = 25;
+			DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
+			DEFAULTS.caretColor = Color.red;
+			DEFAULTS.selectionColor = new Color(0xccccff);
+			DEFAULTS.lineHighlightColor = new Color(0xe0e0e0);
+			DEFAULTS.lineHighlight = true;
+			DEFAULTS.bracketHighlightColor = Color.black;
+			DEFAULTS.bracketHighlight = true;
+			DEFAULTS.eolMarkerColor = new Color(0x009999);
+			DEFAULTS.eolMarkers = true;
+			DEFAULTS.paintInvalid = true;
+		}
+
+		return DEFAULTS;
+	}
+}