001: /*
002: * Chunk.java - A syntax token with extra information required for painting it
003: * on screen
004: * :tabSize=8:indentSize=8:noTabs=false:
005: * :folding=explicit:collapseFolds=1:
006: *
007: * Copyright (C) 2001, 2002 Slava Pestov
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; either version 2
012: * of the License, or any later version.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: * GNU General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with this program; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
022: */
023:
024: package org.gjt.sp.jedit.syntax;
025:
026: //{{{ Imports
027: import javax.swing.text.*;
028: import java.awt.font.*;
029: import java.awt.geom.*;
030: import java.awt.*;
031:
032: import org.gjt.sp.jedit.Debug;
033:
034: //}}}
035:
036: /**
037: * A syntax token with extra information required for painting it
038: * on screen.
039: * @since jEdit 4.1pre1
040: */
041: public class Chunk extends Token {
042: //{{{ paintChunkList() method
043: /**
044: * Paints a chunk list.
045: * @param chunks The chunk list
046: * @param gfx The graphics context
047: * @param x The x co-ordinate
048: * @param y The y co-ordinate
049: * @return The width of the painted text
050: * @since jEdit 4.2pre1
051: */
052: public static float paintChunkList(Chunk chunks, Graphics2D gfx,
053: float x, float y, boolean glyphVector) {
054: Rectangle clipRect = gfx.getClipBounds();
055:
056: float _x = 0.0f;
057:
058: while (chunks != null) {
059: // only paint visible chunks
060: if (x + _x + chunks.width > clipRect.x
061: && x + _x < clipRect.x + clipRect.width) {
062: // Useful for debugging purposes
063: if (Debug.CHUNK_PAINT_DEBUG) {
064: gfx.draw(new Rectangle2D.Float(x + _x, y - 10,
065: chunks.width, 10));
066: }
067:
068: if (chunks.accessable && chunks.visible) {
069: gfx.setFont(chunks.style.getFont());
070: gfx.setColor(chunks.style.getForegroundColor());
071:
072: if (glyphVector && chunks.gv != null)
073: gfx.drawGlyphVector(chunks.gv, x + _x, y);
074: else if (chunks.str != null) {
075: gfx.drawString(chunks.str, (int) (x + _x),
076: (int) y);
077: }
078: }
079: }
080:
081: _x += chunks.width;
082: chunks = (Chunk) chunks.next;
083: }
084:
085: return _x;
086: } //}}}
087:
088: //{{{ paintChunkBackgrounds() method
089: /**
090: * Paints the background highlights of a chunk list.
091: * @param chunks The chunk list
092: * @param gfx The graphics context
093: * @param x The x co-ordinate
094: * @param y The y co-ordinate
095: * @return The width of the painted backgrounds
096: * @since jEdit 4.2pre1
097: */
098: public static float paintChunkBackgrounds(Chunk chunks,
099: Graphics2D gfx, float x, float y) {
100: Rectangle clipRect = gfx.getClipBounds();
101:
102: float _x = 0.0f;
103:
104: FontMetrics forBackground = gfx.getFontMetrics();
105:
106: int ascent = forBackground.getAscent();
107: int height = forBackground.getHeight();
108:
109: while (chunks != null) {
110: // only paint visible chunks
111: if (x + _x + chunks.width > clipRect.x
112: && x + _x < clipRect.x + clipRect.width) {
113: if (chunks.accessable) {
114: //{{{ Paint token background color if necessary
115: Color bgColor = chunks.background;
116: if (bgColor != null) {
117: gfx.setColor(bgColor);
118:
119: gfx.fill(new Rectangle2D.Float(x + _x, y
120: - ascent, _x + chunks.width - _x,
121: height));
122: } //}}}
123: }
124: }
125:
126: _x += chunks.width;
127: chunks = (Chunk) chunks.next;
128: }
129:
130: return _x;
131: } //}}}
132:
133: //{{{ offsetToX() method
134: /**
135: * Converts an offset in a chunk list into an x co-ordinate.
136: * @param chunks The chunk list
137: * @param offset The offset
138: * @since jEdit 4.1pre1
139: */
140: public static float offsetToX(Chunk chunks, int offset) {
141: if (chunks != null && offset < chunks.offset) {
142: throw new ArrayIndexOutOfBoundsException(offset + " < "
143: + chunks.offset);
144: }
145:
146: float x = 0.0f;
147:
148: while (chunks != null) {
149: if (chunks.accessable
150: && offset < chunks.offset + chunks.length)
151: return x + chunks.offsetToX(offset - chunks.offset);
152:
153: x += chunks.width;
154: chunks = (Chunk) chunks.next;
155: }
156:
157: return x;
158: } //}}}
159:
160: //{{{ xToOffset() method
161: /**
162: * Converts an x co-ordinate in a chunk list into an offset.
163: * @param chunks The chunk list
164: * @param x The x co-ordinate
165: * @param round Round up to next letter if past the middle of a letter?
166: * @return The offset within the line, or -1 if the x co-ordinate is too
167: * far to the right
168: * @since jEdit 4.1pre1
169: */
170: public static int xToOffset(Chunk chunks, float x, boolean round) {
171: float _x = 0.0f;
172:
173: while (chunks != null) {
174: if (chunks.accessable && x < _x + chunks.width)
175: return chunks.xToOffset(x - _x, round);
176:
177: _x += chunks.width;
178: chunks = (Chunk) chunks.next;
179: }
180:
181: return -1;
182: } //}}}
183:
184: //{{{ Instance variables
185: public boolean accessable;
186: public boolean visible;
187: public boolean initialized;
188:
189: // set up after init()
190: public SyntaxStyle style;
191: // this is either style.getBackgroundColor() or
192: // styles[defaultID].getBackgroundColor()
193: public Color background;
194: public float width;
195: public GlyphVector gv;
196: public String str;
197:
198: //}}}
199:
200: //{{{ Chunk constructor
201: public Chunk(float width, int offset, ParserRuleSet rules) {
202: super (Token.NULL, offset, 0, rules);
203: this .width = width;
204: } //}}}
205:
206: //{{{ Chunk constructor
207: public Chunk(byte id, int offset, int length, ParserRuleSet rules,
208: SyntaxStyle[] styles, byte defaultID) {
209: super (id, offset, length, rules);
210: accessable = true;
211: style = styles[id];
212: background = style.getBackgroundColor();
213: if (background == null)
214: background = styles[defaultID].getBackgroundColor();
215: } //}}}
216:
217: //{{{ getPositions() method
218: public final float[] getPositions() {
219: if (gv == null)
220: return null;
221:
222: if (positions == null)
223: positions = gv.getGlyphPositions(0, length, null);
224:
225: return positions;
226: } //}}}
227:
228: //{{{ offsetToX() method
229: public final float offsetToX(int offset) {
230: if (!visible)
231: return 0.0f;
232: else
233: return getPositions()[offset * 2];
234: } //}}}
235:
236: //{{{ xToOffset() method
237: public final int xToOffset(float x, boolean round) {
238: if (!visible) {
239: if (round && width - x < x)
240: return offset + length;
241: else
242: return offset;
243: } else {
244: float[] pos = getPositions();
245:
246: for (int i = 0; i < length; i++) {
247: float glyphX = pos[i * 2];
248: float nextX = (i == length - 1 ? width : pos[i * 2 + 2]);
249:
250: if (nextX > x) {
251: if (!round || nextX - x > x - glyphX)
252: return offset + i;
253: else
254: return offset + i + 1;
255: }
256: }
257: }
258:
259: // wtf?
260: return -1;
261: } //}}}
262:
263: //{{{ init() method
264: public void init(Segment seg, TabExpander expander, float x,
265: FontRenderContext fontRenderContext) {
266: initialized = true;
267:
268: if (!accessable) {
269: // do nothing
270: } else if (length == 1
271: && seg.array[seg.offset + offset] == '\t') {
272: visible = false;
273: float newX = expander.nextTabStop(x, offset + length);
274: width = newX - x;
275: } else {
276: visible = true;
277:
278: str = new String(seg.array, seg.offset + offset, length);
279:
280: Rectangle2D logicalBounds;
281: gv = style.getFont().createGlyphVector(fontRenderContext,
282: str);
283: logicalBounds = gv.getLogicalBounds();
284:
285: width = (float) logicalBounds.getWidth();
286: }
287: } //}}}
288:
289: //{{{ Private members
290: private float[] positions;
291: //}}}
292: }
|