001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.swing.text;
024:
025: import java.awt.Dimension;
026: import java.awt.Font;
027: import java.awt.FontMetrics;
028: import java.awt.Graphics;
029: import java.io.IOException;
030: import java.io.Reader;
031:
032: import javax.swing.JTextPane;
033: import javax.swing.event.DocumentEvent;
034: import javax.swing.event.DocumentListener;
035: import javax.swing.text.BadLocationException;
036: import javax.swing.text.DefaultStyledDocument;
037: import javax.swing.text.Segment;
038: import javax.swing.text.Style;
039: import javax.swing.text.StyledDocument;
040:
041: import org.isqlviewer.sql.processor.SqlProcessor;
042: import org.isqlviewer.sql.processor.Token;
043: import org.isqlviewer.sql.processor.TokenType;
044:
045: /**
046: * Display text with syntax highlighting. Highlighting is done with full accuracy, using a given language scanner. Large
047: * amounts of re-highlighting are done in small bursts to make sure the user interface doesn't freeze.
048: */
049: public class SyntaxHighlighter extends JTextPane implements
050: DocumentListener {
051:
052: private static final long serialVersionUID = -5305919966146189023L;
053: private StyledDocument document;
054: private SqlProcessor scanner;
055: private SyntaxStylizer stylelizer;
056: private Segment text = new Segment();
057: private int firstRehighlightToken;
058: private int smallAmount = 100;
059: private int height, width;
060:
061: /**
062: * Create a graphics component which displays text with syntax highlighting. Provide a width and height, in
063: * characters, and a language scanner.
064: */
065: public SyntaxHighlighter(int height, int width, SqlProcessor scanner) {
066:
067: super (new DefaultStyledDocument());
068: document = (StyledDocument) getDocument();
069: this .height = height;
070: this .width = width;
071: this .scanner = scanner;
072: this .stylelizer = new SyntaxStylizer(this );
073: document.addDocumentListener(this );
074: }
075:
076: @Override
077: public void setFont(Font font) {
078:
079: int borderOfJTextPane = 3;
080: super .setFont(font);
081: FontMetrics metrics = getFontMetrics(font);
082: int paneWidth = width * metrics.charWidth('W') + 2
083: * borderOfJTextPane;
084: int paneHeight = height * metrics.getHeight() + 2
085: * borderOfJTextPane;
086: Dimension size = new Dimension(paneWidth, paneHeight);
087: setMinimumSize(size);
088: setPreferredSize(size);
089: invalidate();
090: }
091:
092: @Override
093: public void read(Reader in, Object desc) throws IOException {
094:
095: int oldLength = getDocument().getLength();
096: document.removeDocumentListener(this );
097: super .read(in, desc);
098: document = (StyledDocument) getDocument();
099: document.addDocumentListener(this );
100: int newLength = getDocument().getLength();
101: firstRehighlightToken = scanner.change(0, oldLength, newLength);
102: repaint();
103: }
104:
105: public void insertUpdate(DocumentEvent e) {
106:
107: int offset = e.getOffset();
108: int length = e.getLength();
109: firstRehighlightToken = scanner.change(offset, 0, length);
110: repaint();
111: }
112:
113: public void removeUpdate(DocumentEvent e) {
114:
115: int offset = e.getOffset();
116: int length = e.getLength();
117: firstRehighlightToken = scanner.change(offset, length, 0);
118: repaint();
119: }
120:
121: public void changedUpdate(DocumentEvent e) {
122:
123: }
124:
125: @Override
126: protected void paintComponent(Graphics g) {
127:
128: super .paintComponent(g);
129: int offset = scanner.position();
130: if (offset < 0)
131: return;
132:
133: int tokensToRedo = 0;
134: int amount = smallAmount;
135: while (tokensToRedo == 0 && offset >= 0) {
136: int length = document.getLength() - offset;
137: if (length > amount)
138: length = amount;
139: try {
140: document.getText(offset, length, text);
141: } catch (BadLocationException e) {
142: return;
143: }
144: tokensToRedo = scanner.scan(text.array, text.offset,
145: text.count);
146: offset = scanner.position();
147: amount = 2 * amount;
148: }
149: for (int i = 0; i < tokensToRedo; i++) {
150: Token t = scanner.getToken(firstRehighlightToken + i);
151: int length = t.symbol.name.length();
152: int type = t.symbol.type.ordinal();
153: if (type < 0) {
154: type = TokenType.UNRECOGNIZED.ordinal();
155: }
156: Style style = stylelizer.styleForWord(t.symbol.type);
157: if (style == null) {
158: style = stylelizer.styleForWord(TokenType.WHITESPACE);
159: }
160: document.setCharacterAttributes(t.position, length, style,
161: true);
162: }
163: firstRehighlightToken += tokensToRedo;
164: if (offset >= 0) {
165: repaint(2);
166: }
167: }
168: }
|