001: /*
002: * DisplayTokenHandler.java - converts tokens to chunks
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2003 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.syntax;
024:
025: //{{{ Imports
026: import org.gjt.sp.jedit.buffer.JEditBuffer;
027:
028: import javax.swing.text.*;
029: import java.awt.font.*;
030: import java.util.List;
031:
032: //}}}
033:
034: /**
035: * Creates {@link Chunk} objects that can be painted on screen.
036: * @version $Id: DisplayTokenHandler.java 9130 2007-03-10 21:02:10Z kpouer $
037: */
038: public class DisplayTokenHandler extends DefaultTokenHandler {
039: // don't have chunks longer than 100 characters to avoid slowing things down
040: public static final int MAX_CHUNK_LEN = 100;
041:
042: //{{{ init() method
043: /**
044: * Init some variables that will be used when marking tokens.
045: * This is called before {@link JEditBuffer#markTokens(int, TokenHandler)}
046: * to store some data that will be required and that we don't want
047: * to put in the parameters
048: *
049: * @param styles
050: * @param fontRenderContext
051: * @param expander
052: * @param out
053: * @param wrapMargin
054: */
055: public void init(SyntaxStyle[] styles,
056: FontRenderContext fontRenderContext, TabExpander expander,
057: List<Chunk> out, float wrapMargin) {
058: super .init();
059:
060: x = 0.0f;
061:
062: this .styles = styles;
063: this .fontRenderContext = fontRenderContext;
064: this .expander = expander;
065:
066: // SILLY: allow for anti-aliased characters' "fuzz"
067: if (wrapMargin != 0.0f)
068: this .wrapMargin = wrapMargin += 2.0f;
069: else
070: this .wrapMargin = 0.0f;
071:
072: this .out = out;
073:
074: seenNonWhitespace = false;
075: endX = endOfWhitespace = 0.0f;
076: end = null;
077: } //}}}
078:
079: //{{{ getChunkList() method
080: /**
081: * Returns the list of chunks.
082: * @since jEdit 4.1pre7
083: */
084: public List<Chunk> getChunkList() {
085: return out;
086: } //}}}
087:
088: //{{{ handleToken() method
089: /**
090: * Called by the token marker when a syntax token has been parsed.
091: * @param seg The segment containing the text
092: * @param id The token type (one of the constants in the
093: * {@link Token} class).
094: * @param offset The start offset of the token
095: * @param length The number of characters in the token
096: * @param context The line context
097: * @since jEdit 4.2pre3
098: */
099: public void handleToken(Segment seg, byte id, int offset,
100: int length, TokenMarker.LineContext context) {
101: if (id == Token.END) {
102: if (firstToken != null)
103: out.add(merge((Chunk) firstToken, seg));
104: return;
105: }
106:
107: for (int splitOffset = 0; splitOffset < length; splitOffset += MAX_CHUNK_LEN) {
108: int splitLength = Math.min(length - splitOffset,
109: MAX_CHUNK_LEN);
110: Chunk chunk = createChunk(id, offset + splitOffset,
111: splitLength, context);
112: addToken(chunk, context);
113:
114: if (wrapMargin != 0.0f) {
115: initChunk(chunk, seg);
116: x += chunk.width;
117:
118: if (Character.isWhitespace(seg.array[seg.offset
119: + chunk.offset])) {
120: if (seenNonWhitespace) {
121: end = lastToken;
122: endX = x;
123: } else
124: endOfWhitespace = x;
125: } else {
126: if (x > wrapMargin && end != null
127: && seenNonWhitespace) {
128: Chunk nextLine = new Chunk(endOfWhitespace,
129: end.offset + end.length,
130: getParserRuleSet(context));
131: initChunk(nextLine, seg);
132:
133: nextLine.next = end.next;
134: end.next = null;
135:
136: if (firstToken != null)
137: out.add(merge((Chunk) firstToken, seg));
138:
139: firstToken = nextLine;
140:
141: x = x - endX + endOfWhitespace;
142:
143: end = null;
144: endX = x;
145: }
146:
147: seenNonWhitespace = true;
148: }
149: }
150: }
151: } //}}}
152:
153: //{{{ Private members
154:
155: //{{{ Instance variables
156: private SyntaxStyle[] styles;
157: private FontRenderContext fontRenderContext;
158: private TabExpander expander;
159: private float x;
160:
161: private List<Chunk> out;
162: private float wrapMargin;
163: private float endX;
164: private Token end;
165:
166: private boolean seenNonWhitespace;
167: private float endOfWhitespace;
168:
169: //}}}
170:
171: //{{{ createChunk() method
172: private Chunk createChunk(byte id, int offset, int length,
173: TokenMarker.LineContext context) {
174: return new Chunk(id, offset, length, getParserRuleSet(context),
175: styles, context.rules.getDefault());
176: } //}}}
177:
178: //{{{ initChunk() method
179: protected void initChunk(Chunk chunk, Segment seg) {
180: chunk.init(seg, expander, x, fontRenderContext);
181: } //}}}
182:
183: //{{{ merge() method
184: private Chunk merge(Chunk first, Segment seg) {
185: if (first == null)
186: return null;
187:
188: Chunk chunk = first;
189: while (chunk.next != null) {
190: Chunk next = (Chunk) chunk.next;
191: if (canMerge(chunk, next, seg)) {
192: // in case already initialized; un-initialize it
193: chunk.initialized = false;
194: chunk.length += next.length;
195: chunk.width += next.width;
196: chunk.next = next.next;
197: } else {
198: if (!chunk.initialized) {
199: initChunk(chunk, seg);
200: if (wrapMargin == 0.0f)
201: x += chunk.width;
202: }
203: chunk = next;
204: }
205: }
206:
207: if (!chunk.initialized)
208: initChunk(chunk, seg);
209:
210: return first;
211: } //}}}
212:
213: //{{{ canMerge() method
214: private static boolean canMerge(Chunk c1, Chunk c2, Segment seg) {
215: if (!c1.accessable || !c2.accessable)
216: return false;
217:
218: char ch1 = seg.array[seg.offset + c1.offset];
219: char ch2 = seg.array[seg.offset + c2.offset];
220:
221: return ((c1.style == c2.style) && ch1 != '\t' && ch2 != '\t' && (c1.length
222: + c2.length <= MAX_CHUNK_LEN));
223: } //}}}
224:
225: //}}}
226: }
|