Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/dom/svg/SVGContext.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/dom/svg/SVGContext.java (revision 549415)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/dom/svg/SVGContext.java (working copy)
@@ -94,4 +94,9 @@
* Returns the font-size on the associated element.
*/
float getFontSize();
+
+ /**
+ * whether parsing should be strict (throw exception on error) or not
+ */
+ boolean strictParsing = false;
}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/svg/SVGUserAgentGUIAdapter.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/svg/SVGUserAgentGUIAdapter.java (revision 549415)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/svg/SVGUserAgentGUIAdapter.java (working copy)
@@ -22,6 +22,9 @@
import java.awt.Component;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
+
+import org.apache.batik.swing.JSVGCanvas;
+import org.apache.batik.util.gui.ErrorConsole;
import org.apache.batik.util.gui.JErrorPane;
/**
@@ -53,8 +56,11 @@
* Displays an error resulting from the specified Exception.
*/
public void displayError(Exception ex) {
- JErrorPane pane = new JErrorPane(ex, JOptionPane.ERROR_MESSAGE);
- JDialog dialog = pane.createDialog(parentComponent, "ERROR");
+// JErrorPane pane = new JErrorPane(ex, JOptionPane.ERROR_MESSAGE);
+ ErrorConsole console = ErrorConsole.getInstance();
+ // Document is null for now, how to get access to it??
+ console.add(ex, null, JOptionPane.ERROR_MESSAGE);
+ JDialog dialog = ErrorConsole.createDialog(parentComponent, "ERROR");
dialog.setModal(false);
dialog.setVisible(true);
}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/JSVGCanvas.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/JSVGCanvas.java (revision 549415)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/swing/JSVGCanvas.java (working copy)
@@ -57,6 +57,7 @@
import org.apache.batik.swing.svg.SVGUserAgent;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLConstants;
+import org.apache.batik.util.gui.ErrorConsole;
import org.apache.batik.util.gui.JErrorPane;
import org.w3c.dom.Element;
@@ -1070,9 +1071,11 @@
if (svgUserAgent != null) {
super.displayError(ex);
} else {
- JErrorPane pane =
- new JErrorPane(ex, JOptionPane.ERROR_MESSAGE);
- JDialog dialog = pane.createDialog(JSVGCanvas.this, "ERROR");
+// JErrorPane pane =
+// new JErrorPane(ex, JOptionPane.ERROR_MESSAGE);
+ ErrorConsole console = ErrorConsole.getInstance();
+ console.add(ex, getURI(), JOptionPane.ERROR_MESSAGE);
+ JDialog dialog = ErrorConsole.createDialog(JSVGCanvas.this, "ERROR");
dialog.setModal(false);
dialog.setVisible(true); // Safe to be called from any thread
}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/DocumentLoader.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/DocumentLoader.java (revision 549415)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/DocumentLoader.java (working copy)
@@ -20,12 +20,15 @@
import java.io.InputStream;
import java.io.IOException;
+import java.lang.reflect.Proxy;
import java.util.HashMap;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGDocumentFactory;
import org.apache.batik.dom.util.DocumentDescriptor;
+import org.apache.batik.swing.svg.SVGDocumentLoader;
import org.apache.batik.util.CleanerThread;
+import org.apache.batik.util.gui.ErrorHandlerProxy;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -104,6 +107,13 @@
return ret;
SVGDocument document = documentFactory.createSVGDocument(uri);
+// System.out.println("Installing proxy");
+// SVGDocumentFactory documentFactoryProxy = (SVGDocumentFactory) Proxy
+// .newProxyInstance(SVGDocumentFactory.class
+// .getClassLoader(),
+// new Class[] { SVGDocumentFactory.class },
+// new ErrorHandlerProxy(documentFactory, uri));
+// SVGDocument document = documentFactoryProxy.createSVGDocument(uri);
DocumentDescriptor desc = documentFactory.getDocumentDescriptor();
DocumentState state = new DocumentState(uri, document, desc);
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/SVGRectElementBridge.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/SVGRectElementBridge.java (revision 549415)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/bridge/SVGRectElementBridge.java (working copy)
@@ -22,12 +22,15 @@
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
+import javax.swing.JOptionPane;
+
import org.apache.batik.dom.svg.AbstractSVGAnimatedLength;
import org.apache.batik.dom.svg.AnimatedLiveAttributeValue;
import org.apache.batik.dom.svg.LiveAttributeException;
import org.apache.batik.dom.svg.SVGOMRectElement;
import org.apache.batik.gvt.ShapeNode;
import org.apache.batik.gvt.ShapePainter;
+import org.apache.batik.util.gui.ErrorConsole;
import org.w3c.dom.Element;
@@ -116,7 +119,12 @@
}
shapeNode.setShape(shape);
} catch (LiveAttributeException ex) {
- throw new BridgeException(ctx, ex);
+ if (strictParsing) {
+ throw new BridgeException(ctx, ex);
+ } else {
+ ctx.userAgent.displayError(ex);
+ shapeNode.setShape(new Rectangle2D.Float(0, 0, 0, 0));
+ }
}
}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaPainter.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaPainter.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaPainter.java (revision 0)
@@ -0,0 +1,693 @@
+/*
+ * TextAreaPainter.java - Paints the text area
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.ToolTipManager;
+import javax.swing.text.*;
+import javax.swing.JComponent;
+import java.awt.event.MouseEvent;
+import java.awt.*;
+
+/**
+ * The text area repaint manager. It performs double buffering and paints
+ * lines of text.
+ * @author Slava Pestov
+ * @version $Id: TextAreaPainter.java,v 1.24 1999/12/13 03:40:30 sp Exp $
+ */
+public class TextAreaPainter extends JComponent implements TabExpander
+{
+ /**
+ * Creates a new repaint manager. This should be not be called
+ * directly.
+ */
+ public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
+ {
+ this.textArea = textArea;
+
+ setAutoscrolls(true);
+ setDoubleBuffered(true);
+ setOpaque(true);
+
+ ToolTipManager.sharedInstance().registerComponent(this);
+
+ currentLine = new Segment();
+ currentLineIndex = -1;
+
+ setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+
+ setFont(new Font("Monospaced",Font.PLAIN,14));
+ setForeground(Color.black);
+ setBackground(Color.white);
+
+ blockCaret = defaults.blockCaret;
+ styles = defaults.styles;
+ cols = defaults.cols;
+ rows = defaults.rows;
+ caretColor = defaults.caretColor;
+ selectionColor = defaults.selectionColor;
+ lineHighlightColor = defaults.lineHighlightColor;
+ lineHighlight = defaults.lineHighlight;
+ bracketHighlightColor = defaults.bracketHighlightColor;
+ bracketHighlight = defaults.bracketHighlight;
+ paintInvalid = defaults.paintInvalid;
+ eolMarkerColor = defaults.eolMarkerColor;
+ eolMarkers = defaults.eolMarkers;
+ }
+
+ /**
+ * Returns if this component can be traversed by pressing the
+ * Tab key. This returns false.
+ */
+ public final boolean isManagingFocus()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the syntax styles used to paint colorized text. Entry n
+ * will be used to paint tokens with id = n.
+ * @see org.gjt.sp.jedit.syntax.Token
+ */
+ public final SyntaxStyle[] getStyles()
+ {
+ return styles;
+ }
+
+ /**
+ * Sets the syntax styles used to paint colorized text. Entry n
+ * will be used to paint tokens with id = n.
+ * @param styles The syntax styles
+ * @see org.gjt.sp.jedit.syntax.Token
+ */
+ public final void setStyles(SyntaxStyle[] styles)
+ {
+ this.styles = styles;
+ repaint();
+ }
+
+ /**
+ * Returns the caret color.
+ */
+ public final Color getCaretColor()
+ {
+ return caretColor;
+ }
+
+ /**
+ * Sets the caret color.
+ * @param caretColor The caret color
+ */
+ public final void setCaretColor(Color caretColor)
+ {
+ this.caretColor = caretColor;
+ invalidateSelectedLines();
+ }
+
+ /**
+ * Returns the selection color.
+ */
+ public final Color getSelectionColor()
+ {
+ return selectionColor;
+ }
+
+ /**
+ * Sets the selection color.
+ * @param selectionColor The selection color
+ */
+ public final void setSelectionColor(Color selectionColor)
+ {
+ this.selectionColor = selectionColor;
+ invalidateSelectedLines();
+ }
+
+ /**
+ * Returns the line highlight color.
+ */
+ public final Color getLineHighlightColor()
+ {
+ return lineHighlightColor;
+ }
+
+ /**
+ * Sets the line highlight color.
+ * @param lineHighlightColor The line highlight color
+ */
+ public final void setLineHighlightColor(Color lineHighlightColor)
+ {
+ this.lineHighlightColor = lineHighlightColor;
+ invalidateSelectedLines();
+ }
+
+ /**
+ * Returns true if line highlight is enabled, false otherwise.
+ */
+ public final boolean isLineHighlightEnabled()
+ {
+ return lineHighlight;
+ }
+
+ /**
+ * Enables or disables current line highlighting.
+ * @param lineHighlight True if current line highlight should be enabled,
+ * false otherwise
+ */
+ public final void setLineHighlightEnabled(boolean lineHighlight)
+ {
+ this.lineHighlight = lineHighlight;
+ invalidateSelectedLines();
+ }
+
+ /**
+ * Returns the bracket highlight color.
+ */
+ public final Color getBracketHighlightColor()
+ {
+ return bracketHighlightColor;
+ }
+
+ /**
+ * Sets the bracket highlight color.
+ * @param bracketHighlightColor The bracket highlight color
+ */
+ public final void setBracketHighlightColor(Color bracketHighlightColor)
+ {
+ this.bracketHighlightColor = bracketHighlightColor;
+ invalidateLine(textArea.getBracketLine());
+ }
+
+ /**
+ * Returns true if bracket highlighting is enabled, false otherwise.
+ * When bracket highlighting is enabled, the bracket matching the
+ * one before the caret (if any) is highlighted.
+ */
+ public final boolean isBracketHighlightEnabled()
+ {
+ return bracketHighlight;
+ }
+
+ /**
+ * Enables or disables bracket highlighting.
+ * When bracket highlighting is enabled, the bracket matching the
+ * one before the caret (if any) is highlighted.
+ * @param bracketHighlight True if bracket highlighting should be
+ * enabled, false otherwise
+ */
+ public final void setBracketHighlightEnabled(boolean bracketHighlight)
+ {
+ this.bracketHighlight = bracketHighlight;
+ invalidateLine(textArea.getBracketLine());
+ }
+
+ /**
+ * Returns true if the caret should be drawn as a block, false otherwise.
+ */
+ public final boolean isBlockCaretEnabled()
+ {
+ return blockCaret;
+ }
+
+ /**
+ * Sets if the caret should be drawn as a block, false otherwise.
+ * @param blockCaret True if the caret should be drawn as a block,
+ * false otherwise.
+ */
+ public final void setBlockCaretEnabled(boolean blockCaret)
+ {
+ this.blockCaret = blockCaret;
+ invalidateSelectedLines();
+ }
+
+ /**
+ * Returns the EOL marker color.
+ */
+ public final Color getEOLMarkerColor()
+ {
+ return eolMarkerColor;
+ }
+
+ /**
+ * Sets the EOL marker color.
+ * @param eolMarkerColor The EOL marker color
+ */
+ public final void setEOLMarkerColor(Color eolMarkerColor)
+ {
+ this.eolMarkerColor = eolMarkerColor;
+ repaint();
+ }
+
+ /**
+ * Returns true if EOL markers are drawn, false otherwise.
+ */
+ public final boolean getEOLMarkersPainted()
+ {
+ return eolMarkers;
+ }
+
+ /**
+ * Sets if EOL markers are to be drawn.
+ * @param eolMarkers True if EOL markers should be drawn, false otherwise
+ */
+ public final void setEOLMarkersPainted(boolean eolMarkers)
+ {
+ this.eolMarkers = eolMarkers;
+ repaint();
+ }
+
+ /**
+ * Returns true if invalid lines are painted as red tildes (~),
+ * false otherwise.
+ */
+ public boolean getInvalidLinesPainted()
+ {
+ return paintInvalid;
+ }
+
+ /**
+ * Sets if invalid lines are to be painted as red tildes.
+ * @param paintInvalid True if invalid lines should be drawn, false otherwise
+ */
+ public void setInvalidLinesPainted(boolean paintInvalid)
+ {
+ this.paintInvalid = paintInvalid;
+ }
+
+ /**
+ * Adds a custom highlight painter.
+ * @param highlight The highlight
+ */
+ public void addCustomHighlight(Highlight highlight)
+ {
+ highlight.init(textArea,highlights);
+ highlights = highlight;
+ }
+
+ /**
+ * Highlight interface.
+ */
+ public interface Highlight
+ {
+ /**
+ * Called after the highlight painter has been added.
+ * @param textArea The text area
+ * @param next The painter this one should delegate to
+ */
+ void init(JEditTextArea textArea, Highlight next);
+
+ /**
+ * This should paint the highlight and delgate to the
+ * next highlight painter.
+ * @param gfx The graphics context
+ * @param line The line number
+ * @param y The y co-ordinate of the line
+ */
+ void paintHighlight(Graphics gfx, int line, int y);
+
+ /**
+ * Returns the tool tip to display at the specified
+ * location. If this highlighter doesn't know what to
+ * display, it should delegate to the next highlight
+ * painter.
+ * @param evt The mouse event
+ */
+ String getToolTipText(MouseEvent evt);
+ }
+
+ /**
+ * Returns the tool tip to display at the specified location.
+ * @param evt The mouse event
+ */
+ public String getToolTipText(MouseEvent evt)
+ {
+ if(highlights != null)
+ return highlights.getToolTipText(evt);
+ else
+ return null;
+ }
+
+ /**
+ * Returns the font metrics used by this component.
+ */
+ public FontMetrics getFontMetrics()
+ {
+ return fm;
+ }
+
+ /**
+ * Sets the font for this component. This is overridden to update the
+ * cached font metrics and to recalculate which lines are visible.
+ * @param font The font
+ */
+ public void setFont(Font font)
+ {
+ super.setFont(font);
+ fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ textArea.recalculateVisibleLines();
+ }
+
+ /**
+ * Repaints the text.
+ * @param g The graphics context
+ */
+ public void paint(Graphics gfx)
+ {
+ tabSize = fm.charWidth(' ') * ((Integer)textArea
+ .getDocument().getProperty(
+ PlainDocument.tabSizeAttribute)).intValue();
+
+ Rectangle clipRect = gfx.getClipBounds();
+
+ gfx.setColor(getBackground());
+ gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
+
+ // We don't use yToLine() here because that method doesn't
+ // return lines past the end of the document
+ int height = fm.getHeight();
+ int firstLine = textArea.getFirstLine();
+ int firstInvalid = firstLine + clipRect.y / height;
+ // Because the clipRect's height is usually an even multiple
+ // of the font height, we subtract 1 from it, otherwise one
+ // too many lines will always be painted.
+ int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
+
+ try
+ {
+ TokenMarker tokenMarker = textArea.getDocument()
+ .getTokenMarker();
+ int x = textArea.getHorizontalOffset();
+
+ for(int line = firstInvalid; line <= lastInvalid; line++)
+ {
+ paintLine(gfx,tokenMarker,line,x);
+ }
+
+ if(tokenMarker != null && tokenMarker.isNextLineRequested())
+ {
+ int h = clipRect.y + clipRect.height;
+ repaint(0,h,getWidth(),getHeight() - h);
+ }
+ }
+ catch(Exception e)
+ {
+ System.err.println("Error repainting line"
+ + " range {" + firstInvalid + ","
+ + lastInvalid + "}:");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Marks a line as needing a repaint.
+ * @param line The line to invalidate
+ */
+ public final void invalidateLine(int line)
+ {
+ repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
+ getWidth(),fm.getHeight());
+ }
+
+ /**
+ * Marks a range of lines as needing a repaint.
+ * @param firstLine The first line to invalidate
+ * @param lastLine The last line to invalidate
+ */
+ public final void invalidateLineRange(int firstLine, int lastLine)
+ {
+ repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(),
+ getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
+ }
+
+ /**
+ * Repaints the lines containing the selection.
+ */
+ public final void invalidateSelectedLines()
+ {
+ invalidateLineRange(textArea.getSelectionStartLine(),
+ textArea.getSelectionEndLine());
+ }
+
+ /**
+ * Implementation of TabExpander interface. Returns next tab stop after
+ * a specified point.
+ * @param x The x co-ordinate
+ * @param tabOffset Ignored
+ * @return The next tab stop after x
+ */
+ public float nextTabStop(float x, int tabOffset)
+ {
+ int offset = textArea.getHorizontalOffset();
+ int ntabs = ((int)x - offset) / tabSize;
+ return (ntabs + 1) * tabSize + offset;
+ }
+
+ /**
+ * Returns the painter's preferred size.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension dim = new Dimension();
+ dim.width = fm.charWidth('w') * cols;
+ dim.height = fm.getHeight() * rows;
+ return dim;
+ }
+
+
+ /**
+ * Returns the painter's minimum size.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ // package-private members
+ int currentLineIndex;
+ Token currentLineTokens;
+ Segment currentLine;
+
+ // protected members
+ protected JEditTextArea textArea;
+
+ protected SyntaxStyle[] styles;
+ protected Color caretColor;
+ protected Color selectionColor;
+ protected Color lineHighlightColor;
+ protected Color bracketHighlightColor;
+ protected Color eolMarkerColor;
+
+ protected boolean blockCaret;
+ protected boolean lineHighlight;
+ protected boolean bracketHighlight;
+ protected boolean paintInvalid;
+ protected boolean eolMarkers;
+ protected int cols;
+ protected int rows;
+
+ protected int tabSize;
+ protected FontMetrics fm;
+
+ protected Highlight highlights;
+
+ protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
+ int line, int x)
+ {
+ Font defaultFont = getFont();
+ Color defaultColor = getForeground();
+
+ currentLineIndex = line;
+ int y = textArea.lineToY(line);
+
+ if(line < 0 || line >= textArea.getLineCount())
+ {
+ if(paintInvalid)
+ {
+ paintHighlight(gfx,line,y);
+ styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
+ gfx.drawString("~",0,y + fm.getHeight());
+ }
+ }
+ else if(tokenMarker == null)
+ {
+ paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
+ }
+ else
+ {
+ paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
+ defaultColor,x,y);
+ }
+ }
+
+ protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
+ Color defaultColor, int x, int y)
+ {
+ paintHighlight(gfx,line,y);
+ textArea.getLineText(line,currentLine);
+
+ gfx.setFont(defaultFont);
+ gfx.setColor(defaultColor);
+
+ y += fm.getHeight();
+ x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
+
+ if(eolMarkers)
+ {
+ gfx.setColor(eolMarkerColor);
+ gfx.drawString(".",x,y);
+ }
+ }
+
+ protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
+ int line, Font defaultFont, Color defaultColor, int x, int y)
+ {
+ textArea.getLineText(currentLineIndex,currentLine);
+ currentLineTokens = tokenMarker.markTokens(currentLine,
+ currentLineIndex);
+
+ paintHighlight(gfx,line,y);
+
+ gfx.setFont(defaultFont);
+ gfx.setColor(defaultColor);
+ y += fm.getHeight();
+ x = SyntaxUtilities.paintSyntaxLine(currentLine,
+ currentLineTokens,styles,this,gfx,x,y);
+
+ if(eolMarkers)
+ {
+ gfx.setColor(eolMarkerColor);
+ gfx.drawString(".",x,y);
+ }
+ }
+
+ protected void paintHighlight(Graphics gfx, int line, int y)
+ {
+ if(line >= textArea.getSelectionStartLine()
+ && line <= textArea.getSelectionEndLine())
+ paintLineHighlight(gfx,line,y);
+
+ if(highlights != null)
+ highlights.paintHighlight(gfx,line,y);
+
+ if(bracketHighlight && line == textArea.getBracketLine())
+ paintBracketHighlight(gfx,line,y);
+
+ if(line == textArea.getCaretLine())
+ paintCaret(gfx,line,y);
+ }
+
+ protected void paintLineHighlight(Graphics gfx, int line, int y)
+ {
+ int height = fm.getHeight();
+ y += fm.getLeading() + fm.getMaxDescent();
+
+ int selectionStart = textArea.getSelectionStart();
+ int selectionEnd = textArea.getSelectionEnd();
+
+ if(selectionStart == selectionEnd)
+ {
+ if(lineHighlight)
+ {
+ gfx.setColor(lineHighlightColor);
+ gfx.fillRect(0,y,getWidth(),height);
+ }
+ }
+ else
+ {
+ gfx.setColor(selectionColor);
+
+ int selectionStartLine = textArea.getSelectionStartLine();
+ int selectionEndLine = textArea.getSelectionEndLine();
+ int lineStart = textArea.getLineStartOffset(line);
+
+ int x1, x2;
+ if(textArea.isSelectionRectangular())
+ {
+ int lineLen = textArea.getLineLength(line);
+ x1 = textArea._offsetToX(line,Math.min(lineLen,
+ selectionStart - textArea.getLineStartOffset(
+ selectionStartLine)));
+ x2 = textArea._offsetToX(line,Math.min(lineLen,
+ selectionEnd - textArea.getLineStartOffset(
+ selectionEndLine)));
+ if(x1 == x2)
+ x2++;
+ }
+ else if(selectionStartLine == selectionEndLine)
+ {
+ x1 = textArea._offsetToX(line,
+ selectionStart - lineStart);
+ x2 = textArea._offsetToX(line,
+ selectionEnd - lineStart);
+ }
+ else if(line == selectionStartLine)
+ {
+ x1 = textArea._offsetToX(line,
+ selectionStart - lineStart);
+ x2 = getWidth();
+ }
+ else if(line == selectionEndLine)
+ {
+ x1 = 0;
+ x2 = textArea._offsetToX(line,
+ selectionEnd - lineStart);
+ }
+ else
+ {
+ x1 = 0;
+ x2 = getWidth();
+ }
+
+ // "inlined" min/max()
+ gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
+ (x1 - x2) : (x2 - x1),height);
+ }
+
+ }
+
+ protected void paintBracketHighlight(Graphics gfx, int line, int y)
+ {
+ int position = textArea.getBracketPosition();
+ if(position == -1)
+ return;
+ y += fm.getLeading() + fm.getMaxDescent();
+ int x = textArea._offsetToX(line,position);
+ gfx.setColor(bracketHighlightColor);
+ // Hack!!! Since there is no fast way to get the character
+ // from the bracket matching routine, we use ( since all
+ // brackets probably have the same width anyway
+ gfx.drawRect(x,y,fm.charWidth('(') - 1,
+ fm.getHeight() - 1);
+ }
+
+ protected void paintCaret(Graphics gfx, int line, int y)
+ {
+ if(textArea.isCaretVisible())
+ {
+ int offset = textArea.getCaretPosition()
+ - textArea.getLineStartOffset(line);
+ int caretX = textArea._offsetToX(line,offset);
+ int caretWidth = ((blockCaret ||
+ textArea.isOverwriteEnabled()) ?
+ fm.charWidth('w') : 1);
+ y += fm.getLeading() + fm.getMaxDescent();
+ int height = fm.getHeight();
+
+ gfx.setColor(caretColor);
+
+ if(textArea.isOverwriteEnabled())
+ {
+ gfx.fillRect(caretX,y + height - 1,
+ caretWidth,1);
+ }
+ else
+ {
+ gfx.drawRect(caretX,y,caretWidth - 1,height - 1);
+ }
+ }
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/DefaultInputHandler.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/DefaultInputHandler.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/DefaultInputHandler.java (revision 0)
@@ -0,0 +1,347 @@
+/*
+ * DefaultInputHandler.java - Default implementation of an input handler
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.KeyStroke;
+import java.awt.event.*;
+import java.awt.Toolkit;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * The default input handler. It maps sequences of keystrokes into actions
+ * and inserts key typed events into the text area.
+ * @author Slava Pestov
+ * @version $Id: DefaultInputHandler.java,v 1.18 1999/12/13 03:40:30 sp Exp $
+ */
+public class DefaultInputHandler extends InputHandler
+{
+ /**
+ * Creates a new input handler with no key bindings defined.
+ */
+ public DefaultInputHandler()
+ {
+ bindings = currentBindings = new Hashtable();
+ }
+
+ /**
+ * Sets up the default key bindings.
+ */
+ public void addDefaultKeyBindings()
+ {
+ addKeyBinding("BACK_SPACE",BACKSPACE);
+ addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
+ addKeyBinding("DELETE",DELETE);
+ addKeyBinding("C+DELETE",DELETE_WORD);
+
+ addKeyBinding("ENTER",INSERT_BREAK);
+ addKeyBinding("TAB",INSERT_TAB);
+
+ addKeyBinding("INSERT",OVERWRITE);
+ addKeyBinding("C+\\",TOGGLE_RECT);
+
+ addKeyBinding("HOME",HOME);
+ addKeyBinding("END",END);
+ addKeyBinding("S+HOME",SELECT_HOME);
+ addKeyBinding("S+END",SELECT_END);
+ addKeyBinding("C+HOME",DOCUMENT_HOME);
+ addKeyBinding("C+END",DOCUMENT_END);
+ addKeyBinding("CS+HOME",SELECT_DOC_HOME);
+ addKeyBinding("CS+END",SELECT_DOC_END);
+
+ addKeyBinding("PAGE_UP",PREV_PAGE);
+ addKeyBinding("PAGE_DOWN",NEXT_PAGE);
+ addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
+ addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
+
+ addKeyBinding("LEFT",PREV_CHAR);
+ addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
+ addKeyBinding("C+LEFT",PREV_WORD);
+ addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
+ addKeyBinding("RIGHT",NEXT_CHAR);
+ addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
+ addKeyBinding("C+RIGHT",NEXT_WORD);
+ addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
+ addKeyBinding("UP",PREV_LINE);
+ addKeyBinding("S+UP",SELECT_PREV_LINE);
+ addKeyBinding("DOWN",NEXT_LINE);
+ addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
+
+ addKeyBinding("C+ENTER",REPEAT);
+ }
+
+ /**
+ * Adds a key binding to this input handler. The key binding is
+ * a list of white space separated key strokes of the form
+ * [modifiers+]key where modifier is C for Control, A for Alt,
+ * or S for Shift, and key is either a character (a-z) or a field
+ * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
+ * @param keyBinding The key binding
+ * @param action The action
+ */
+ public void addKeyBinding(String keyBinding, ActionListener action)
+ {
+ Hashtable current = bindings;
+
+ StringTokenizer st = new StringTokenizer(keyBinding);
+ while(st.hasMoreTokens())
+ {
+ KeyStroke keyStroke = parseKeyStroke(st.nextToken());
+ if(keyStroke == null)
+ return;
+
+ if(st.hasMoreTokens())
+ {
+ Object o = current.get(keyStroke);
+ if(o instanceof Hashtable)
+ current = (Hashtable)o;
+ else
+ {
+ o = new Hashtable();
+ current.put(keyStroke,o);
+ current = (Hashtable)o;
+ }
+ }
+ else
+ current.put(keyStroke,action);
+ }
+ }
+
+ /**
+ * Removes a key binding from this input handler. This is not yet
+ * implemented.
+ * @param keyBinding The key binding
+ */
+ public void removeKeyBinding(String keyBinding)
+ {
+ throw new InternalError("Not yet implemented");
+ }
+
+ /**
+ * Removes all key bindings from this input handler.
+ */
+ public void removeAllKeyBindings()
+ {
+ bindings.clear();
+ }
+
+ /**
+ * Returns a copy of this input handler that shares the same
+ * key bindings. Setting key bindings in the copy will also
+ * set them in the original.
+ */
+ public InputHandler copy()
+ {
+ return new DefaultInputHandler(this);
+ }
+
+ /**
+ * Handle a key pressed event. This will look up the binding for
+ * the key stroke and execute it.
+ */
+ public void keyPressed(KeyEvent evt)
+ {
+ int keyCode = evt.getKeyCode();
+ int modifiers = evt.getModifiers();
+
+ if(keyCode == KeyEvent.VK_CONTROL ||
+ keyCode == KeyEvent.VK_SHIFT ||
+ keyCode == KeyEvent.VK_ALT ||
+ keyCode == KeyEvent.VK_META)
+ return;
+
+ if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
+ || evt.isActionKey()
+ || keyCode == KeyEvent.VK_BACK_SPACE
+ || keyCode == KeyEvent.VK_DELETE
+ || keyCode == KeyEvent.VK_ENTER
+ || keyCode == KeyEvent.VK_TAB
+ || keyCode == KeyEvent.VK_ESCAPE)
+ {
+ if(grabAction != null)
+ {
+ handleGrabAction(evt);
+ return;
+ }
+
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
+ modifiers);
+ Object o = currentBindings.get(keyStroke);
+ if(o == null)
+ {
+ // Don't beep if the user presses some
+ // key we don't know about unless a
+ // prefix is active. Otherwise it will
+ // beep when caps lock is pressed, etc.
+ if(currentBindings != bindings)
+ {
+ Toolkit.getDefaultToolkit().beep();
+ // F10 should be passed on, but C+e F10
+ // shouldn't
+ repeatCount = 0;
+ repeat = false;
+ evt.consume();
+ }
+ currentBindings = bindings;
+ return;
+ }
+ else if(o instanceof ActionListener)
+ {
+ currentBindings = bindings;
+
+ executeAction(((ActionListener)o),
+ evt.getSource(),null);
+
+ evt.consume();
+ return;
+ }
+ else if(o instanceof Hashtable)
+ {
+ currentBindings = (Hashtable)o;
+ evt.consume();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Handle a key typed event. This inserts the key into the text area.
+ */
+ public void keyTyped(KeyEvent evt)
+ {
+ int modifiers = evt.getModifiers();
+ char c = evt.getKeyChar();
+ if(c != KeyEvent.CHAR_UNDEFINED &&
+ (modifiers & KeyEvent.ALT_MASK) == 0)
+ {
+ if(c >= 0x20 && c != 0x7f)
+ {
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(
+ Character.toUpperCase(c));
+ Object o = currentBindings.get(keyStroke);
+
+ if(o instanceof Hashtable)
+ {
+ currentBindings = (Hashtable)o;
+ return;
+ }
+ else if(o instanceof ActionListener)
+ {
+ currentBindings = bindings;
+ executeAction((ActionListener)o,
+ evt.getSource(),
+ String.valueOf(c));
+ return;
+ }
+
+ currentBindings = bindings;
+
+ if(grabAction != null)
+ {
+ handleGrabAction(evt);
+ return;
+ }
+
+ // 0-9 adds another 'digit' to the repeat number
+ if(repeat && Character.isDigit(c))
+ {
+ repeatCount *= 10;
+ repeatCount += (c - '0');
+ return;
+ }
+
+ executeAction(INSERT_CHAR,evt.getSource(),
+ String.valueOf(evt.getKeyChar()));
+
+ repeatCount = 0;
+ repeat = false;
+ }
+ }
+ }
+
+ /**
+ * Converts a string to a keystroke. The string should be of the
+ * form modifiers+shortcut where modifiers
+ * is any combination of A for Alt, C for Control, S for Shift
+ * or M for Meta, and shortcut is either a single character,
+ * or a keycode name from the KeyEvent
class, without
+ * the VK_
prefix.
+ * @param keyStroke A string description of the key stroke
+ */
+ public static KeyStroke parseKeyStroke(String keyStroke)
+ {
+ if(keyStroke == null)
+ return null;
+ int modifiers = 0;
+ int index = keyStroke.indexOf('+');
+ if(index != -1)
+ {
+ for(int i = 0; i < index; i++)
+ {
+ switch(Character.toUpperCase(keyStroke
+ .charAt(i)))
+ {
+ case 'A':
+ modifiers |= InputEvent.ALT_MASK;
+ break;
+ case 'C':
+ modifiers |= InputEvent.CTRL_MASK;
+ break;
+ case 'M':
+ modifiers |= InputEvent.META_MASK;
+ break;
+ case 'S':
+ modifiers |= InputEvent.SHIFT_MASK;
+ break;
+ }
+ }
+ }
+ String key = keyStroke.substring(index + 1);
+ if(key.length() == 1)
+ {
+ char ch = Character.toUpperCase(key.charAt(0));
+ if(modifiers == 0)
+ return KeyStroke.getKeyStroke(ch);
+ else
+ return KeyStroke.getKeyStroke(ch,modifiers);
+ }
+ else if(key.length() == 0)
+ {
+ System.err.println("Invalid key stroke: " + keyStroke);
+ return null;
+ }
+ else
+ {
+ int ch;
+
+ try
+ {
+ ch = KeyEvent.class.getField("VK_".concat(key))
+ .getInt(null);
+ }
+ catch(Exception e)
+ {
+ System.err.println("Invalid key stroke: "
+ + keyStroke);
+ return null;
+ }
+
+ return KeyStroke.getKeyStroke(ch,modifiers);
+ }
+ }
+
+ // private members
+ private Hashtable bindings;
+ private Hashtable currentBindings;
+
+ private DefaultInputHandler(DefaultInputHandler copy)
+ {
+ bindings = currentBindings = copy.bindings;
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/XMLTokenMarker.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/XMLTokenMarker.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/XMLTokenMarker.java (revision 0)
@@ -0,0 +1,190 @@
+/*
+ * XMLTokenMarker.java - XML token marker
+ * Copyright (C) 1998, 1999 Slava Pestov
+ * Copyright (C) 2001 Tom Bradford
+ *
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.text.Segment;
+
+/**
+ * XML Token Marker Rewrite
+ *
+ * @author Tom Bradford
+ * @version $Id: XMLTokenMarker.java,v 1.5 2001/07/29 20:45:43 tom Exp $
+ */
+public class XMLTokenMarker extends TokenMarker {
+ public XMLTokenMarker() {
+ }
+
+ public byte markTokensImpl(byte token, Segment line, int lineIndex) {
+ char[] array = line.array;
+ int offset = line.offset;
+ int lastOffset = offset;
+ int length = line.count + offset;
+
+ // Ugly hack to handle multi-line tags
+ boolean sk1 = token == Token.KEYWORD1;
+
+ for ( int i = offset; i < length; i++ ) {
+ int ip1 = i+1;
+ char c = array[i];
+ switch ( token ) {
+ case Token.NULL: // text
+ switch ( c ) {
+ case '<':
+ addToken(i-lastOffset, token);
+ lastOffset = i;
+ if ( SyntaxUtilities.regionMatches(false, line, ip1, "!--") ) {
+ i += 3;
+ token = Token.COMMENT1;
+ }
+ else if ( array[ip1] == '!' ) {
+ i += 1;
+ token = Token.COMMENT2;
+ }
+ else if ( array[ip1] == '?' ) {
+ i += 1;
+ token = Token.KEYWORD3;
+ }
+ else
+ token = Token.KEYWORD1;
+ break;
+
+ case '&':
+ addToken(i - lastOffset, token);
+ lastOffset = i;
+ token = Token.LABEL;
+ break;
+ }
+ break;
+
+ case Token.KEYWORD1: // tag
+ switch ( c ) {
+ case '>':
+ addToken(ip1-lastOffset, token);
+ lastOffset = ip1;
+ token = Token.NULL;
+ sk1 = false;
+ break;
+
+ case ' ':
+ case '\t':
+ addToken(i-lastOffset, token);
+ lastOffset = i;
+ token = Token.KEYWORD2;
+ sk1 = false;
+ break;
+
+ default:
+ if ( sk1 ) {
+ token = Token.KEYWORD2;
+ sk1 = false;
+ }
+ break;
+ }
+ break;
+
+ case Token.KEYWORD2: // attribute
+ switch ( c ) {
+ case '>':
+ addToken(ip1-lastOffset, token);
+ lastOffset = ip1;
+ token = Token.NULL;
+ break;
+
+ case '/':
+ addToken(i-lastOffset, token);
+ lastOffset = i;
+ token = Token.KEYWORD1;
+ break;
+
+ case '=':
+ addToken(i-lastOffset, token);
+ lastOffset = i;
+ token = Token.OPERATOR;
+ }
+ break;
+
+ case Token.OPERATOR: // equal for attribute
+ switch ( c ) {
+ case '\"':
+ case '\'':
+ addToken(i-lastOffset, token);
+ lastOffset = i;
+ if ( c == '\"' )
+ token = Token.LITERAL1;
+ else
+ token = Token.LITERAL2;
+ break;
+ }
+ break;
+
+ case Token.LITERAL1:
+ case Token.LITERAL2: // strings
+ if ( ( token == Token.LITERAL1 && c == '\"' )
+ || ( token == Token.LITERAL2 && c == '\'' ) ) {
+ addToken(ip1-lastOffset, token);
+ lastOffset = ip1;
+ token = Token.KEYWORD1;
+ }
+ break;
+
+ case Token.LABEL: // entity
+ if ( c == ';' ) {
+ addToken(ip1-lastOffset, token);
+ lastOffset = ip1;
+ token = Token.NULL;
+ break;
+ }
+ break;
+
+ case Token.COMMENT1: // Inside a comment
+ if ( SyntaxUtilities.regionMatches(false, line, i, "-->") ) {
+ addToken((i+3)-lastOffset, token);
+ lastOffset = i+3;
+ token = Token.NULL;
+ }
+ break;
+
+ case Token.COMMENT2: // Inside a declaration
+ if ( SyntaxUtilities.regionMatches(false, line, i, ">") ) {
+ addToken(ip1-lastOffset, token);
+ lastOffset = ip1;
+ token = Token.NULL;
+ }
+ break;
+
+ case Token.KEYWORD3: // Inside a processor instruction
+ if ( SyntaxUtilities.regionMatches(false, line, i, "?>") ) {
+ addToken((i+2)-lastOffset, token);
+ lastOffset = i+2;
+ token = Token.NULL;
+ }
+ break;
+
+ default:
+ throw new InternalError("Invalid state: " + token);
+ }
+ }
+
+ switch ( token ) {
+ case Token.LABEL:
+ addToken(length-lastOffset, Token.INVALID);
+ token = Token.NULL;
+ break;
+
+ default:
+ addToken(length-lastOffset, token);
+ break;
+ }
+
+ return token;
+ }
+}
+
+
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaDefaults.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaDefaults.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextAreaDefaults.java (revision 0)
@@ -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.apache.batik.apps.svgbrowser.srcview;
+
+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;
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SourceViewFrame.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SourceViewFrame.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SourceViewFrame.java (revision 0)
@@ -0,0 +1,810 @@
+package org.apache.batik.apps.svgbrowser.srcview;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenuBar;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.batik.apps.svgbrowser.AboutDialog;
+import org.apache.batik.apps.svgbrowser.Application;
+import org.apache.batik.swing.svg.SVGUserAgent;
+import org.apache.batik.util.gui.resource.ActionMap;
+import org.apache.batik.util.gui.resource.ButtonFactory;
+import org.apache.batik.util.gui.resource.MenuFactory;
+import org.apache.batik.util.gui.resource.MissingListenerException;
+import org.apache.batik.util.gui.resource.ResourceManager;
+
+public class SourceViewFrame extends JFrame implements ActionMap {
+
+ public static final String RESOURCES = "org.apache.batik.apps.svgbrowser.srcview.resources.SourceViewFrame";
+
+ // The action names
+ public static final String SAVE_AS_ACTION = "SaveAsAction";
+
+ public static final String CLOSE_ACTION = "CloseAction";
+
+ public static final String UNDO_ACTION = "UndoAction";
+
+ public static final String REDO_ACTION = "RedoAction";
+
+ public static final String CUT_ACTION = "CutAction";
+
+ public static final String COPY_ACTION = "CopyAction";
+
+ public static final String PASTE_ACTION = "PasteAction";
+
+ public static final String DELETE_ACTION = "DeleteAction";
+
+ public static final String SELECT_ALL_ACTION = "SelectAllAction";
+
+ public static final String FIND_ACTION = "FindAction";
+
+ public static final String FIND_CLOSE_ACTION = "FindCloseAction";
+
+ public static final String FIND_AGAIN_ACTION = "FindAgainAction";
+
+ public static final String FIND_PREVIOUS_ACTION = "FindPreviousAction";
+
+ public static final String HIGHLIGHT_ALL_ACTION = "HighlightAllAction";
+
+ public static final String GOTO_LINE_ACTION = "GotoLineAction";
+
+ public static final String TEXT_SIZE_INCREASE_ACTION = "TextSizeIncreaseAction";
+
+ public static final String TEXT_SIZE_DECREASE_ACTION = "TextSizeDecreaseAction";
+
+ public static final String TEXT_SIZE_NORMAL_ACTION = "TextSizeNormalAction";
+
+ public static final String SYNTAX_HIGHLIGHTING_ACTION = "SyntaxHighlightingAction";
+
+ public static final String ABOUT_ACTION = "AboutAction";
+
+ protected static final float[] FONT_SIZES = { 6, 8, 10, 11, 12, 14, 18, 28,
+ 36, 72 };
+
+ /**
+ * The resource bundle
+ */
+ protected static ResourceBundle bundle;
+
+ /**
+ * The resource manager
+ */
+ protected static ResourceManager resources;
+ static {
+ bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
+ resources = new ResourceManager(bundle);
+ }
+
+ /**
+ * The SVG user agent.
+ */
+ protected SVGUserAgent userAgent;
+
+ /**
+ * The current application.
+ */
+ protected Application application;
+
+ /**
+ * The text to be displayed in the source viewer
+ */
+ protected String sourceText;
+
+ /**
+ * The syntax highlighted text area
+ */
+ protected JEditTextArea textArea;
+
+ /**
+ * TokenMarker used by syntax highlighted text area to tokenize text
+ */
+ protected TokenMarker tokenMarker;
+
+ /**
+ * The current save path.
+ */
+ protected File currentSavePath = new File("");
+
+ /**
+ * The find bar.
+ */
+ protected JToolBar findbar;
+
+ /**
+ * Label to display status messages in the find bar
+ */
+ protected FindBarMessage findbarMessage;
+
+ /**
+ * The status bar.
+ */
+ protected StatusBar statusBar;
+
+ /**
+ * The current font size index (into the array FONT_SIZES)
+ */
+ protected int fontSizeIndex;
+
+ /**
+ * The default font size index (into the array FONT_SIZES)
+ */
+ protected final int defaultFontSizeIndex = 4;
+
+ /**
+ * Creates a new source viewer frame.
+ */
+ public SourceViewFrame(Application app, String text, SVGUserAgent agt)
+ throws MissingResourceException {
+ application = app;
+ sourceText = text;
+ userAgent = agt;
+
+ listeners.put(SAVE_AS_ACTION, new SaveAsAction());
+ listeners.put(CLOSE_ACTION, new CloseAction());
+ listeners.put(UNDO_ACTION, new UndoAction());
+ listeners.put(REDO_ACTION, new RedoAction());
+ listeners.put(CUT_ACTION, new CutAction());
+ listeners.put(COPY_ACTION, new CopyAction());
+ listeners.put(PASTE_ACTION, new PasteAction());
+ listeners.put(DELETE_ACTION, new DeleteAction());
+ listeners.put(SELECT_ALL_ACTION, new SelectAllAction());
+ listeners.put(FIND_ACTION, new FindAction());
+ listeners.put(FIND_CLOSE_ACTION, new FindCloseAction());
+ listeners.put(FIND_AGAIN_ACTION, new FindAgainAction());
+ listeners.put(FIND_PREVIOUS_ACTION, new FindPreviousAction());
+ listeners.put(HIGHLIGHT_ALL_ACTION, new HighlightAllAction());
+ listeners.put(GOTO_LINE_ACTION, new GotoLineAction());
+ listeners.put(TEXT_SIZE_INCREASE_ACTION, new TextSizeIncreaseAction());
+ listeners.put(TEXT_SIZE_DECREASE_ACTION, new TextSizeDecreaseAction());
+ listeners.put(TEXT_SIZE_NORMAL_ACTION, new TextSizeNormalAction());
+ listeners.put(SYNTAX_HIGHLIGHTING_ACTION,
+ new SyntaxHighlightingAction());
+ listeners.put(ABOUT_ACTION, new AboutAction());
+
+ // Create the menu
+ MenuFactory mf = new MenuFactory(bundle, this);
+
+ // TODO:Remove this and use specialized version (commented out below)
+ JMenuBar mb = mf.createJMenuBar("MenuBar");
+ // JMenuBar mb = mf.createJMenuBar("MenuBar", application
+ // .getUISpecialization());
+ setJMenuBar(mb);
+
+ // Create the central panel
+ JPanel center = new JPanel(new BorderLayout());
+ getContentPane().add(center, BorderLayout.CENTER);
+
+ // Create syntax-highlighted text area
+ textArea = new JEditTextArea();
+ textArea.setText(sourceText);
+ tokenMarker = new XMLTokenMarker();
+ textArea.setTokenMarker(tokenMarker);
+ fontSizeIndex = defaultFontSizeIndex;
+ TextAreaPainter painter = textArea.getPainter();
+ painter.setFont(new Font("Monospaced", Font.PLAIN,
+ (int) (FONT_SIZES[fontSizeIndex])));
+ textArea.addCaretListener(new CaretListener() {
+ public void caretUpdate(CaretEvent e) {
+ JEditTextArea a = (JEditTextArea) e.getSource();
+ int row = a.getCaretLine() + 1;
+ int col = a.getCaretPosition() - a.getLineStartOffset(row - 1)
+ + 1;
+ statusBar.setPosition(row, col);
+ }
+ });
+ center.add(textArea);
+
+ // Create find bar (initially invisible)
+ ButtonFactory bf = new ButtonFactory(bundle, this);
+ findbar = new JToolBar();
+ findbar.setVisible(false);
+ findbar.setFloatable(false);
+ findbar.add(bf.createJToolbarButton("FindBarClose"));
+ findbar
+ .add(new JLabel(resources.getString("FindBar.label.find")
+ + ": "));
+ final JTextField findText = new JTextField(20);
+ JPanel p = new JPanel();
+ p.setOpaque(false);
+ p.add(findText);
+ p.setMinimumSize(p.getPreferredSize());
+ p.setMaximumSize(p.getPreferredSize());
+ findbar.add(p);
+ final JButton next = bf.createJToolbarButton("FindBarNext");
+ next.setEnabled(false);
+ findbar.add(next);
+ final JButton previous = bf.createJToolbarButton("FindBarPrevious");
+ previous.setEnabled(false);
+ findbar.add(previous);
+ final JToggleButton highlightAll = bf
+ .createJToolbarToggleButton("FindBarHighlightAll");
+ highlightAll.setEnabled(false);
+ findbar.add(highlightAll);
+ JCheckBox matchCase = bf.createJCheckBox("FindBarMatchCase");
+ matchCase.setOpaque(false);
+ findbar.add(matchCase);
+ findbarMessage = new FindBarMessage();
+ findbar.add(findbarMessage);
+ findText.getDocument().addDocumentListener(new DocumentListener() {
+ public void changedUpdate(DocumentEvent e) {
+ update();
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ update();
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ update();
+ }
+
+ private void update() {
+ String str = findText.getText().trim();
+ boolean enabled = !str.equals("");
+ next.setEnabled(enabled);
+ previous.setEnabled(enabled);
+ highlightAll.setEnabled(enabled);
+ if (enabled) {
+ if (!find(str, DIRECTION_DOWN)) {
+ findText.setBackground(new Color(255, 128, 128));
+ }
+ } else {
+ findbarMessage.setMessage(FindBarMessage.NONE);
+ findText.setBackground(UIManager
+ .getColor("TextField.background"));
+ }
+ }
+ });
+ center.add(findbar, BorderLayout.SOUTH);
+
+ // Create status bar
+ statusBar = new StatusBar();
+ getContentPane().add(statusBar, BorderLayout.SOUTH);
+ }
+
+ protected static ImageIcon notFoundIcon, continueFromTopIcon;
+
+ protected static String notFoundStr, continueFromTopStr;
+
+ static {
+ notFoundStr = resources.getString("FindBarNotFound.text");
+ notFoundIcon = new ImageIcon(SourceViewFrame.class
+ .getResource(resources.getString("FindBarNotFound.icon")));
+ continueFromTopStr = resources.getString("FindBarContinueFromTop.text");
+ continueFromTopIcon = new ImageIcon(SourceViewFrame.class
+ .getResource(resources.getString("FindBarContinueFromTop.icon")));
+ }
+
+ protected class FindBarMessage extends JLabel {
+ public static final int NONE = 0, NOT_FOUND = 1, CONTINUE_FROM_TOP = 2;
+
+ public void setMessage(int type) {
+ switch (type) {
+ case NOT_FOUND:
+ setIcon(notFoundIcon);
+ setText(notFoundStr);
+ break;
+ case CONTINUE_FROM_TOP:
+ setIcon(continueFromTopIcon);
+ setText(continueFromTopStr);
+ break;
+ case NONE:
+ default:
+ setIcon(null);
+ setText("");
+ break;
+ }
+ }
+ }
+
+ /**
+ * Save the document as a different file
+ */
+ public class SaveAsAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ JFileChooser fileChooser;
+ fileChooser = new JFileChooser(
+ currentSavePath.isAbsolute() ? currentSavePath
+ : currentSavePath.getAbsoluteFile());
+ fileChooser.setDialogTitle(resources.getString("SaveAs.title"));
+ fileChooser.setFileHidingEnabled(false);
+ fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fileChooser.addChoosableFileFilter(new FileFilter() {
+ public boolean accept(File f) {
+ if (f == null) {
+ return false;
+ }
+ if (f.isDirectory()) {
+ return true;
+ }
+ String fileName = f.getName().toLowerCase();
+ return fileName.endsWith(".svg");
+ }
+
+ public String getDescription() {
+ return "SVG Document";
+ }
+ });
+
+ int choice = fileChooser.showSaveDialog(SourceViewFrame.this);
+ if (choice != JFileChooser.APPROVE_OPTION)
+ return;
+
+ final File f = fileChooser.getSelectedFile();
+
+ statusBar.setMessage(resources.getString("Message.saveAs"));
+ currentSavePath = f;
+ OutputStreamWriter w = null;
+ try {
+ OutputStream tos = null;
+ tos = new FileOutputStream(f);
+ tos = new BufferedOutputStream(tos);
+ w = new OutputStreamWriter(tos, "utf-8");
+ } catch (Exception ex) {
+ userAgent.displayError(ex);
+ return;
+ }
+
+ final OutputStreamWriter writer = w;
+
+ final Runnable doneRun = new Runnable() {
+ public void run() {
+ String doneStr = resources.getString("Message.done");
+ statusBar.setMessage(doneStr);
+ }
+ };
+
+ try {
+ writer.write(textArea.getText());
+ writer.close();
+
+ SwingUtilities.invokeLater(doneRun);
+ } catch (Exception ex) {
+ userAgent.displayError(ex);
+ }
+ }
+ }
+
+ /**
+ * Closes the source viewer
+ */
+ public class CloseAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ dispose();
+ }
+ }
+
+ /**
+ * Undo last action
+ */
+ public class UndoAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+
+ }
+ }
+
+ /**
+ * Redo last undone action
+ */
+ public class RedoAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+
+ }
+ }
+
+ /**
+ * Cut selected text
+ */
+ public class CutAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ textArea.cut();
+ }
+ }
+
+ /**
+ * Copy selected text
+ */
+ public class CopyAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ textArea.copy();
+ }
+ }
+
+ /**
+ * Paste copied text
+ */
+ public class PasteAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ textArea.paste();
+ }
+ }
+
+ /**
+ * Delete selected text
+ */
+ public class DeleteAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ }
+ }
+
+ /**
+ * Select all text
+ */
+ public class SelectAllAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ textArea.selectAll();
+ }
+ }
+
+ /**
+ * Displays the find bar if not already visible
+ */
+ public class FindAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (!findbar.isVisible()) {
+ findbar.setVisible(true);
+ }
+ }
+ }
+
+ /**
+ * Closes the find bar if visible
+ */
+ public class FindCloseAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (findbar.isVisible()) {
+ findbar.setVisible(false);
+ }
+ }
+ }
+
+ /**
+ * Find the string in the text specified using the Find action
+ */
+ public class FindAgainAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (!findbar.isVisible()) {
+ findbar.setVisible(true);
+ } else {
+ find(lastFindStr, DIRECTION_DOWN);
+ }
+ }
+ }
+
+ /**
+ * Find the string in the text specified using the Find action
+ */
+ public class FindPreviousAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (!findbar.isVisible()) {
+ findbar.setVisible(true);
+ } else {
+ find(lastFindStr, DIRECTION_UP);
+ }
+ }
+ }
+
+ /**
+ * Highlight all occurrences of the search phrase in the text
+ */
+ public class HighlightAllAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ }
+ }
+
+ private String lastFindStr;
+
+ private int lastOffset = -1;
+
+ private static final int DIRECTION_UP = -1, DIRECTION_DOWN = 1;
+
+ /**
+ * Help function to search for a phrase in the text
+ *
+ * @param phrase
+ * search string
+ * @param direction
+ * whether to search downward or upward
+ * @return true is search string found, false otherwise
+ */
+ private boolean find(String phrase, int direction) {
+ boolean found = false;
+ boolean wrap = false;
+
+ String text = textArea.getText();
+ int offset = text.indexOf(phrase, lastOffset + 1);
+ if (offset < 0 && lastOffset >= 0) {
+ lastOffset = -1;
+ wrap = true;
+ offset = text.indexOf(phrase, 0);
+ }
+ lastOffset = offset;
+ found = lastOffset >= 0;
+
+ if (found) {
+ textArea.setSelectionStart(lastOffset);
+ textArea.setSelectionEnd(lastOffset + phrase.length());
+ }
+
+ if (!found) {
+ findbarMessage.setMessage(FindBarMessage.NOT_FOUND);
+ } else if (wrap) {
+ findbarMessage.setMessage(FindBarMessage.CONTINUE_FROM_TOP);
+ } else {
+ findbarMessage.setMessage(FindBarMessage.NONE);
+ }
+ return found;
+ }
+
+ /**
+ * Goto a particular line
+ */
+ public class GotoLineAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ GotoLineDialog gld = new GotoLineDialog(
+ SourceViewFrame.this, textArea.getLineCount());
+ gld.setVisible(true);
+ int n = gld.getLineNumber();
+ if (n > 0) {
+ gotoLine(n);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Helper function to adjust caret position and scroll viewport
+ *
+ * @param n
+ * line number of the new caret position
+ */
+ private void gotoLine(int n) {
+ textArea.setCaretPosition(textArea.getLineStartOffset(n - 1));
+ textArea.scrollToCaret();
+ }
+
+ /**
+ * Increase text size
+ */
+ public class TextSizeIncreaseAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (fontSizeIndex < FONT_SIZES.length - 1) {
+ TextAreaPainter painter = textArea.getPainter();
+ painter.setFont(painter.getFont().deriveFont(
+ FONT_SIZES[++fontSizeIndex]));
+ }
+ }
+ }
+
+ /**
+ * Decrease text size
+ */
+ public class TextSizeDecreaseAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ if (fontSizeIndex > 0) {
+ TextAreaPainter painter = textArea.getPainter();
+ painter.setFont(painter.getFont().deriveFont(
+ FONT_SIZES[--fontSizeIndex]));
+ }
+ }
+ }
+
+ /**
+ * Set text size to default value
+ */
+ public class TextSizeNormalAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ TextAreaPainter painter = textArea.getPainter();
+ painter.setFont(painter.getFont().deriveFont(
+ FONT_SIZES[fontSizeIndex = defaultFontSizeIndex]));
+ }
+ }
+
+ /**
+ * Toggle syntax highlighting
+ */
+ public class SyntaxHighlightingAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ textArea
+ .setTokenMarker(textArea.getTokenMarker() == null ? tokenMarker
+ : null);
+ textArea.repaint();
+ }
+ }
+
+ /**
+ * To show the about dialog
+ */
+ public class AboutAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ AboutDialog dlg = new AboutDialog(SourceViewFrame.this);
+ // Work around pack() bug on some platforms
+ dlg.setSize(dlg.getPreferredSize());
+ dlg.setLocationRelativeTo(SourceViewFrame.this);
+ dlg.setVisible(true);
+ dlg.toFront();
+ }
+ }
+
+ /**
+ * The map that contains the action listeners
+ */
+ protected Map listeners = new HashMap();
+
+ /**
+ * Returns the action associated with the given string or null on error
+ *
+ * @param key
+ * the key mapped with the action to get
+ * @throws MissingListenerException
+ * if the action is not found
+ */
+ public Action getAction(String key) throws MissingListenerException {
+ Action result = (Action) listeners.get(key);
+ if (result == null) {
+ throw new MissingListenerException("Can't find action.", RESOURCES,
+ key);
+ }
+ return result;
+ }
+
+ private class GotoLineDialog extends JDialog implements ActionListener,
+ DocumentListener {
+
+ private JTextField textField;
+
+ private JButton okButton, cancelButton;
+
+ private JLabel errorMessage;
+
+ private int totalLines;
+
+ private int lineNumber = -1;
+
+ public GotoLineDialog(JFrame owner, int totalLines) {
+ super(owner, resources.getString("GotoLine.title"), true);
+ this.totalLines = totalLines;
+
+ JPanel content = new JPanel(new BorderLayout());
+ content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ content.setPreferredSize(new Dimension(300, 120));
+ getContentPane().add(content);
+
+ Box input = new Box(BoxLayout.Y_AXIS);
+ content.add(input, BorderLayout.NORTH);
+
+ String str = resources.getString("GotoLine.label.enterLineNumber");
+ str = str.replace("{0}", "" + totalLines);
+ JLabel label = new JLabel(str);
+ input.add(label);
+
+ textField = new JTextField(120);
+ textField.getDocument().addDocumentListener(this);
+ input.add(textField);
+
+ errorMessage = new JLabel();
+ input.add(errorMessage);
+
+ Box buttons = new Box(BoxLayout.X_AXIS);
+ content.add(buttons, BorderLayout.SOUTH);
+
+ buttons.add(Box.createHorizontalGlue());
+
+ okButton = new JButton(resources.getString("GotoLine.button.ok"));
+ okButton.setEnabled(false);
+ okButton.addActionListener(this);
+ buttons.add(okButton);
+
+ buttons.add(Box.createHorizontalStrut(5));
+
+ cancelButton = new JButton(resources
+ .getString("GotoLine.button.cancel"));
+ cancelButton.addActionListener(this);
+ buttons.add(cancelButton);
+
+ pack();
+ setLocationRelativeTo(getParent());
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == okButton) {
+ lineNumber = Integer.parseInt(textField.getText());
+ dispose();
+ } else if (e.getSource() == cancelButton) {
+ dispose();
+ }
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ check();
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ check();
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ check();
+ }
+
+ private void check() {
+ boolean disabled = false;
+ String error = "";
+ try {
+ String text = textField.getText();
+ if (text == null || text.trim().equals("")) {
+ disabled = true;
+ } else {
+ int n = Integer.parseInt(text);
+ if (n < 1 || n > totalLines) {
+ disabled = true;
+ error = "Number out of range";
+ }
+ }
+ } catch (NumberFormatException e) {
+ disabled = true;
+ error = "Not a number";
+ }
+ okButton.setEnabled(!disabled);
+ errorMessage.setText(error);
+ }
+ }
+
+ public static void main(String[] args) {
+ // try {
+ // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ // } catch (Exception e) {
+ // e.printStackTrace();
+ // }
+
+ SourceViewFrame svf = new SourceViewFrame(null, "abcdef", null);
+ svf.setSize(700, 500);
+ svf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ svf.setVisible(true);
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxStyle.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxStyle.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxStyle.java (revision 0)
@@ -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.apache.batik.apps.svgbrowser.srcview;
+
+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;
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/StatusBar.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/StatusBar.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/StatusBar.java (revision 0)
@@ -0,0 +1,147 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+package org.apache.batik.apps.svgbrowser.srcview;
+
+import java.awt.Dimension;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.BevelBorder;
+
+import org.apache.batik.util.gui.resource.ResourceManager;
+
+/**
+ * This class represents a viewer status bar.
+ *
+ */
+public class StatusBar extends JPanel {
+
+ /**
+ * The gui resources file name
+ */
+ protected static final String RESOURCES =
+ "org.apache.batik.apps.svgbrowser.srcview.resources.StatusBarMessages";
+
+ /**
+ * The resource bundle
+ */
+ protected static ResourceBundle bundle;
+
+ /**
+ * The resource manager
+ */
+ protected static ResourceManager rManager;
+
+ /**
+ * The string to be used for "Row"
+ */
+ private static String rowStr;
+
+ /**
+ * The string to be used for "Column"
+ */
+ private static String colStr;
+
+ static {
+ bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
+ rManager = new ResourceManager(bundle);
+ rowStr = rManager.getString("Position.row");
+ colStr = rManager.getString("Position.column");
+ }
+
+ /**
+ * The row value
+ */
+ protected int row;
+
+ /**
+ * The column value
+ */
+ protected int column;
+
+ /**
+ * The position label.
+ */
+ protected JLabel position;
+
+ /**
+ * The message label
+ */
+ protected JLabel message;
+
+ /**
+ * Creates a new status bar.
+ */
+ public StatusBar() throws MissingResourceException {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+
+ position = new JLabel();
+ position.setPreferredSize(new Dimension(110, 16));
+ add(position);
+
+ message = new JLabel();
+ add(message);
+
+ add(Box.createHorizontalGlue());
+ }
+
+ /**
+ * Sets the row.
+ */
+ public void setRow(int row) {
+ this.row = row;
+ updatePosition();
+ }
+
+ /**
+ * Sets the column.
+ */
+ public void setColumn(int column) {
+ this.column = column;
+ updatePosition();
+ }
+
+ /**
+ * Sets the row and column together
+ */
+ public void setPosition(int row, int column) {
+ this.row = row;
+ this.column = column;
+ updatePosition();
+ }
+
+ private void updatePosition() {
+ position.setText(rowStr + " " + row + ", " + colStr + " " + column);
+ }
+
+ /**
+ * Sets the message
+ * @param s the message
+ */
+ public void setMessage(String s) {
+ message.setText(s);
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/Token.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/Token.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/Token.java (revision 0)
@@ -0,0 +1,149 @@
+/*
+ * Token.java - Generic token
+ * Copyright (C) 1998, 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.apache.batik.apps.svgbrowser.srcview;
+
+/**
+ * A linked list of tokens. Each token has three fields - a token
+ * identifier, which is a byte value that can be looked up in the
+ * array returned by SyntaxDocument.getColors()
+ * to get a color value, a length value which is the length of the
+ * token in the text, and a pointer to the next token in the list.
+ *
+ * @author Slava Pestov
+ * @version $Id: Token.java,v 1.12 1999/12/13 03:40:30 sp Exp $
+ */
+public class Token
+{
+ /**
+ * Normal text token id. This should be used to mark
+ * normal text.
+ */
+ public static final byte NULL = 0;
+
+ /**
+ * Comment 1 token id. This can be used to mark a comment.
+ */
+ public static final byte COMMENT1 = 1;
+
+ /**
+ * Comment 2 token id. This can be used to mark a comment.
+ */
+ public static final byte COMMENT2 = 2;
+
+
+ /**
+ * Literal 1 token id. This can be used to mark a string
+ * literal (eg, C mode uses this to mark "..." literals)
+ */
+ public static final byte LITERAL1 = 3;
+
+ /**
+ * Literal 2 token id. This can be used to mark an object
+ * literal (eg, Java mode uses this to mark true, false, etc)
+ */
+ public static final byte LITERAL2 = 4;
+
+ /**
+ * Label token id. This can be used to mark labels
+ * (eg, C mode uses this to mark ...: sequences)
+ */
+ public static final byte LABEL = 5;
+
+ /**
+ * Keyword 1 token id. This can be used to mark a
+ * keyword. This should be used for general language
+ * constructs.
+ */
+ public static final byte KEYWORD1 = 6;
+
+ /**
+ * Keyword 2 token id. This can be used to mark a
+ * keyword. This should be used for preprocessor
+ * commands, or variables.
+ */
+ public static final byte KEYWORD2 = 7;
+
+ /**
+ * Keyword 3 token id. This can be used to mark a
+ * keyword. This should be used for data types.
+ */
+ public static final byte KEYWORD3 = 8;
+
+ /**
+ * Operator token id. This can be used to mark an
+ * operator. (eg, SQL mode marks +, -, etc with this
+ * token type)
+ */
+ public static final byte OPERATOR = 9;
+
+ /**
+ * Invalid token id. This can be used to mark invalid
+ * or incomplete tokens, so the user can easily spot
+ * syntax errors.
+ */
+ public static final byte INVALID = 10;
+
+ /**
+ * The total number of defined token ids.
+ */
+ public static final byte ID_COUNT = 11;
+
+ /**
+ * The first id that can be used for internal state
+ * in a token marker.
+ */
+ public static final byte INTERNAL_FIRST = 100;
+
+ /**
+ * The last id that can be used for internal state
+ * in a token marker.
+ */
+ public static final byte INTERNAL_LAST = 126;
+
+ /**
+ * The token type, that along with a length of 0
+ * marks the end of the token list.
+ */
+ public static final byte END = 127;
+
+ /**
+ * The length of this token.
+ */
+ public int length;
+
+ /**
+ * The id of this token.
+ */
+ public byte id;
+
+ /**
+ * The next token in the linked list.
+ */
+ public Token next;
+
+ /**
+ * Creates a new token.
+ * @param length The length of the token
+ * @param id The id of the token
+ */
+ public Token(int length, byte id)
+ {
+ this.length = length;
+ this.id = id;
+ }
+
+ /**
+ * Returns a string representation of this token.
+ */
+ public String toString()
+ {
+ return "[id=" + id + ",length=" + length + "]";
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextUtilities.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextUtilities.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TextUtilities.java (revision 0)
@@ -0,0 +1,183 @@
+/*
+ * TextUtilities.java - Utility functions used by the text area classes
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.text.*;
+
+/**
+ * Class with several utility functions used by the text area component.
+ * @author Slava Pestov
+ * @version $Id: TextUtilities.java,v 1.4 1999/12/13 03:40:30 sp Exp $
+ */
+public class TextUtilities
+{
+ /**
+ * Returns the offset of the bracket matching the one at the
+ * specified offset of the document, or -1 if the bracket is
+ * unmatched (or if the character is not a bracket).
+ * @param doc The document
+ * @param offset The offset
+ * @exception BadLocationException If an out-of-bounds access
+ * was attempted on the document text
+ */
+ public static int findMatchingBracket(Document doc, int offset)
+ throws BadLocationException
+ {
+ if(doc.getLength() == 0)
+ return -1;
+ char c = doc.getText(offset,1).charAt(0);
+ char cprime; // c` - corresponding character
+ boolean direction; // true = back, false = forward
+
+ switch(c)
+ {
+ case '(': cprime = ')'; direction = false; break;
+ case ')': cprime = '('; direction = true; break;
+ case '[': cprime = ']'; direction = false; break;
+ case ']': cprime = '['; direction = true; break;
+ case '{': cprime = '}'; direction = false; break;
+ case '}': cprime = '{'; direction = true; break;
+ default: return -1;
+ }
+
+ int count;
+
+ // How to merge these two cases is left as an exercise
+ // for the reader.
+
+ // Go back or forward
+ if(direction)
+ {
+ // Count is 1 initially because we have already
+ // `found' one closing bracket
+ count = 1;
+
+ // Get text[0,offset-1];
+ String text = doc.getText(0,offset);
+
+ // Scan backwards
+ for(int i = offset - 1; i >= 0; i--)
+ {
+ // If text[i] == c, we have found another
+ // closing bracket, therefore we will need
+ // two opening brackets to complete the
+ // match.
+ char x = text.charAt(i);
+ if(x == c)
+ count++;
+
+ // If text[i] == cprime, we have found a
+ // opening bracket, so we return i if
+ // --count == 0
+ else if(x == cprime)
+ {
+ if(--count == 0)
+ return i;
+ }
+ }
+ }
+ else
+ {
+ // Count is 1 initially because we have already
+ // `found' one opening bracket
+ count = 1;
+
+ // So we don't have to + 1 in every loop
+ offset++;
+
+ // Number of characters to check
+ int len = doc.getLength() - offset;
+
+ // Get text[offset+1,len];
+ String text = doc.getText(offset,len);
+
+ // Scan forwards
+ for(int i = 0; i < len; i++)
+ {
+ // If text[i] == c, we have found another
+ // opening bracket, therefore we will need
+ // two closing brackets to complete the
+ // match.
+ char x = text.charAt(i);
+
+ if(x == c)
+ count++;
+
+ // If text[i] == cprime, we have found an
+ // closing bracket, so we return i if
+ // --count == 0
+ else if(x == cprime)
+ {
+ if(--count == 0)
+ return i + offset;
+ }
+ }
+ }
+
+ // Nothing found
+ return -1;
+ }
+
+ /**
+ * Locates the start of the word at the specified position.
+ * @param line The text
+ * @param pos The position
+ */
+ public static int findWordStart(String line, int pos, String noWordSep)
+ {
+ char ch = line.charAt(pos - 1);
+
+ if(noWordSep == null)
+ noWordSep = "";
+ boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
+ && noWordSep.indexOf(ch) == -1);
+
+ int wordStart = 0;
+ for(int i = pos - 1; i >= 0; i--)
+ {
+ ch = line.charAt(i);
+ if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
+ noWordSep.indexOf(ch) == -1))
+ {
+ wordStart = i + 1;
+ break;
+ }
+ }
+
+ return wordStart;
+ }
+
+ /**
+ * Locates the end of the word at the specified position.
+ * @param line The text
+ * @param pos The position
+ */
+ public static int findWordEnd(String line, int pos, String noWordSep)
+ {
+ char ch = line.charAt(pos);
+
+ if(noWordSep == null)
+ noWordSep = "";
+ boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
+ && noWordSep.indexOf(ch) == -1);
+
+ int wordEnd = line.length();
+ for(int i = pos; i < line.length(); i++)
+ {
+ ch = line.charAt(i);
+ if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
+ noWordSep.indexOf(ch) == -1))
+ {
+ wordEnd = i;
+ break;
+ }
+ }
+ return wordEnd;
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxUtilities.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxUtilities.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxUtilities.java (revision 0)
@@ -0,0 +1,157 @@
+/*
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+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 Segment
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 Segment
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
+ * setStyles()
method of SyntaxDocument
+ * 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.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() {}
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TokenMarker.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TokenMarker.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/TokenMarker.java (revision 0)
@@ -0,0 +1,344 @@
+/*
+ * TokenMarker.java - Generic token marker
+ * Copyright (C) 1998, 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.text.Segment;
+import java.util.*;
+
+/**
+ * A token marker that splits lines of text into tokens. Each token carries
+ * a length field and an indentification tag that can be mapped to a color
+ * for painting that token.
+ *
+ * For performance reasons, the linked list of tokens is reused after each
+ * line is tokenized. Therefore, the return value of markTokens
+ * should only be used for immediate painting. Notably, it cannot be
+ * cached.
+ *
+ * @author Slava Pestov
+ * @version $Id: TokenMarker.java,v 1.32 1999/12/13 03:40:30 sp Exp $
+ *
+ * @see org.gjt.sp.jedit.syntax.Token
+ */
+public abstract class TokenMarker
+{
+ /**
+ * A wrapper for the lower-level markTokensImpl
method
+ * that is called to split a line up into tokens.
+ * @param line The line
+ * @param lineIndex The line number
+ */
+ public Token markTokens(Segment line, int lineIndex)
+ {
+ if(lineIndex >= length)
+ {
+ throw new IllegalArgumentException("Tokenizing invalid line: "
+ + lineIndex);
+ }
+
+ lastToken = null;
+
+ LineInfo info = lineInfo[lineIndex];
+ LineInfo prev;
+ if(lineIndex == 0)
+ prev = null;
+ else
+ prev = lineInfo[lineIndex - 1];
+
+ byte oldToken = info.token;
+ byte token = markTokensImpl(prev == null ?
+ Token.NULL : prev.token,line,lineIndex);
+
+ info.token = token;
+
+ /*
+ * This is a foul hack. It stops nextLineRequested
+ * from being cleared if the same line is marked twice.
+ *
+ * Why is this necessary? It's all JEditTextArea's fault.
+ * When something is inserted into the text, firing a
+ * document event, the insertUpdate() method shifts the
+ * caret (if necessary) by the amount inserted.
+ *
+ * All caret movement is handled by the select() method,
+ * which eventually pipes the new position to scrollTo()
+ * and calls repaint().
+ *
+ * Note that at this point in time, the new line hasn't
+ * yet been painted; the caret is moved first.
+ *
+ * scrollTo() calls offsetToX(), which tokenizes the line
+ * unless it is being called on the last line painted
+ * (in which case it uses the text area's painter cached
+ * token list). What scrollTo() does next is irrelevant.
+ *
+ * After scrollTo() has done it's job, repaint() is
+ * called, and eventually we end up in paintLine(), whose
+ * job is to paint the changed line. It, too, calls
+ * markTokens().
+ *
+ * The problem was that if the line started a multiline
+ * token, the first markTokens() (done in offsetToX())
+ * would set nextLineRequested (because the line end
+ * token had changed) but the second would clear it
+ * (because the line was the same that time) and therefore
+ * paintLine() would never know that it needed to repaint
+ * subsequent lines.
+ *
+ * This bug took me ages to track down, that's why I wrote
+ * all the relevant info down so that others wouldn't
+ * duplicate it.
+ */
+ if(!(lastLine == lineIndex && nextLineRequested))
+ nextLineRequested = (oldToken != token);
+
+ lastLine = lineIndex;
+
+ addToken(0,Token.END);
+
+ return firstToken;
+ }
+
+ /**
+ * An abstract method that splits a line up into tokens. It
+ * should parse the line, and call addToken()
to
+ * add syntax tokens to the token list. Then, it should return
+ * the initial token type for the next line.
+ *
+ * For example if the current line contains the start of a
+ * multiline comment that doesn't end on that line, this method
+ * should return the comment token type so that it continues on
+ * the next line.
+ *
+ * @param token The initial token type for this line
+ * @param line The line to be tokenized
+ * @param lineIndex The index of the line in the document,
+ * starting at 0
+ * @return The initial token type for the next line
+ */
+ protected abstract byte markTokensImpl(byte token, Segment line,
+ int lineIndex);
+
+ /**
+ * Returns if the token marker supports tokens that span multiple
+ * lines. If this is true, the object using this token marker is
+ * required to pass all lines in the document to the
+ * markTokens()
method (in turn).
+ *
+ * The default implementation returns true; it should be overridden
+ * to return false on simpler token markers for increased speed.
+ */
+ public boolean supportsMultilineTokens()
+ {
+ return true;
+ }
+
+ /**
+ * Informs the token marker that lines have been inserted into
+ * the document. This inserts a gap in the lineInfo
+ * array.
+ * @param index The first line number
+ * @param lines The number of lines
+ */
+ public void insertLines(int index, int lines)
+ {
+ if(lines <= 0)
+ return;
+ length += lines;
+ ensureCapacity(length);
+ int len = index + lines;
+ System.arraycopy(lineInfo,index,lineInfo,len,
+ lineInfo.length - len);
+
+ for(int i = index + lines - 1; i >= index; i--)
+ {
+ lineInfo[i] = new LineInfo();
+ }
+ }
+
+ /**
+ * Informs the token marker that line have been deleted from
+ * the document. This removes the lines in question from the
+ * lineInfo
array.
+ * @param index The first line number
+ * @param lines The number of lines
+ */
+ public void deleteLines(int index, int lines)
+ {
+ if (lines <= 0)
+ return;
+ int len = index + lines;
+ length -= lines;
+ System.arraycopy(lineInfo,len,lineInfo,
+ index,lineInfo.length - len);
+ }
+
+ /**
+ * Returns the number of lines in this token marker.
+ */
+ public int getLineCount()
+ {
+ return length;
+ }
+
+ /**
+ * Returns true if the next line should be repainted. This
+ * will return true after a line has been tokenized that starts
+ * a multiline token that continues onto the next line.
+ */
+ public boolean isNextLineRequested()
+ {
+ return nextLineRequested;
+ }
+
+ // protected members
+
+ /**
+ * The first token in the list. This should be used as the return
+ * value from markTokens()
.
+ */
+ protected Token firstToken;
+
+ /**
+ * The last token in the list. New tokens are added here.
+ * This should be set to null before a new line is to be tokenized.
+ */
+ protected Token lastToken;
+
+ /**
+ * An array for storing information about lines. It is enlarged and
+ * shrunk automatically by the insertLines()
and
+ * deleteLines()
methods.
+ */
+ protected LineInfo[] lineInfo;
+
+ /**
+ * The number of lines in the model being tokenized. This can be
+ * less than the length of the lineInfo
array.
+ */
+ protected int length;
+
+ /**
+ * The last tokenized line.
+ */
+ protected int lastLine;
+
+ /**
+ * True if the next line should be painted.
+ */
+ protected boolean nextLineRequested;
+
+ /**
+ * Creates a new TokenMarker
. This DOES NOT create
+ * a lineInfo array; an initial call to insertLines()
+ * does that.
+ */
+ protected TokenMarker()
+ {
+ lastLine = -1;
+ }
+
+ /**
+ * Ensures that the lineInfo
array can contain the
+ * specified index. This enlarges it if necessary. No action is
+ * taken if the array is large enough already.
+ *
+ * It should be unnecessary to call this under normal
+ * circumstances; insertLine()
should take care of
+ * enlarging the line info array automatically.
+ *
+ * @param index The array index
+ */
+ protected void ensureCapacity(int index)
+ {
+ if(lineInfo == null)
+ lineInfo = new LineInfo[index + 1];
+ else if(lineInfo.length <= index)
+ {
+ LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2];
+ System.arraycopy(lineInfo,0,lineInfoN,0,
+ lineInfo.length);
+ lineInfo = lineInfoN;
+ }
+ }
+
+ /**
+ * Adds a token to the token list.
+ * @param length The length of the token
+ * @param id The id of the token
+ */
+ protected void addToken(int length, byte id)
+ {
+ if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST)
+ throw new InternalError("Invalid id: " + id);
+
+ if(length == 0 && id != Token.END)
+ return;
+
+ if(firstToken == null)
+ {
+ firstToken = new Token(length,id);
+ lastToken = firstToken;
+ }
+ else if(lastToken == null)
+ {
+ lastToken = firstToken;
+ firstToken.length = length;
+ firstToken.id = id;
+ }
+ else if(lastToken.next == null)
+ {
+ lastToken.next = new Token(length,id);
+ lastToken = lastToken.next;
+ }
+ else
+ {
+ lastToken = lastToken.next;
+ lastToken.length = length;
+ lastToken.id = id;
+ }
+ }
+
+ /**
+ * Inner class for storing information about tokenized lines.
+ */
+ public class LineInfo
+ {
+ /**
+ * Creates a new LineInfo object with token = Token.NULL
+ * and obj = null.
+ */
+ public LineInfo()
+ {
+ }
+
+ /**
+ * Creates a new LineInfo object with the specified
+ * parameters.
+ */
+ public LineInfo(byte token, Object obj)
+ {
+ this.token = token;
+ this.obj = obj;
+ }
+
+ /**
+ * The id of the last token of the line.
+ */
+ public byte token;
+
+ /**
+ * This is for use by the token marker implementations
+ * themselves. It can be used to store anything that
+ * is an object and that needs to exist on a per-line
+ * basis.
+ */
+ public Object obj;
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/KeywordMap.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/KeywordMap.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/KeywordMap.java (revision 0)
@@ -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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.text.Segment;
+
+/**
+ * A KeywordMap
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.
+ *
+ * This class is used by CTokenMarker
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 KeywordMap
.
+ * @param ignoreCase True if keys are case insensitive
+ */
+ public KeywordMap(boolean ignoreCase)
+ {
+ this(ignoreCase, 52);
+ this.ignoreCase = ignoreCase;
+ }
+
+ /**
+ * Creates a new KeywordMap
.
+ * @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;
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxDocument.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxDocument.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/SyntaxDocument.java (revision 0)
@@ -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.apache.batik.apps.svgbrowser.srcview;
+
+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);
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/JEditTextArea.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/JEditTextArea.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/JEditTextArea.java (revision 0)
@@ -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.apache.batik.apps.svgbrowser.srcview;
+
+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:
+ *
+ * + * To use it in your app, treat it like any other component, for example: + *
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" + * + "}");+ * + * @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 + *
getDocument().getTokenMarker()
.
+ */
+ public final TokenMarker getTokenMarker()
+ {
+ return document.getTokenMarker();
+ }
+
+ /**
+ * Sets the document's token marker. Equivalent to caling
+ * getDocument().setTokenMarker()
.
+ * @param tokenMarker The token marker
+ */
+ public final void setTokenMarker(TokenMarker tokenMarker)
+ {
+ document.setTokenMarker(tokenMarker);
+ }
+
+ /**
+ * Returns the length of the document. Equivalent to calling
+ * getDocument().getLength()
.
+ */
+ 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 < end, and end
+ * if end > 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 setSelectedText()
, 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();
+ }
+}
Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/InputHandler.java
===================================================================
--- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/InputHandler.java (revision 0)
+++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/srcview/InputHandler.java (revision 0)
@@ -0,0 +1,1070 @@
+/*
+ * InputHandler.java - Manages key bindings and executes actions
+ * 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.apache.batik.apps.svgbrowser.srcview;
+
+import javax.swing.text.*;
+import javax.swing.JPopupMenu;
+import java.awt.event.*;
+import java.awt.Component;
+import java.util.*;
+
+/**
+ * An input handler converts the user's key strokes into concrete actions.
+ * It also takes care of macro recording and action repetition.
+ *
+ * This class provides all the necessary support code for an input
+ * handler, but doesn't actually do any key binding logic. It is up
+ * to the implementations of this class to do so.
+ *
+ * @author Slava Pestov
+ * @version $Id: InputHandler.java,v 1.14 1999/12/13 03:40:30 sp Exp $
+ * @see org.gjt.sp.jedit.textarea.DefaultInputHandler
+ */
+public abstract class InputHandler extends KeyAdapter
+{
+ /**
+ * If this client property is set to Boolean.TRUE on the text area,
+ * the home/end keys will support 'smart' BRIEF-like behaviour
+ * (one press = start/end of line, two presses = start/end of
+ * viewscreen, three presses = start/end of document). By default,
+ * this property is not set.
+ */
+ public static final String SMART_HOME_END_PROPERTY = "InputHandler.homeEnd";
+
+ public static final ActionListener BACKSPACE = new backspace();
+ public static final ActionListener BACKSPACE_WORD = new backspace_word();
+ public static final ActionListener DELETE = new delete();
+ public static final ActionListener DELETE_WORD = new delete_word();
+ public static final ActionListener END = new end(false);
+ public static final ActionListener DOCUMENT_END = new document_end(false);
+ public static final ActionListener SELECT_END = new end(true);
+ public static final ActionListener SELECT_DOC_END = new document_end(true);
+ public static final ActionListener INSERT_BREAK = new insert_break();
+ public static final ActionListener INSERT_TAB = new insert_tab();
+ public static final ActionListener HOME = new home(false);
+ public static final ActionListener DOCUMENT_HOME = new document_home(false);
+ public static final ActionListener SELECT_HOME = new home(true);
+ public static final ActionListener SELECT_DOC_HOME = new document_home(true);
+ public static final ActionListener NEXT_CHAR = new next_char(false);
+ public static final ActionListener NEXT_LINE = new next_line(false);
+ public static final ActionListener NEXT_PAGE = new next_page(false);
+ public static final ActionListener NEXT_WORD = new next_word(false);
+ public static final ActionListener SELECT_NEXT_CHAR = new next_char(true);
+ public static final ActionListener SELECT_NEXT_LINE = new next_line(true);
+ public static final ActionListener SELECT_NEXT_PAGE = new next_page(true);
+ public static final ActionListener SELECT_NEXT_WORD = new next_word(true);
+ public static final ActionListener OVERWRITE = new overwrite();
+ public static final ActionListener PREV_CHAR = new prev_char(false);
+ public static final ActionListener PREV_LINE = new prev_line(false);
+ public static final ActionListener PREV_PAGE = new prev_page(false);
+ public static final ActionListener PREV_WORD = new prev_word(false);
+ public static final ActionListener SELECT_PREV_CHAR = new prev_char(true);
+ public static final ActionListener SELECT_PREV_LINE = new prev_line(true);
+ public static final ActionListener SELECT_PREV_PAGE = new prev_page(true);
+ public static final ActionListener SELECT_PREV_WORD = new prev_word(true);
+ public static final ActionListener REPEAT = new repeat();
+ public static final ActionListener TOGGLE_RECT = new toggle_rect();
+
+ // Default action
+ public static final ActionListener INSERT_CHAR = new insert_char();
+
+ private static Hashtable actions;
+
+ static
+ {
+ actions = new Hashtable();
+ actions.put("backspace",BACKSPACE);
+ actions.put("backspace-word",BACKSPACE_WORD);
+ actions.put("delete",DELETE);
+ actions.put("delete-word",DELETE_WORD);
+ actions.put("end",END);
+ actions.put("select-end",SELECT_END);
+ actions.put("document-end",DOCUMENT_END);
+ actions.put("select-doc-end",SELECT_DOC_END);
+ actions.put("insert-break",INSERT_BREAK);
+ actions.put("insert-tab",INSERT_TAB);
+ actions.put("home",HOME);
+ actions.put("select-home",SELECT_HOME);
+ actions.put("document-home",DOCUMENT_HOME);
+ actions.put("select-doc-home",SELECT_DOC_HOME);
+ actions.put("next-char",NEXT_CHAR);
+ actions.put("next-line",NEXT_LINE);
+ actions.put("next-page",NEXT_PAGE);
+ actions.put("next-word",NEXT_WORD);
+ actions.put("select-next-char",SELECT_NEXT_CHAR);
+ actions.put("select-next-line",SELECT_NEXT_LINE);
+ actions.put("select-next-page",SELECT_NEXT_PAGE);
+ actions.put("select-next-word",SELECT_NEXT_WORD);
+ actions.put("overwrite",OVERWRITE);
+ actions.put("prev-char",PREV_CHAR);
+ actions.put("prev-line",PREV_LINE);
+ actions.put("prev-page",PREV_PAGE);
+ actions.put("prev-word",PREV_WORD);
+ actions.put("select-prev-char",SELECT_PREV_CHAR);
+ actions.put("select-prev-line",SELECT_PREV_LINE);
+ actions.put("select-prev-page",SELECT_PREV_PAGE);
+ actions.put("select-prev-word",SELECT_PREV_WORD);
+ actions.put("repeat",REPEAT);
+ actions.put("toggle-rect",TOGGLE_RECT);
+ actions.put("insert-char",INSERT_CHAR);
+ }
+
+ /**
+ * Returns a named text area action.
+ * @param name The action name
+ */
+ public static ActionListener getAction(String name)
+ {
+ return (ActionListener)actions.get(name);
+ }
+
+ /**
+ * Returns the name of the specified text area action.
+ * @param listener The action
+ */
+ public static String getActionName(ActionListener listener)
+ {
+ Enumeration e = getActions();
+ while(e.hasMoreElements())
+ {
+ String name = (String)e.nextElement();
+ ActionListener _listener = getAction(name);
+ if(_listener == listener)
+ return name;
+ }
+ return null;
+ }
+
+ /**
+ * Returns an enumeration of all available actions.
+ */
+ public static Enumeration getActions()
+ {
+ return actions.keys();
+ }
+
+ /**
+ * Adds the default key bindings to this input handler.
+ * This should not be called in the constructor of this
+ * input handler, because applications might load the
+ * key bindings from a file, etc.
+ */
+ public abstract void addDefaultKeyBindings();
+
+ /**
+ * Adds a key binding to this input handler.
+ * @param keyBinding The key binding (the format of this is
+ * input-handler specific)
+ * @param action The action
+ */
+ public abstract void addKeyBinding(String keyBinding, ActionListener action);
+
+ /**
+ * Removes a key binding from this input handler.
+ * @param keyBinding The key binding
+ */
+ public abstract void removeKeyBinding(String keyBinding);
+
+ /**
+ * Removes all key bindings from this input handler.
+ */
+ public abstract void removeAllKeyBindings();
+
+ /**
+ * Grabs the next key typed event and invokes the specified
+ * action with the key as a the action command.
+ * @param action The action
+ */
+ public void grabNextKeyStroke(ActionListener listener)
+ {
+ grabAction = listener;
+ }
+
+ /**
+ * Returns if repeating is enabled. When repeating is enabled,
+ * actions will be executed multiple times. This is usually
+ * invoked with a special key stroke in the input handler.
+ */
+ public boolean isRepeatEnabled()
+ {
+ return repeat;
+ }
+
+ /**
+ * Enables repeating. When repeating is enabled, actions will be
+ * executed multiple times. Once repeating is enabled, the input
+ * handler should read a number from the keyboard.
+ */
+ public void setRepeatEnabled(boolean repeat)
+ {
+ this.repeat = repeat;
+ }
+
+ /**
+ * Returns the number of times the next action will be repeated.
+ */
+ public int getRepeatCount()
+ {
+ return (repeat ? Math.max(1,repeatCount) : 1);
+ }
+
+ /**
+ * Sets the number of times the next action will be repeated.
+ * @param repeatCount The repeat count
+ */
+ public void setRepeatCount(int repeatCount)
+ {
+ this.repeatCount = repeatCount;
+ }
+
+ /**
+ * Returns the macro recorder. If this is non-null, all executed
+ * actions should be forwarded to the recorder.
+ */
+ public InputHandler.MacroRecorder getMacroRecorder()
+ {
+ return recorder;
+ }
+
+ /**
+ * Sets the macro recorder. If this is non-null, all executed
+ * actions should be forwarded to the recorder.
+ * @param recorder The macro recorder
+ */
+ public void setMacroRecorder(InputHandler.MacroRecorder recorder)
+ {
+ this.recorder = recorder;
+ }
+
+ /**
+ * Returns a copy of this input handler that shares the same
+ * key bindings. Setting key bindings in the copy will also
+ * set them in the original.
+ */
+ public abstract InputHandler copy();
+
+ /**
+ * Executes the specified action, repeating and recording it as
+ * necessary.
+ * @param listener The action listener
+ * @param source The event source
+ * @param actionCommand The action command
+ */
+ public void executeAction(ActionListener listener, Object source,
+ String actionCommand)
+ {
+ // create event
+ ActionEvent evt = new ActionEvent(source,
+ ActionEvent.ACTION_PERFORMED,
+ actionCommand);
+
+ // don't do anything if the action is a wrapper
+ // (like EditAction.Wrapper)
+ if(listener instanceof Wrapper)
+ {
+ listener.actionPerformed(evt);
+ return;
+ }
+
+ // remember old values, in case action changes them
+ boolean _repeat = repeat;
+ int _repeatCount = getRepeatCount();
+
+ // execute the action
+ if(listener instanceof InputHandler.NonRepeatable)
+ listener.actionPerformed(evt);
+ else
+ {
+ for(int i = 0; i < Math.max(1,repeatCount); i++)
+ listener.actionPerformed(evt);
+ }
+
+ // do recording. Notice that we do no recording whatsoever
+ // for actions that grab keys
+ if(grabAction == null)
+ {
+ if(recorder != null)
+ {
+ if(!(listener instanceof InputHandler.NonRecordable))
+ {
+ if(_repeatCount != 1)
+ recorder.actionPerformed(REPEAT,String.valueOf(_repeatCount));
+
+ recorder.actionPerformed(listener,actionCommand);
+ }
+ }
+
+ // If repeat was true originally, clear it
+ // Otherwise it might have been set by the action, etc
+ if(_repeat)
+ {
+ repeat = false;
+ repeatCount = 0;
+ }
+ }
+ }
+
+ /**
+ * Returns the text area that fired the specified event.
+ * @param evt The event
+ */
+ public static JEditTextArea getTextArea(EventObject evt)
+ {
+ if(evt != null)
+ {
+ Object o = evt.getSource();
+ if(o instanceof Component)
+ {
+ // find the parent text area
+ Component c = (Component)o;
+ for(;;)
+ {
+ if(c instanceof JEditTextArea)
+ return (JEditTextArea)c;
+ else if(c == null)
+ break;
+ if(c instanceof JPopupMenu)
+ c = ((JPopupMenu)c)
+ .getInvoker();
+ else
+ c = c.getParent();
+ }
+ }
+ }
+
+ // this shouldn't happen
+ System.err.println("BUG: getTextArea() returning null");
+ System.err.println("Report this to Slava Pestov