KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > ate > swing > ATERenderingView


1 package org.antlr.works.ate.swing;
2
3 import org.antlr.works.ate.ATEPanel;
4 import org.antlr.works.ate.ATETextPane;
5 import org.antlr.works.ate.syntax.generic.ATESyntaxEngine;
6 import org.antlr.works.ate.syntax.misc.ATEToken;
7
8 import javax.swing.text.*;
9 import java.awt.*;
10 import java.util.List JavaDoc;
11
12 /*
13
14 [The "BSD licence"]
15 Copyright (c) 2005-2006 Jean Bovet
16 All rights reserved.
17
18 Redistribution and use in source and binary forms, with or without
19 modification, are permitted provided that the following conditions
20 are met:
21
22 1. Redistributions of source code must retain the above copyright
23 notice, this list of conditions and the following disclaimer.
24 2. Redistributions in binary form must reproduce the above copyright
25 notice, this list of conditions and the following disclaimer in the
26 documentation and/or other materials provided with the distribution.
27 3. The name of the author may not be used to endorse or promote products
28 derived from this software without specific prior written permission.
29
30 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41 */

42
43 /** This class is responsible to draw each line of the text and
44  * apply the appropriate attributes for each token.
45  *
46  * The idea behind this class has been inspired by the open-source project DrJava:
47  * http://drjava.sourceforge.net/
48  */

49 public class ATERenderingView extends PlainView {
50
51     public static final Color BACKGROUND_HIGHLIGHT_COLOR = new Color(1.0f, 1.0f, 0.5f, 0.3f);
52     public static Font DEFAULT_FONT;
53
54     protected ATEPanel textEditor;
55     protected ATETextPane textPane;
56     protected List JavaDoc<ATEToken> tokens;
57
58     protected DisplayOperation displayOp = new DisplayOperation();
59     protected ModelToViewOperation modelToViewOp = new ModelToViewOperation();
60     protected ViewToModel viewToModelOp = new ViewToModel();
61
62     protected Graphics currentGraphics;
63     protected Color savedColor;
64
65     public ATERenderingView(Element elem, ATEPanel textEditor) {
66         super(elem);
67         this.textEditor = textEditor;
68         this.textPane = textEditor.getTextPane();
69     }
70
71     /**
72      * Renders a line of text, suppressing whitespace at the end
73      * and expanding any tabs. This is implemented to make calls
74      * to the methods <code>drawUnselectedText</code> and
75      * <code>drawSelectedText</code> so that the way selected and
76      * unselected text are rendered can be customized.
77      *
78      * @param lineIndex the line to draw >= 0
79      * @param g the <code>Graphics</code> context
80      * @param x the starting X position >= 0
81      * @param y the starting Y position >= 0
82      * @see #drawUnselectedText
83      * @see #drawSelectedText
84      */

85     protected void drawLine(int lineIndex, Graphics g, int x, int y) {
86         // Highlight the background where the cursor is located
87
if(textPane.highlightCursorLine()) {
88             final Element line = getElement().getElement(lineIndex);
89             final int p0 = line.getStartOffset();
90             final int p1 = line.getEndOffset();
91
92             final int cursorPosition = textPane.getCaretPosition()+1;
93             if(cursorPosition > p0 && cursorPosition <= p1) {
94                 saveColor(g);
95                 g.setColor(BACKGROUND_HIGHLIGHT_COLOR);
96                 final int fontHeight = metrics.getHeight();
97                 g.fillRect(0, y-fontHeight+metrics.getDescent(), textPane.getWidth(), fontHeight);
98                 restore(g);
99             }
100         }
101
102         super.drawLine(lineIndex, g, x, y);
103     }
104
105     /**
106      * Provides a mapping from the document model coordinate space
107      * to the coordinate space of the view mapped to it.
108      *
109      */

110     public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
111         if(!textEditor.isSyntaxColoring()) {
112             return super.modelToView(pos, a, b);
113         }
114
115         // Fetch the line index closest to the given position
116
final Element element = getElement();
117         // Fetch the element for the current view
118
final int lineIndex = element.getElementIndex(pos);
119
120         // Fetch the element located at the beginning of the line
121
final Element line = element.getElement(lineIndex);
122         // Fetch the index of this element
123
final int p0 = line.getStartOffset();
124
125         // Fetch the rectangle of the line
126
final Rectangle posRect = lineToRect(a, lineIndex);
127
128         // Compute the location of the specified position using our renderText() method:
129
// it will take care of all characters attributes.
130
posRect.x += renderText(modelToViewOp, currentGraphics, 0, 0, p0, pos);
131
132         // Default width
133
posRect.width = 1;
134         // The height of a line is considered as fix
135
posRect.height = metrics.getHeight();
136
137         // Return the rectangle representing the specified position
138
return posRect;
139     }
140
141     /**
142      * Provides a mapping from the view coordinate space to the logical
143      * coordinate space of the model.
144      *
145      */

146     public int viewToModel(float fx, float fy, Shape a, Position.Bias[] biasReturn) {
147         if(!textEditor.isSyntaxColoring()) {
148             return super.viewToModel(fx, fy, a, biasReturn);
149         }
150
151         // Currently we don't compute the exact bias. We take the default one (forward).
152
biasReturn[0] = Position.Bias.Forward;
153
154         // Fetch the allocated region bounds in which to render
155
final Rectangle bounds = a.getBounds();
156         if(fy < bounds.y) {
157             // The y coordinate is below the allocated region in which to render.
158
// Return the start offset of the view.
159
return getStartOffset();
160         } else if(fy > bounds.y + bounds.height) {
161             // The y coordinate is above the allocated region in which to render.
162
// Return the end offset of the view.
163
return getEndOffset() - 1;
164         }
165
166         // Now compute the location of line the position corresponds to. It is possible
167
// that the position, while in the allocated region, can be located after the
168
// last line of the document.
169
Element element = getElement();
170
171         // Again consider each line as having a fixed height
172
final int lineIndex = Math.abs(((int)fy - bounds.y) / metrics.getHeight() );
173         if(lineIndex >= element.getElementCount()) {
174             // Past the last line of the document, return the last index of the view
175
return getEndOffset() - 1;
176         }
177
178         // OK. Now let's see if the x coordinate is past the left or right edge of the line
179
final Element line = element.getElement(lineIndex);
180         if(fx < bounds.x) {
181             // The x coordinate is past the left edge of the line. Return the start offset of the line.
182
return line.getStartOffset();
183         } else if(fx > bounds.x + bounds.width) {
184             // The x coordinate is past the right edge of the line. Return the end offset of the line.
185
return line.getEndOffset() - 1;
186         }
187
188         // Fine. Now let's compute the exact location by using our custom rendering method
189
// that will take care of each token attribute.
190
final int p0 = line.getStartOffset();
191         final int p1 = line.getEndOffset() - 1;
192         try {
193             viewToModelOp.setParameters((int)fx, p0);
194             renderText(viewToModelOp, currentGraphics, bounds.x, (int)fy, p0, p1);
195             return viewToModelOp.modelPos;
196         } catch (BadLocationException e) {
197             // What should we do? Currently nothing because it should not happen.
198
return -1;
199         }
200     }
201
202     /**
203      * Renders the given range in the model as normal unselected
204      * text. Uses the foreground or disabled color to render the text.
205      *
206      * @param g the graphics context
207      * @param x the starting X coordinate >= 0
208      * @param y the starting Y coordinate >= 0
209      * @param p0 the beginning position in the model >= 0
210      * @param p1 the ending position in the model >= 0
211      * @return the X location of the end of the range >= 0
212      * @throws javax.swing.text.BadLocationException
213      * if the range is invalid
214      */

215
216     protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException {
217         this.currentGraphics = g;
218         return renderText(displayOp, g, x, y, p0, p1);
219     }
220
221     /**
222      * Renders the given range in the model as selected text. This
223      * is implemented to render the text in the color specified in
224      * the hosting component. It assumes the highlighter will render
225      * the selected background.
226      *
227      * @param g the graphics context
228      * @param x the starting X coordinate >= 0
229      * @param y the starting Y coordinate >= 0
230      * @param p0 the beginning position in the model >= 0
231      * @param p1 the ending position in the model >= 0
232      * @return the location of the end of the range
233      * @throws javax.swing.text.BadLocationException
234      * if the range is invalid
235      */

236     protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException {
237         this.currentGraphics = g;
238         return renderText(displayOp, g, x, y, p0, p1);
239     }
240
241     /**
242      * This method renders the text using the token information to set up the display attribute
243      * of each token.
244      *
245      */

246     protected int renderText(TextOperation action, Graphics g, int x, int y, int p0, int p1) throws BadLocationException {
247         if(p0 == p1)
248             return x;
249
250         if(!textEditor.isSyntaxColoring()) {
251             return super.drawUnselectedText(g, x, y, p0, p1);
252         }
253
254         // Note: the tokens are not contiguous (e.g. white spaces are ignored)
255
final Document doc = getDocument();
256         final ATESyntaxEngine engine = textEditor.getParserEngine();
257         tokens = engine.getTokens();
258         int p = p0;
259         final int start = findStartingTokenIndex(p0, 0, tokens.size(), 0);
260         for (int i = start; i < tokens.size(); i++) {
261             ATEToken t = tokens.get(i);
262             AttributeSet attribute = engine.getAttributeForToken(t);
263             if(t.start >= p0 && t.start <= p1) {
264                 // Fill any non-contiguous token with default color
265
if(t.start > p) {
266                     x = action.renderTextPortion(g, x, y, p, t.start, p1, doc, null);
267                 }
268
269                 x = action.renderTextPortion(g, x, y, t.start, t.end, p1, doc, attribute);
270                 p = t.end;
271             } else if(t.end >= p0 && t.start < p0) {
272                 x = action.renderTextPortion(g, x, y, p0, t.end, p1, doc, attribute);
273                 p = t.end;
274             } else if(t.start > p1) {
275                 break;
276             }
277         }
278
279         // Fill any remaining range with default color
280
if(p < p1) {
281             x = action.renderTextPortion(g, x, y, p, p1, p1, doc, null);
282         }
283
284         return x;
285     }
286
287     /** This method finds the first token that is located in the line index p0
288      *
289      * @param p0
290      * @param low
291      * @param high
292      * @param candidate
293      * @return The index of the first token on line p0
294      */

295     private int findStartingTokenIndex(int p0, int low, int high, int candidate) {
296         if(Math.abs(high-low) <= 1)
297             return Math.min(candidate, low);
298
299         final int middle = low + (high-low) / 2;
300         final ATEToken t = tokens.get(middle);
301         if(p0 >= t.startLineIndex && p0 <= t.endLineIndex) {
302             return findStartingTokenIndex(p0, low, middle, middle);
303         } else {
304             if(t.startLineIndex < p0)
305                 return findStartingTokenIndex(p0, middle, high, candidate);
306             else
307                 return findStartingTokenIndex(p0, low, middle, candidate);
308         }
309     }
310
311     /** This method applies an AttributeSet to a Graphics context
312      *
313      * @param g The graphic context
314      * @param attribute The attribute to apply
315      */

316     protected void applyAttribute(Graphics g, AttributeSet attribute) {
317         if(attribute == null) {
318             g.setColor(Color.black);
319             g.setFont(DEFAULT_FONT);
320             return;
321         }
322
323         g.setFont(getFontForAttribute(attribute));
324
325         final Color c = StyleConstants.getForeground(attribute);
326         if(c == null)
327             g.setColor(Color.black);
328         else
329             g.setColor(c);
330     }
331
332     /** Return the font given the specified attributes
333      *
334      * @param attribute The font attributes
335      * @return The font given the specified attributes
336      */

337     protected Font getFontForAttribute(AttributeSet attribute) {
338         Font f = DEFAULT_FONT;
339         if(StyleConstants.isBold(attribute))
340             f = f.deriveFont(Font.BOLD);
341         if(StyleConstants.isItalic(attribute))
342             f = f.deriveFont(Font.ITALIC);
343         return f;
344     }
345
346     /** Save the current graphics color
347      *
348      * @param g The graphics
349      */

350     protected void saveColor(Graphics g) {
351         savedColor = g.getColor();
352     }
353
354     /** Restore the previously saved graphics color
355      *
356      * @param g The graphics
357      */

358     protected void restore(Graphics g) {
359         g.setColor(savedColor);
360     }
361
362     public interface TextOperation {
363         int renderTextPortion(Graphics g, int x, int y, int start, int end, int max, Document doc, AttributeSet attribute) throws BadLocationException;
364     }
365
366     public class DisplayOperation implements TextOperation {
367
368         public int renderTextPortion(Graphics g, int x, int y, int start, int end, int max, Document doc, AttributeSet attribute)
369                 throws BadLocationException
370         {
371             if(g == null)
372                 return 0;
373
374             int length = end - start;
375             if(start + length > max)
376                 length = max - start;
377
378             saveColor(g);
379             applyAttribute(g, attribute);
380             Segment text = getLineBuffer();
381             doc.getText(start, length, text);
382
383             x = Utilities.drawTabbedText(text, x, y, g, ATERenderingView.this, start);
384             restore(g);
385             return x;
386         }
387     }
388
389     public class ModelToViewOperation implements TextOperation {
390
391         public int renderTextPortion(Graphics g, int x, int y, int start, int end, int max, Document doc, AttributeSet attribute)
392                 throws BadLocationException
393         {
394             if(g == null)
395                 return 0;
396
397             int length = end - start;
398             if(start + length > max)
399                 length = max - start;
400
401             saveColor(g);
402             applyAttribute(g, attribute);
403             Segment text = getLineBuffer();
404             doc.getText(start, length, text);
405
406             x += Utilities.getTabbedTextWidth(text, g.getFontMetrics(), x, ATERenderingView.this, start);
407             restore(g);
408             return x;
409         }
410     }
411
412     public class ViewToModel implements TextOperation {
413
414         private int modelPos;
415         private int viewX;
416
417         public void setParameters(int viewX, int modelPos) {
418             this.viewX = viewX;
419             this.modelPos = modelPos;
420         }
421
422         public int renderTextPortion(Graphics g, int x, int y, int start, int end, int max, Document doc, AttributeSet attribute)
423                 throws BadLocationException
424         {
425             if(g == null)
426                 return 0;
427
428             int length = end - start;
429             if(start + length > max)
430                 length = max - start;
431
432             saveColor(g);
433             applyAttribute(g, attribute);
434             Segment text = getLineBuffer();
435             doc.getText(start, length, text);
436
437             modelPos += Utilities.getTabbedTextOffset(text, g.getFontMetrics(), x, viewX, ATERenderingView.this, start);
438             x +=Utilities.getTabbedTextWidth(text, g.getFontMetrics(), x, ATERenderingView.this, start);
439
440             restore(g);
441             return x;
442         }
443     }
444
445 }
446
Popular Tags