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: + *

+ * It is also faster and doesn't have as many problems. It can be used + * in other applications; the only other part of jEdit it depends on is + * the syntax package.

+ * + * 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 "); + return null; + } + + // protected members + + /** + * If a key is being grabbed, this method should be called with + * the appropriate key event. It executes the grab action with + * the typed character as the parameter. + */ + protected void handleGrabAction(KeyEvent evt) + { + // Clear it *before* it is executed so that executeAction() + // resets the repeat count + ActionListener _grabAction = grabAction; + grabAction = null; + executeAction(_grabAction,evt.getSource(), + String.valueOf(evt.getKeyChar())); + } + + // protected members + protected ActionListener grabAction; + protected boolean repeat; + protected int repeatCount; + protected InputHandler.MacroRecorder recorder; + + /** + * If an action implements this interface, it should not be repeated. + * Instead, it will handle the repetition itself. + */ + public interface NonRepeatable {} + + /** + * If an action implements this interface, it should not be recorded + * by the macro recorder. Instead, it will do its own recording. + */ + public interface NonRecordable {} + + /** + * For use by EditAction.Wrapper only. + * @since jEdit 2.2final + */ + public interface Wrapper {} + + /** + * Macro recorder. + */ + public interface MacroRecorder + { + void actionPerformed(ActionListener listener, + String actionCommand); + } + + public static class backspace implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + if(!textArea.isEditable()) + { + textArea.getToolkit().beep(); + return; + } + + if(textArea.getSelectionStart() + != textArea.getSelectionEnd()) + { + textArea.setSelectedText(""); + } + else + { + int caret = textArea.getCaretPosition(); + if(caret == 0) + { + textArea.getToolkit().beep(); + return; + } + try + { + textArea.getDocument().remove(caret - 1,1); + } + catch(BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + } + + public static class backspace_word implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int start = textArea.getSelectionStart(); + if(start != textArea.getSelectionEnd()) + { + textArea.setSelectedText(""); + } + + int line = textArea.getCaretLine(); + int lineStart = textArea.getLineStartOffset(line); + int caret = start - lineStart; + + String lineText = textArea.getLineText(textArea + .getCaretLine()); + + if(caret == 0) + { + if(lineStart == 0) + { + textArea.getToolkit().beep(); + return; + } + caret--; + } + else + { + String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordStart(lineText,caret,noWordSep); + } + + try + { + textArea.getDocument().remove( + caret + lineStart, + start - (caret + lineStart)); + } + catch(BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + + public static class delete implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + if(!textArea.isEditable()) + { + textArea.getToolkit().beep(); + return; + } + + if(textArea.getSelectionStart() + != textArea.getSelectionEnd()) + { + textArea.setSelectedText(""); + } + else + { + int caret = textArea.getCaretPosition(); + if(caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + try + { + textArea.getDocument().remove(caret,1); + } + catch(BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + } + + public static class delete_word implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int start = textArea.getSelectionStart(); + if(start != textArea.getSelectionEnd()) + { + textArea.setSelectedText(""); + } + + int line = textArea.getCaretLine(); + int lineStart = textArea.getLineStartOffset(line); + int caret = start - lineStart; + + String lineText = textArea.getLineText(textArea + .getCaretLine()); + + if(caret == lineText.length()) + { + if(lineStart + caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + caret++; + } + else + { + String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); + } + + try + { + textArea.getDocument().remove(start, + (caret + lineStart) - start); + } + catch(BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + + public static class end implements ActionListener + { + private boolean select; + + public end(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + int caret = textArea.getCaretPosition(); + + int lastOfLine = textArea.getLineEndOffset( + textArea.getCaretLine()) - 1; + int lastVisibleLine = textArea.getFirstLine() + + textArea.getVisibleLines(); + if(lastVisibleLine >= textArea.getLineCount()) + { + lastVisibleLine = Math.min(textArea.getLineCount() - 1, + lastVisibleLine); + } + else + lastVisibleLine -= (textArea.getElectricScroll() + 1); + + int lastVisible = textArea.getLineEndOffset(lastVisibleLine) - 1; + int lastDocument = textArea.getDocumentLength(); + + if(caret == lastDocument) + { + textArea.getToolkit().beep(); + return; + } + else if(!Boolean.TRUE.equals(textArea.getClientProperty( + SMART_HOME_END_PROPERTY))) + caret = lastOfLine; + else if(caret == lastVisible) + caret = lastDocument; + else if(caret == lastOfLine) + caret = lastVisible; + else + caret = lastOfLine; + + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + } + } + + public static class document_end implements ActionListener + { + private boolean select; + + public document_end(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + if(select) + textArea.select(textArea.getMarkPosition(), + textArea.getDocumentLength()); + else + textArea.setCaretPosition(textArea + .getDocumentLength()); + } + } + + public static class home implements ActionListener + { + private boolean select; + + public home(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + int caret = textArea.getCaretPosition(); + + int firstLine = textArea.getFirstLine(); + + int firstOfLine = textArea.getLineStartOffset( + textArea.getCaretLine()); + int firstVisibleLine = (firstLine == 0 ? 0 : + firstLine + textArea.getElectricScroll()); + int firstVisible = textArea.getLineStartOffset( + firstVisibleLine); + + if(caret == 0) + { + textArea.getToolkit().beep(); + return; + } + else if(!Boolean.TRUE.equals(textArea.getClientProperty( + SMART_HOME_END_PROPERTY))) + caret = firstOfLine; + else if(caret == firstVisible) + caret = 0; + else if(caret == firstOfLine) + caret = firstVisible; + else + caret = firstOfLine; + + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + } + } + + public static class document_home implements ActionListener + { + private boolean select; + + public document_home(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + if(select) + textArea.select(textArea.getMarkPosition(),0); + else + textArea.setCaretPosition(0); + } + } + + public static class insert_break implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + if(!textArea.isEditable()) + { + textArea.getToolkit().beep(); + return; + } + + textArea.setSelectedText("\n"); + } + } + + public static class insert_tab implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + if(!textArea.isEditable()) + { + textArea.getToolkit().beep(); + return; + } + + textArea.overwriteSetSelectedText("\t"); + } + } + + public static class next_char implements ActionListener + { + private boolean select; + + public next_char(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + if(caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + + if(select) + textArea.select(textArea.getMarkPosition(), + caret + 1); + else + textArea.setCaretPosition(caret + 1); + } + } + + public static class next_line implements ActionListener + { + private boolean select; + + public next_line(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + int line = textArea.getCaretLine(); + + if(line == textArea.getLineCount() - 1) + { + textArea.getToolkit().beep(); + return; + } + + int magic = textArea.getMagicCaretPosition(); + if(magic == -1) + { + magic = textArea.offsetToX(line, + caret - textArea.getLineStartOffset(line)); + } + + caret = textArea.getLineStartOffset(line + 1) + + textArea.xToOffset(line + 1,magic); + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + textArea.setMagicCaretPosition(magic); + } + } + + public static class next_page implements ActionListener + { + private boolean select; + + public next_page(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int lineCount = textArea.getLineCount(); + int firstLine = textArea.getFirstLine(); + int visibleLines = textArea.getVisibleLines(); + int line = textArea.getCaretLine(); + + firstLine += visibleLines; + + if(firstLine + visibleLines >= lineCount - 1) + firstLine = lineCount - visibleLines; + + textArea.setFirstLine(firstLine); + + int caret = textArea.getLineStartOffset( + Math.min(textArea.getLineCount() - 1, + line + visibleLines)); + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + } + } + + public static class next_word implements ActionListener + { + private boolean select; + + public next_word(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + int line = textArea.getCaretLine(); + int lineStart = textArea.getLineStartOffset(line); + caret -= lineStart; + + String lineText = textArea.getLineText(textArea + .getCaretLine()); + + if(caret == lineText.length()) + { + if(lineStart + caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + caret++; + } + else + { + String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); + } + + if(select) + textArea.select(textArea.getMarkPosition(), + lineStart + caret); + else + textArea.setCaretPosition(lineStart + caret); + } + } + + public static class overwrite implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + textArea.setOverwriteEnabled( + !textArea.isOverwriteEnabled()); + } + } + + public static class prev_char implements ActionListener + { + private boolean select; + + public prev_char(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + if(caret == 0) + { + textArea.getToolkit().beep(); + return; + } + + if(select) + textArea.select(textArea.getMarkPosition(), + caret - 1); + else + textArea.setCaretPosition(caret - 1); + } + } + + public static class prev_line implements ActionListener + { + private boolean select; + + public prev_line(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + int line = textArea.getCaretLine(); + + if(line == 0) + { + textArea.getToolkit().beep(); + return; + } + + int magic = textArea.getMagicCaretPosition(); + if(magic == -1) + { + magic = textArea.offsetToX(line, + caret - textArea.getLineStartOffset(line)); + } + + caret = textArea.getLineStartOffset(line - 1) + + textArea.xToOffset(line - 1,magic); + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + textArea.setMagicCaretPosition(magic); + } + } + + public static class prev_page implements ActionListener + { + private boolean select; + + public prev_page(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int firstLine = textArea.getFirstLine(); + int visibleLines = textArea.getVisibleLines(); + int line = textArea.getCaretLine(); + + if(firstLine < visibleLines) + firstLine = visibleLines; + + textArea.setFirstLine(firstLine - visibleLines); + + int caret = textArea.getLineStartOffset( + Math.max(0,line - visibleLines)); + if(select) + textArea.select(textArea.getMarkPosition(),caret); + else + textArea.setCaretPosition(caret); + } + } + + public static class prev_word implements ActionListener + { + private boolean select; + + public prev_word(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + int caret = textArea.getCaretPosition(); + int line = textArea.getCaretLine(); + int lineStart = textArea.getLineStartOffset(line); + caret -= lineStart; + + String lineText = textArea.getLineText(textArea + .getCaretLine()); + + if(caret == 0) + { + if(lineStart == 0) + { + textArea.getToolkit().beep(); + return; + } + caret--; + } + else + { + String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordStart(lineText,caret,noWordSep); + } + + if(select) + textArea.select(textArea.getMarkPosition(), + lineStart + caret); + else + textArea.setCaretPosition(lineStart + caret); + } + } + + public static class repeat implements ActionListener, + InputHandler.NonRecordable + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + textArea.getInputHandler().setRepeatEnabled(true); + String actionCommand = evt.getActionCommand(); + if(actionCommand != null) + { + textArea.getInputHandler().setRepeatCount( + Integer.parseInt(actionCommand)); + } + } + } + + public static class toggle_rect implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + textArea.setSelectionRectangular( + !textArea.isSelectionRectangular()); + } + } + + public static class insert_char implements ActionListener, + InputHandler.NonRepeatable + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + String str = evt.getActionCommand(); + int repeatCount = textArea.getInputHandler().getRepeatCount(); + + if(textArea.isEditable()) + { + StringBuffer buf = new StringBuffer(); + for(int i = 0; i < repeatCount; i++) + buf.append(str); + textArea.overwriteSetSelectedText(buf.toString()); + } + else + { + textArea.getToolkit().beep(); + } + } + } +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/JSVGViewerFrame.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/JSVGViewerFrame.java (revision 549415) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/apps/svgbrowser/JSVGViewerFrame.java (working copy) @@ -56,6 +56,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; +import java.net.URI; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -70,6 +71,7 @@ import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; @@ -88,6 +90,9 @@ import javax.swing.text.Document; import javax.swing.text.PlainDocument; +import org.apache.batik.apps.svgbrowser.srcview.JEditTextArea; +import org.apache.batik.apps.svgbrowser.srcview.SourceViewFrame; +import org.apache.batik.apps.svgbrowser.srcview.XMLTokenMarker; import org.apache.batik.bridge.DefaultExternalResourceSecurity; import org.apache.batik.bridge.DefaultScriptSecurity; import org.apache.batik.bridge.EmbededExternalResourceSecurity; @@ -102,6 +107,7 @@ import org.apache.batik.bridge.UpdateManagerEvent; import org.apache.batik.bridge.UpdateManagerListener; import org.apache.batik.dom.StyleSheetProcessingInstruction; +import org.apache.batik.dom.svg.LiveAttributeException; import org.apache.batik.dom.svg.SVGOMDocument; import org.apache.batik.dom.util.HashTable; import org.apache.batik.dom.util.DOMUtilities; @@ -133,6 +139,7 @@ import org.apache.batik.util.SVGConstants; import org.apache.batik.util.XMLConstants; import org.apache.batik.util.gui.DOMViewer; +import org.apache.batik.util.gui.ErrorConsole; import org.apache.batik.util.gui.JErrorPane; import org.apache.batik.util.gui.LocationBar; import org.apache.batik.util.gui.MemoryMonitor; @@ -1841,26 +1848,13 @@ final ParsedURL u = new ParsedURL(svgDocument.getURL()); - final JFrame fr = new JFrame(u.toString()); - fr.setSize(resources.getInteger("ViewSource.width"), - resources.getInteger("ViewSource.height")); - final JTextArea ta = new JTextArea(); - ta.setLineWrap(true); - ta.setFont(new Font("monospaced", Font.PLAIN, 12)); - - JScrollPane scroll = new JScrollPane(); - scroll.getViewport().add(ta); - scroll.setVerticalScrollBarPolicy - (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - fr.getContentPane().add(scroll, BorderLayout.CENTER); - new Thread() { public void run() { char [] buffer = new char[4096]; try { - Document doc = new PlainDocument(); - + StringBuilder sb = new StringBuilder(); + ParsedURL purl = new ParsedURL(svgDocument.getURL()); InputStream is = u.openStream(getInputHandler(purl). @@ -1870,13 +1864,16 @@ Reader in = XMLUtilities.createXMLDocumentReader(is); int len; while ((len=in.read(buffer, 0, buffer.length)) != -1) { - doc.insertString(doc.getLength(), - new String(buffer, 0, len), null); + sb.append(new String(buffer, 0, len)); } - ta.setDocument(doc); - ta.setEditable(false); - ta.setBackground(Color.white); + final JFrame fr = new SourceViewFrame(application, sb.toString(), userAgent); + fr.setTitle(u.toString()); + ImageIcon icon = new ImageIcon + (getClass().getResource(resources.getString("ViewSource.frameicon"))); + fr.setIconImage(icon.getImage()); + fr.setSize(resources.getInteger("ViewSource.width"), + resources.getInteger("ViewSource.height")); fr.setVisible(true); } catch (Exception ex) { userAgent.displayError(ex); @@ -2734,9 +2731,16 @@ if (debug) { ex.printStackTrace(); } - JErrorPane pane = - new JErrorPane(ex, JOptionPane.ERROR_MESSAGE); - JDialog dialog = pane.createDialog(JSVGViewerFrame.this, "ERROR"); +// JErrorPane pane = +// new JErrorPane(ex, JOptionPane.ERROR_MESSAGE); + ErrorConsole console = ErrorConsole.getInstance(); + if (ex instanceof LiveAttributeException) { + LiveAttributeException lex = (LiveAttributeException)ex; + console.add(lex.getElement().getNodeName(), svgDocument.getDocumentURI(), JOptionPane.ERROR_MESSAGE); + } else { + console.add(ex, svgDocument.getDocumentURI(), JOptionPane.ERROR_MESSAGE); + } + JDialog dialog = ErrorConsole.createDialog(JSVGViewerFrame.this, "ERROR"); dialog.setModal(false); dialog.setVisible(true); } Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JFramesView.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JFramesView.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JFramesView.java (revision 0) @@ -0,0 +1,380 @@ +/* + + 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.util.gui.resource; + +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Stroke; +import java.awt.event.ActionEvent; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +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.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.border.BevelBorder; + +public class JFramesView extends JPanel implements ActionMap { + + /** + * The resource file name + */ + protected static final String RESOURCES = "org.apache.batik.util.gui.resource.resources.JTimeline"; + + /** + * 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 button factory. + */ + protected ButtonFactory bf = new ButtonFactory(bundle, this); + + /** + * The menu factory. + */ + protected MenuFactory mf = new MenuFactory(bundle, this); + + private JTimelineSlider sliderPanel; + + private JPopupMenu cornerPopup; + + private JLabel timerLabel; + + private int precisionMillis = 100; + + private int pixelsPerUnit = 50; + + private float value = 1; + + private int knobPosition; + + private JScrollPane framesScrollPane; + + private JScrollBar verticalScrollBar; + + public JFramesView() { + super(new BorderLayout()); + + actions.put("CornerButtonAction", new CornerButtonAction()); + actions.put("IncreaseUnitSizeMenuItemAction", + new IncreaseUnitSizeMenuItemAction()); + actions.put("DecreaseUnitSizeMenuItemAction", + new DecreaseUnitSizeMenuItemAction()); + + sliderPanel = new JTimelineSlider(); + + framesScrollPane = new JScrollPane( + new JTimelineFramesPanel()); + framesScrollPane.setBorder(null); + framesScrollPane + .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + framesScrollPane + .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); + framesScrollPane.setColumnHeaderView(sliderPanel); + + JButton cornerButton = bf.createJButton("CornerButton"); + cornerButton.setFocusable(false); + cornerPopup = new JPopupMenu(); + cornerPopup.add(mf.createJMenuItem("IncreaseUnitSizeMenuItem")); + cornerPopup.add(mf.createJMenuItem("DecreaseUnitSizeMenuItem")); + framesScrollPane + .setCorner(JScrollPane.UPPER_RIGHT_CORNER, cornerButton); + add(framesScrollPane); + + Box footerPanel = new Box(BoxLayout.X_AXIS); + add(footerPanel, BorderLayout.SOUTH); + timerLabel = new JLabel("-- : -- : --"); + timerLabel.setBorder(BorderFactory + .createBevelBorder(BevelBorder.LOWERED)); + footerPanel.add(timerLabel); + JScrollBar horizScrollBar = new JScrollBar(JScrollBar.HORIZONTAL); + int prefHeight = horizScrollBar.getPreferredSize().height; + footerPanel + .setMaximumSize(new Dimension(Integer.MAX_VALUE, prefHeight)); + footerPanel.setMinimumSize(new Dimension(0, prefHeight)); + footerPanel.setPreferredSize(footerPanel.getMinimumSize()); + footerPanel.add(horizScrollBar); + footerPanel.add(Box.createHorizontalStrut(framesScrollPane + .getVerticalScrollBar().getPreferredSize().width)); + } + + public JScrollBar getVerticalScrollBar() { + return verticalScrollBar; + } + + public void setVerticalScrollBar(JScrollBar verticalScrollBar) { + this.verticalScrollBar = verticalScrollBar; + add(verticalScrollBar, BorderLayout.EAST); + verticalScrollBar.addAdjustmentListener(new AdjustmentListener() { + public void adjustmentValueChanged(AdjustmentEvent e) { + + } + }); + } + + /** + * The map that contains the actions + */ + protected Map actions = 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 { + return (Action) actions.get(key); + } + + protected class CornerButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + Component invoker = (Component) evt.getSource(); + cornerPopup.show(invoker, 0, invoker.getHeight()); + } + } + + protected class IncreaseUnitSizeMenuItemAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + // TODO: + // Increase unit size in timeline slider + } + } + + protected class DecreaseUnitSizeMenuItemAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + // TODO: + // Decrease unit size in timeline slider + } + } + + public void paint(Graphics g) { + knobPosition = (int) (value * pixelsPerUnit) + SLIDER_X_OFFSET; + + super.paint(g); + } + + public static final int SLIDER_X_OFFSET = 10; + + private static final Color SLIDER_KNOB_FILL_COLOR = new Color(0x77FF7777, + true); + + private static final Color SLIDER_KNOB_STROKE_COLOR = new Color(0x00FF0000); + + private static final Stroke SLIDER_KNOB_STROKE = new BasicStroke(1, + BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL, 5.0f, + new float[] { 5.0f }, 0.0f); + + private class JTimelineSlider extends JComponent { + + private boolean paintTrack = false; + + private Polygon knob; + + private Rectangle knobBounds; + + private boolean over; + + private MouseMotionListener mml = new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + int x = e.getX(), y = e.getY(); + boolean b = knobBounds.contains(x, y); + if (b != b) { + repaint(); + } + over = b; + } + + public void mouseDragged(MouseEvent e) { + if (over) { + int x = e.getX(); + if (x >= SLIDER_X_OFFSET) { + value = (float) (x - SLIDER_X_OFFSET) / pixelsPerUnit; + JFramesView.this.repaint(); + } + } + } + }; + + public JTimelineSlider() { + int height = 30; + + setMaximumSize(new Dimension(Integer.MAX_VALUE, height)); + setMinimumSize(new Dimension(0, height)); + setPreferredSize(getMinimumSize()); + setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + addMouseMotionListener(mml); + + int pos = height / 2 - 3; + knob = new Polygon(); + knob.addPoint(-4, -pos); + knob.addPoint(4, -pos); + knob.addPoint(4, pos); + knob.addPoint(-4, pos); + knob.addPoint(-4, -pos); + + knobBounds = new Rectangle(6, height); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + Dimension size = getSize(); + if (paintTrack) { + int pos = size.height / 2; + g.setColor(Color.gray); + g.fillRect(0, pos - 1, size.width, 2); + g.setColor(Color.white); + g.drawLine(0, pos + 1, size.width, pos + 1); + } + + int dx = pixelsPerUnit * precisionMillis / 1000; + FontMetrics fm = g.getFontMetrics(); + int timeUnit = 0; + for (int i = SLIDER_X_OFFSET; i < size.width; i += pixelsPerUnit) { + String str = timeUnit++ + ""; + int strWidth = fm.stringWidth(str), strHeight = fm.getHeight() + - fm.getDescent(); + g.setColor(getBackground()); + g.fillRect(i - strWidth / 2, (size.height - strHeight) / 2, + strWidth, strHeight); + g.setColor(Color.black); + g.drawString(str, i - strWidth / 2, + (size.height + strHeight) / 2); + g.setColor(Color.gray); + g.drawLine(i, size.height - 2, i, size.height - 7); + g.drawLine(i, 2, i, 7); + g.setColor(Color.lightGray); + for (int j = i + dx; j < i + pixelsPerUnit; j += dx) { + g.drawLine(j, size.height - 2, j, size.height - 5); + g.drawLine(j, 2, j, 5); + } + } + + // Paint knob + int x = knobPosition; + int y = size.height / 2; + knobBounds.x = x - knobBounds.width / 2; + knobBounds.y = y - knobBounds.height / 2; + + g.translate(x, y); + g.setColor(SLIDER_KNOB_FILL_COLOR); + g.fillPolygon(knob); + g.setColor(SLIDER_KNOB_STROKE_COLOR); + g.drawPolygon(knob); + g.translate(-x, -y); + } + } + + private class JTimelineFramesPanel extends JPanel { + + private boolean inside; + + private int hoverPosition; + + private MouseListener ml = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + inside = true; + JFramesView.this.repaint(); + } + + public void mouseExited(MouseEvent e) { + inside = false; + JFramesView.this.repaint(); + } + }; + + private MouseMotionListener mml = new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + int x = e.getX() - SLIDER_X_OFFSET; + int dx = pixelsPerUnit * precisionMillis / 1000; + hoverPosition = SLIDER_X_OFFSET + x - x % dx; + JFramesView.this.repaint(); + } + }; + + public JTimelineFramesPanel() { + addMouseListener(ml); + addMouseMotionListener(mml); + } + + protected void paintComponent(Graphics g) { + Dimension size = getSize(); + + int dx = pixelsPerUnit * precisionMillis / 1000; + g.setColor(Color.lightGray); + for (int i = SLIDER_X_OFFSET; i < size.width; i += dx) { + g.drawLine(i, 0, i, size.height); + } + + g.setColor(SLIDER_KNOB_STROKE_COLOR); + g.drawLine(knobPosition, 0, knobPosition, size.height); + + if (inside) { + Graphics2D g2 = (Graphics2D)g; + g2.setStroke(SLIDER_KNOB_STROKE); + + g2.drawLine(hoverPosition, 0, hoverPosition, size.height); + } + } + } +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/ButtonFactory.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/ButtonFactory.java (revision 549415) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/ButtonFactory.java (working copy) @@ -28,6 +28,7 @@ import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JRadioButton; +import javax.swing.JToggleButton; /** * This class represents a button factory which builds @@ -123,6 +124,31 @@ } /** + * Creates and returns a new swing button initialised + * to be used as a toolbar toggle button + * @param name the name of the button in the resource bundle + * @throws MissingResourceException if key is not the name of a button. + * It is not thrown if the mnemonic and the action keys are missing + * @throws ResourceFormatException if the mnemonic is not a single + * character + * @throws MissingListenerException if the button action is not found in + * the action map + */ + public JToggleButton createJToolbarToggleButton(String name) + throws MissingResourceException, + ResourceFormatException, + MissingListenerException { + JToggleButton result; + try { + result = new JToolbarToggleButton(getString(name+TEXT_SUFFIX)); + } catch (MissingResourceException e) { + result = new JToolbarToggleButton(); + } + initializeButton(result, name); + return result; + } + + /** * Creates and returns a new swing radio button * @param name the name of the button in the resource bundle * @throws MissingResourceException if key is not the name of a button. Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/Hyperlink.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/Hyperlink.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/Hyperlink.java (revision 0) @@ -0,0 +1,72 @@ +package org.apache.batik.util.gui.resource; + +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JLabel; + +public class Hyperlink extends JLabel { + private MouseListener ml = new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (callback != null) { + callback.linkClicked(new HyperlinkEvent(Hyperlink.this.getCommand())); + } + } + }; + + private HyperlinkCallback callback; + + private String command; + + private static final String HTML_TEXT = "__HTML_TEXT__"; + + private String text; + + public Hyperlink(String text, HyperlinkCallback callback) { + this.callback = callback; + setLinkText(text); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + addMouseListener(ml); + } + + public String getLinkText() { + return text; + } + + public void setLinkText(String text) { + this.text = text; + super.setText(HTML_TEXT.replace("__HTML_TEXT__", text)); + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public static class HyperlinkEvent { + + private String command; + + public HyperlinkEvent(String command) { + this.command = command; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + } + + public static interface HyperlinkCallback { + public void linkClicked(HyperlinkEvent e); + } +} + Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarButton.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarButton.java (revision 549415) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarButton.java (working copy) @@ -23,6 +23,7 @@ import java.awt.event.MouseEvent; import javax.swing.JButton; +import javax.swing.UIManager; /** * This class represents the buttons used in toolbars. @@ -57,7 +58,14 @@ } setBorderPainted(false); setMargin(new Insets(2, 2, 2, 2)); - addMouseListener(new MouseListener()); + + // Windows XP look and feel seems to have a bug due to which the + // size of the parent container changes when the border painted + // property is set. Temporary fix: disable mouseover behavior if + // installed lnf is Windows XP + if (!UIManager.getLookAndFeel().getName().equals("Windows")) { + addMouseListener(new MouseListener()); + } } /** Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JDocumentTree.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JDocumentTree.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JDocumentTree.java (revision 0) @@ -0,0 +1,279 @@ +package org.apache.batik.util.gui.resource; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.swing.BorderFactory; +import javax.swing.DefaultListModel; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.ListCellRenderer; +import javax.swing.tree.TreeNode; + +public class JDocumentTree extends JPanel { + + /** + * The resource file name + */ + protected static final String RESOURCES = "org.apache.batik.util.gui.resource.resources.JDocumentTree"; + + /** + * 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); + } + + private JList nodeList; + + private JScrollPane nodeListScrollPane; + + private Node rootNode; + + public JDocumentTree() { + this(null); + } + + public JDocumentTree(JTree tree) { + super(new BorderLayout()); + + nodeList = new JList(new DefaultListModel()); + nodeList.setFixedCellHeight(25); + nodeList.setCellRenderer(new DocTreeCellRenderer()); + nodeListScrollPane = new JScrollPane(nodeList); + nodeListScrollPane + .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); + nodeListScrollPane.setBorder(null); + add(nodeListScrollPane); + + TreeNode treeNode = (TreeNode) tree.getModel().getRoot(); + rootNode = parse(treeNode, null); + rebuildNodeList(nodeList, rootNode); + + nodeList.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + Node selectedNode = (Node) nodeList.getSelectedValue(); + selectedNode.setExpanded(!selectedNode.isExpanded()); + rebuildNodeList(nodeList, rootNode); + } + } + }); + } + + public JScrollBar getVerticalScrollBar() { + return nodeListScrollPane.getVerticalScrollBar(); + } + + public void setVerticalScrollBar(JScrollBar verticalScrollBar) { + nodeListScrollPane.setVerticalScrollBar(verticalScrollBar); + } + + private void rebuildNodeList(JList nodeList, Node node) { + DefaultListModel listModel = (DefaultListModel) nodeList.getModel(); + listModel.clear(); + addNode(listModel, node); + } + + private void addNode(DefaultListModel listModel, Node node) { + Node parent = node.getParent(); + if (parent == null || parent.isExpanded()) { + listModel.addElement(node); + Node[] children = node.getChildren(); + if (children != null) { + for (int i = 0; i < children.length; i++) { + addNode(listModel, children[i]); + } + } + } + } + + private Node parse(TreeNode treeNode, Node parent) { + if (treeNode == null) { + return null; + } + + Node docNode = new Node(parent, treeNode.toString()); + int childCount = treeNode.getChildCount(); + if (childCount > 0) { + ArrayList childDocNodes = new ArrayList(); + for (int i = 0; i < childCount; i++) { + Node childDocNode = parse((TreeNode) treeNode.getChildAt(i), + docNode); + if (childDocNode != null) { + childDocNodes.add(childDocNode); + } + } + docNode.setChildren((Node[]) childDocNodes.toArray(new Node[0])); + } + + return docNode; + } + + private class Node extends JComponent { + + private String text; + + private Node parent; + + private int depth; + + private Node[] children; + + private boolean expanded = true; + + private boolean locked = false; + + public Node(Node parent, String text) { + this.parent = parent; + this.depth = parent == null ? 0 : parent.depth + 1; + setText(text); + } + + public Node getParent() { + return parent; + } + + public int getDepth() { + return depth; + } + + public Node[] getChildren() { + return children; + } + + public int getChildCount() { + return children == null ? 0 : children.length; + } + + public void setChildren(Node[] children) { + this.children = children; + if (children != null) { + for (int i = 0; i < children.length; i++) { + children[i].parent = this; + } + } + } + + public boolean isExpanded() { + if (parent == null) { + return true; + } + if (!expanded) { + return false; + } + return parent.isExpanded(); + } + + public void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + } + + private class DocTreeCellRenderer extends JLabel implements + ListCellRenderer { + + private final ImageIcon SPACER_ICON = getIcon("TreeNodeSpacer.icon"); + + private final ImageIcon CLOSED_ICON = getIcon("TreeNodeClosed.icon"); + + private final ImageIcon OPENED_ICON = getIcon("TreeNodeOpened.icon"); + + private final ImageIcon LEAF_ICON = getIcon("TreeLeaf.icon"); + + private final ImageIcon LOCK_ICON = getIcon("TreeNodeLocked.icon"); + + private Paint normalPaint, selectedPaint; + + private boolean selected; + + private boolean locked = true; + + public DocTreeCellRenderer() { + setIcon(SPACER_ICON); + + Dimension dim = getPreferredSize(); + normalPaint = new Color(234, 234, 234); + selectedPaint = new GradientPaint(0, 0, Color.white, 0, + 2 * dim.height, Color.gray); + } + + private ImageIcon getIcon(String name) { + return new ImageIcon(JDocumentTree.this.getClass().getResource( + resources.getString(name))); + } + + protected void paintComponent(Graphics g) { + Dimension size = getSize(); + Graphics2D g2 = (Graphics2D) g; + g2.setPaint(selected ? selectedPaint : normalPaint); + g2.fillRect(0, 0, size.width, size.height); + + g2.setColor(Color.lightGray); + g2.drawLine(0, size.height - 2, size.width, size.height - 2); + g2.setColor(Color.white); + g2.drawLine(0, size.height - 1, size.width, size.height - 1); + + super.paintComponent(g); + + g.setColor(Color.white); + int x, y; + x = size.width - 18; + y = (size.height - 16) / 2; + g.draw3DRect(x, y, 16, 16, false); + if (locked) { + LOCK_ICON.paintIcon(this, g, x, y); + } + } + + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + Node node = (Node) value; + selected = isSelected; + setText(node.getText()); + setBorder(BorderFactory.createEmptyBorder(0, node.depth * 10, 0, 0)); + if (node.getChildren() == null || node.getChildCount() == 0) {// leaf + setIcon(LEAF_ICON); + } else if (node.isExpanded()) { + setIcon(OPENED_ICON); + } else { + setIcon(CLOSED_ICON); + } + + return this; + } + } +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarToggleButton.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarToggleButton.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/resource/JToolbarToggleButton.java (revision 0) @@ -0,0 +1,84 @@ +/* + + 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.util.gui.resource; + +import java.awt.Insets; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JToggleButton; +import javax.swing.UIManager; + +import org.apache.batik.util.gui.resource.JToolbarButton.MouseListener; + +/** + * This class represents the buttons used in toolbars. + * + * @author Stephane Hillion + * @version $Id: JToolbarButton.java 498555 2007-01-22 08:09:33Z cam $ + */ +public class JToolbarToggleButton extends JToggleButton { + /** + * Creates a new toolbar button. + */ + public JToolbarToggleButton() { + initialize(); + } + + /** + * Creates a new toolbar button. + * @param txt The button text. + */ + public JToolbarToggleButton(String txt) { + super(txt); + initialize(); + } + + /** + * Initializes the button. + */ + protected void initialize() { + if (!System.getProperty("java.version").startsWith("1.3")) { + setOpaque(false); + setBackground(new java.awt.Color(0, 0, 0, 0)); + } + setBorderPainted(false); + setMargin(new Insets(2, 2, 2, 2)); + + // Windows XP look and feel seems to have a bug due to which the + // size of the parent container changes when the border painted + // property is set. Temporary fix: disable mouseover behavior if + // installed lnf is Windows XP + if (!UIManager.getLookAndFeel().getName().equals("Windows")) { + addMouseListener(new MouseListener()); + } + } + + /** + * To manage the mouse interactions. + */ + protected class MouseListener extends MouseAdapter { + public void mouseEntered(MouseEvent ev) { + setBorderPainted(true); + } + public void mouseExited(MouseEvent ev) { + setBorderPainted(false); + } + } +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/TimelineViewer.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/TimelineViewer.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/TimelineViewer.java (revision 0) @@ -0,0 +1,214 @@ +package org.apache.batik.util.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.event.ActionEvent; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; +import javax.swing.JTree; +import javax.swing.tree.TreeCellRenderer; + +import org.apache.batik.util.gui.resource.ActionMap; +import org.apache.batik.util.gui.resource.JDocumentTree; +import org.apache.batik.util.gui.resource.JFramesView; +import org.apache.batik.util.gui.resource.MissingListenerException; +import org.apache.batik.util.gui.resource.ResourceManager; +import org.apache.batik.util.gui.resource.ToolBarFactory; + +public class TimelineViewer extends JPanel implements ActionMap { + + /** + * The resource file name + */ + protected static final String RESOURCES = "org.apache.batik.util.gui.resources.TimelineViewer"; + + /** + * 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 toolbar factory. + */ + protected ToolBarFactory tf = new ToolBarFactory(bundle, this); + + private JTree docTree; + + public TimelineViewer() { + super(new BorderLayout()); + setPreferredSize(new Dimension(800, 300)); + + JSplitPane splitPane = new JSplitPane(); + splitPane.setResizeWeight(0.0); + JPanel leftPane = new JPanel(); + splitPane.setLeftComponent(leftPane); + + leftPane.setLayout(new BorderLayout()); + leftPane.setMinimumSize(new Dimension(300, 0)); + leftPane.setPreferredSize(getMinimumSize()); + leftPane.add(createToolBar(), BorderLayout.NORTH); + + JScrollBar scrollBar = new JScrollBar(JScrollBar.VERTICAL, 0, 1, 0, 100); + JDocumentTree docTree = new JDocumentTree(new JTree()); + docTree.setVerticalScrollBar(scrollBar); + leftPane.add(docTree); + + JFramesView framesView = new JFramesView(); + framesView.setVerticalScrollBar(scrollBar); + splitPane.setRightComponent(framesView); + add(splitPane); + } + + private JPanel createToolBar() { + JPanel toolbarPanel = new JPanel(new BorderLayout()); + JToolBar toolbar = tf.createJToolBar("ToolBar"); + toolbar.setFloatable(false); + + toolbarPanel.add(toolbar); + toolbarPanel.add(new JSeparator(), BorderLayout.SOUTH); + + return toolbarPanel; + } + + public JDialog createDialog(Component owner, String title) { + JDialog dialog = new JDialog(JOptionPane.getFrameForComponent(owner), + title); + dialog.getContentPane().add(this, BorderLayout.CENTER); + dialog.pack(); + return dialog; + } + + /** + * The map that contains the actions + */ + protected Map actions = 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 { + return (Action) actions.get(key); + } + + /** + * The action associated with the 'All' button. + */ + protected class DisplayAllButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + } + } + + public static void main(String[] args) { + JDialog d = new TimelineViewer().createDialog(null, + "Timeline Viewer 0.1"); + d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + d.setVisible(true); + } + +// private class DocTreeCellRenderer extends Box implements +// TreeCellRenderer { +// +// private final ImageIcon SPACER_ICON = getIcon("TreeNodeSpacer.icon"); +// +// private final ImageIcon CLOSED_ICON = getIcon("TreeNodeClosed.icon"); +// +// private final ImageIcon OPENED_ICON = getIcon("TreeNodeOpened.icon"); +// +// private final ImageIcon LEAF_ICON = getIcon("TreeLeaf.icon"); +// +// private Paint gradient; +// +// private JLabel label; +// +// private JLabel icon; +// +// public DocTreeCellRenderer() { +// super(BoxLayout.X_AXIS); +// setOpaque(false); +// +// icon = new JLabel(SPACER_ICON); +// add(icon); +// +// label = new JLabel(); +// add(label); +// +// Dimension dim = getPreferredSize(); +// gradient = new GradientPaint(0, 0, Color.white, 0, +// 2 * dim.height, Color.gray); +// } +// +// public void setBounds(int x, int y, int width, int height) { +// super.setBounds(x, y, docTree.getWidth(), height); +// System.out.println(getBounds()); +// } +// +// private ImageIcon getIcon(String name) { +// String uri = resources.getString(name); +// System.out.println(uri); +// return new ImageIcon(TimelineViewer.this.getClass().getResource( +// uri)); +// } +// +// protected void paintComponent(Graphics g) { +// Graphics2D g2 = (Graphics2D) g; +// g2.setPaint(gradient); +// g2.fillRect(0, 0, getWidth(), getHeight()); +// +// super.paintComponent(g); +// } +// +// public Component getTreeCellRendererComponent(JTree tree, Object value, +// boolean selected, boolean expanded, boolean leaf, int row, +// boolean hasFocus) { +// label.setText(value.toString()); +// +// if (leaf) { +// icon.setIcon(LEAF_ICON); +// } else if (expanded) { +// icon.setIcon(OPENED_ICON); +// } else { +// icon.setIcon(CLOSED_ICON); +// } +// return this; +// } +// } +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorHandlerProxy.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorHandlerProxy.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorHandlerProxy.java (revision 0) @@ -0,0 +1,38 @@ +package org.apache.batik.util.gui; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.swing.JDialog; +import javax.swing.JOptionPane; + +public class ErrorHandlerProxy implements InvocationHandler { + + private Object delegate; + + private String uri; + + public ErrorHandlerProxy(Object delegate, String uri) { + this.delegate = delegate; + this.uri = uri; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { + System.out.println("proxy invoked"); + return method.invoke(delegate, args); + } catch (InvocationTargetException ite) { + System.out.println("Exception occurred"); + ite.printStackTrace(); + } catch (Exception e) { + System.out.println("Exception occurred"); + ErrorConsole console = ErrorConsole.getInstance(); + console.add(e, uri, JOptionPane.ERROR_MESSAGE); + JDialog dialog = ErrorConsole.createDialog(null, "ERROR"); + dialog.setVisible(true); + } + return null; + } + +} Index: C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorConsole.java =================================================================== --- C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorConsole.java (revision 0) +++ C:/Documents and Settings/fyp24ys/Desktop/batikws/xml-batik/sources/org/apache/batik/util/gui/ErrorConsole.java (revision 0) @@ -0,0 +1,645 @@ +package org.apache.batik.util.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.SystemColor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import javax.swing.AbstractAction; +import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ButtonModel; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.Highlighter.HighlightPainter; + +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.util.ParsedURL; +import org.apache.batik.util.gui.resource.ActionMap; +import org.apache.batik.util.gui.resource.ButtonFactory; +import org.apache.batik.util.gui.resource.Hyperlink; +import org.apache.batik.util.gui.resource.JToolbarSeparator; +import org.apache.batik.util.gui.resource.MenuFactory; +import org.apache.batik.util.gui.resource.MissingListenerException; +import org.apache.batik.util.gui.resource.ResourceManager; +import org.apache.batik.util.gui.resource.Hyperlink.HyperlinkEvent; +import org.w3c.dom.Document; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.LocatorImpl; + +public class ErrorConsole extends JPanel implements ActionMap { + + /** + * The resource file name + */ + protected static final String RESOURCES = "org.apache.batik.util.gui.resources.ErrorConsole"; + + /** + * 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 button factory. + */ + protected ButtonFactory bf = new ButtonFactory(bundle, this); + + /** + * The menu factory. + */ + protected MenuFactory mf = new MenuFactory(bundle, this); + + private Box consoleList; + + private ButtonGroup errorGroup; + + private int displayType; + + private boolean sortOrderAscending = true; + + private JPopupMenu popupMenu; + + public ErrorConsole() { + super(new BorderLayout()); + + actions.put("DisplayAllButtonAction", new DisplayAllButtonAction()); + actions.put("DisplayErrorsButtonAction", + new DisplayErrorsButtonAction()); + actions.put("DisplayWarningsButtonAction", + new DisplayWarningsButtonAction()); + actions.put("DisplayMessagesButtonAction", + new DisplayMessagesButtonAction()); + actions.put("ClearButtonAction", new ClearButtonAction()); + + actions.put("FirstLastSortMenuAction", new FirstLastSortMenuAction()); + actions.put("LastFirstSortMenuAction", new LastFirstSortMenuAction()); + actions.put("CopyMenuAction", new CopyMenuAction()); + + JPanel errorPanel = new JPanel(new BorderLayout()); + errorPanel.setBackground(Color.white); + + JScrollPane errorScrollPane = new JScrollPane(errorPanel); + errorScrollPane + .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + add(errorScrollPane); + + errorGroup = new ButtonGroup(); + + consoleList = new Box(BoxLayout.Y_AXIS); + errorPanel.add(consoleList, BorderLayout.NORTH); + + popupMenu = createPopupMenu(); + + add(createToolBar(), BorderLayout.NORTH); + setPreferredSize(new Dimension(550, 400)); + } + + private JToolBar createToolBar() { + JToolBar toolbar = new JToolBar(); + toolbar.setFloatable(false); + + BooleanButtonGroup displayGroup = new BooleanButtonGroup(); + + JToggleButton displayAll = bf + .createJToolbarToggleButton("DisplayAllButton"); + displayGroup.add(displayAll); + displayAll.setSelected(true); + displayType(-1, true); + toolbar.add(displayAll); + + JToggleButton displayErrors = bf + .createJToolbarToggleButton("DisplayErrorsButton"); + displayGroup.addOr(displayErrors); + toolbar.add(displayErrors); + + JToggleButton displayWarnings = bf + .createJToolbarToggleButton("DisplayWarningsButton"); + displayGroup.addOr(displayWarnings); + toolbar.add(displayWarnings); + + JToggleButton displayMessages = bf + .createJToolbarToggleButton("DisplayMessagesButton"); + displayGroup.addOr(displayMessages); + toolbar.add(displayMessages); + + toolbar.add(new JToolbarSeparator()); + + JButton clear = bf.createJToolbarButton("ClearButton"); + displayGroup.add(clear); + toolbar.add(clear); + + return toolbar; + } + + private class BooleanButtonGroup extends ButtonGroup implements + ItemListener { + + private List orElements = new ArrayList(); + + private AbstractButton orButton = new JToggleButton(); + + public BooleanButtonGroup() { + orButton.addItemListener(this); + add(orButton); + } + + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == orButton + && e.getStateChange() == ItemEvent.DESELECTED) { + for (Iterator i = orElements.iterator(); i.hasNext();) { + AbstractButton button = (AbstractButton) i.next(); + button.setSelected(false); + } + } else if (e.getStateChange() == ItemEvent.SELECTED) { + setSelected(((AbstractButton) e.getSource()).getModel(), true); + } + } + + public void addOr(AbstractButton button) { + orElements.add(button); + button.addItemListener(this); + } + + public void setSelected(ButtonModel model, boolean selected) { + for (Iterator i = orElements.iterator(); i.hasNext();) { + AbstractButton button = (AbstractButton) i.next(); + if (button.getModel() == model) { + orButton.setSelected(selected); + return; + } + } + super.setSelected(model, selected); + } + } + + private JPopupMenu createPopupMenu() { + JPopupMenu popupMenu = mf.createJMenu("PopupMenu").getPopupMenu(); + return popupMenu; + } + + protected static ErrorConsole instance; + + public static ErrorConsole getInstance() { + if (instance == null) { + instance = new ErrorConsole(); + } + return instance; + } + + public static JDialog createDialog(Component owner, String title) { + JDialog dialog = new JDialog(JOptionPane.getFrameForComponent(owner), + title); + dialog.getContentPane().add(getInstance(), BorderLayout.CENTER); + dialog.pack(); + return dialog; + } + + public void add(Throwable th, String uri, int type) { + ErrorButton errorButton = new ErrorButton(th, uri, type); + errorButton.setPopupMenu(popupMenu); + consoleList.add(errorButton); + errorGroup.add(errorButton); + } + + public void add(String elementName, String uri, int type) { + ErrorButton errorButton = new ErrorButton(elementName, uri, type); + errorButton.setPopupMenu(popupMenu); + consoleList.add(errorButton); + errorGroup.add(errorButton); + } + + /** + * The map that contains the actions + */ + protected Map actions = 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 { + return (Action) actions.get(key); + } + + /** + * The action associated with the 'All' button. + */ + protected class DisplayAllButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + displayType(-1, true); + } + } + + /** + * The action associated with the 'Errors' button. + */ + protected class DisplayErrorsButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + boolean selected = ((AbstractButton) evt.getSource()).isSelected(); + displayType(JOptionPane.ERROR_MESSAGE, selected); + } + } + + /** + * The action associated with the 'Warnings' button. + */ + protected class DisplayWarningsButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + boolean selected = ((AbstractButton) evt.getSource()).isSelected(); + displayType(JOptionPane.WARNING_MESSAGE, selected); + } + } + + /** + * The action associated with the 'Messages' button. + */ + protected class DisplayMessagesButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + boolean selected = ((AbstractButton) evt.getSource()).isSelected(); + displayType(JOptionPane.INFORMATION_MESSAGE, selected); + } + } + + /** + * The action associated with the 'Clear' button. + */ + protected class ClearButtonAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + clearType(displayType); + Enumeration buttons = errorGroup.getElements(); + while (buttons.hasMoreElements()) { + errorGroup.remove(buttons.nextElement()); + } + } + } + + /** + * The action associated with the 'First to Last Sort' menu item. + */ + protected class FirstLastSortMenuAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + if (sortOrderAscending) { + return; + } + reverseComponentOrder(consoleList); + sortOrderAscending = true; + } + } + + /** + * The action associated with the 'Last to First Sort' menu item. + */ + protected class LastFirstSortMenuAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + if (!sortOrderAscending) { + return; + } + reverseComponentOrder(consoleList); + sortOrderAscending = false; + } + } + + private void reverseComponentOrder(Container container) { + Component[] components = container.getComponents(); + consoleList.removeAll(); + for (int i = components.length - 1; i >= 0; i--) { + consoleList.add(components[i]); + } + } + + /** + * The action associated with the 'Copy' menu item. + */ + protected class CopyMenuAction extends AbstractAction { + + public void actionPerformed(ActionEvent evt) { + + } + } + + private void clearType(int type) { + Component[] components = consoleList.getComponents(); + for (int i = 0; i < components.length; i++) { + ErrorButton eb = (ErrorButton) components[i]; + if (type < 0 || eb.getType() == type) { + consoleList.remove(eb); + } + } + } + + private void displayType(int type, boolean visible) { + displayType = type; + Component[] components = consoleList.getComponents(); + for (Component c : components) { + ErrorButton eb = (ErrorButton) c; + if (type < 0 || (eb.getType() == type)) { + eb.setVisible(visible); + } + } + } + + private class ErrorButton extends JToggleButton { + + private int type; + + private JTextPane messageText; + + private Hyperlink link; + + private ImageIcon icon; + + private GradientPaint gradient; + + private JPanel details; + + private JPopupMenu popupMenu; + + private JTextArea detailText; + + private ItemListener il = new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + details.setVisible(true); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + details.setVisible(false); + } else { + return; + } + repaint(); + } + }; + + private MouseListener ml = new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + popupMenu.show(ErrorButton.this, e.getX(), e.getY()); + } + } + }; + + public ErrorButton(int type) { + this.type = type; + + setLayout(new BorderLayout(10, 10)); + setContentAreaFilled(false); + setBorderPainted(false); + setOpaque(false); + setFocusPainted(false); + setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + addItemListener(il); + addMouseListener(ml); + + String s; + switch (type) { + case JOptionPane.WARNING_MESSAGE: + s = resources.getString("ErrorButton.warning_icon"); + break; + case JOptionPane.INFORMATION_MESSAGE: + s = resources.getString("ErrorButton.message_icon"); + break; + case JOptionPane.ERROR_MESSAGE: + default: + s = resources.getString("ErrorButton.error_icon"); + break; + } + icon = new ImageIcon(getClass().getResource(s)); + add(new JLabel(icon), BorderLayout.WEST); + + Box centerPane = new Box(BoxLayout.Y_AXIS); + add(centerPane); + + messageText = new JTextPane(); + messageText.setEditable(false); + messageText.setOpaque(false); + messageText.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + ErrorButton.this.setSelected(true); + } + }); + centerPane.add(messageText, BorderLayout.NORTH); + + link = new Hyperlink("", new Hyperlink.HyperlinkCallback() { + public void linkClicked(HyperlinkEvent e) { + // new DocumentViewer(doc).setVisible(true); + } + }); + centerPane.add(link, BorderLayout.SOUTH); + + details = new JPanel(new BorderLayout()); + details.setOpaque(false); + details.setVisible(false); + add(details, BorderLayout.SOUTH); + + detailText = new JTextArea(); + detailText.setFont(new Font("Monospaced", Font.PLAIN, 12)); + detailText.setEditable(false); + detailText.setLineWrap(true); + final JScrollPane stackTraceScrollPane = new JScrollPane(detailText); + stackTraceScrollPane.getViewport().setPreferredSize( + new Dimension(300, 100)); + stackTraceScrollPane.setVisible(false); + final JButton btnShowDetails = new JButton("Show Details >>"); + btnShowDetails.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + boolean visible = stackTraceScrollPane.isVisible(); + stackTraceScrollPane.setVisible(!visible); + btnShowDetails.setText(visible ? "Show Details >>" + : "<< Hide Details"); + } + }); + JPanel button = new JPanel(new FlowLayout(FlowLayout.LEADING)); + button.setOpaque(false); + button.add(btnShowDetails); + details.add(button, BorderLayout.NORTH); + details.add(stackTraceScrollPane); + + Dimension dim = getPreferredSize(); + gradient = new GradientPaint(0, 0, new Color(217, 226, 234), 0, + 2 * dim.height, new Color(193, 207, 221)); + + } + + public ErrorButton(Throwable th, String uri, int type) { + this(type); + + messageText.setText(th.getMessage()); + + StringWriter sw = new StringWriter(); + th.printStackTrace(new PrintWriter(sw)); + sw.flush(); + detailText.setText(sw.toString()); + } + + public ErrorButton(String elementName, String uri, int type) { + this(type); + + messageText.setText("Document uri: " + uri); + String text; + try { + BufferedReader reader = new BufferedReader(new FileReader(ParsedURL.parseURL(uri).path)); + String line; + StringBuilder sb = new StringBuilder(); + while ((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + text = sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + text = "Error reading file: " + uri; + } + detailText.setText(text); + + highlightElement( + elementName, + uri, + detailText, + new DefaultHighlighter.DefaultHighlightPainter(Color.yellow)); + } + + private void highlightElement(String name, String uri, + JTextComponent textComponent, HighlightPainter hlPainter) { + Highlighter highlighter = textComponent.getHighlighter(); + highlighter.removeAllHighlights(); + + String text = textComponent.getText(); + for (int index = 0; (index = text.indexOf(name, index)) >= 0; index += name.length()) { + try { + highlighter.addHighlight(index, index + name.length(), hlPainter); + } catch (BadLocationException e) { + break; + } + } + } + + public void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + int width = getWidth(), height = getHeight(); + g2.setPaint(isSelected() ? gradient : Color.white); + g2.fillRect(0, 0, width, height); + g2.setColor(SystemColor.controlShadow); + g2.drawLine(0, height - 1, width, height - 1); + super.paintComponent(g); + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + repaint(); + } + + public JPopupMenu getPopupMenu() { + return popupMenu; + } + + public void setPopupMenu(JPopupMenu popupMenu) { + this.popupMenu = popupMenu; + } + } + + private static Locator searchLoc; + + public static void main(String[] args) throws Exception { + ErrorConsole ec = ErrorConsole.getInstance(); + String uri = new File(System.getProperty("user.home") + + "/Desktop/batikws/xml-batik/rect.svg").toURI().toString(); + final Locator locator = new LocatorImpl(); + SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(null) { + public void startElement(String uri, + String localName, + String rawName, + Attributes attributes) throws SAXException { + super.startElement(uri, localName, rawName, attributes); + + System.out.println(localName); + if (currentNode.getNodeName().equals("desc")) { + searchLoc = new LocatorImpl(locator); + } + } + }; + f.setDocumentLocator(locator); + Document doc = f.createDocument(uri); + if (searchLoc != null) { + System.out.println(searchLoc.getLineNumber() + ", " + searchLoc.getColumnNumber()); + } + //ec.add(doc.getChildNodes(), uri, JOptionPane.ERROR_MESSAGE); + ec.add(new Exception("Test Exception"), null, + JOptionPane.WARNING_MESSAGE); + ec.add(new Exception("Test Exception"), null, + JOptionPane.INFORMATION_MESSAGE); + JDialog d = ErrorConsole.createDialog(null, "Error Console 0.1"); + d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + d.setVisible(true); + } +}