KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bluej > editor > moe > BlueJSyntaxView


1 // Copyright (c) 2000, 2005 BlueJ Group, Deakin University
2
//
3
// This software is made available under the terms of the "MIT License"
4
// A copy of this license is included with this source distribution
5
// in "license.txt" and is also available at:
6
// http://www.opensource.org/licenses/mit-license.html
7
// Any queries should be directed to Michael Kolling mik@bluej.org
8

9 package bluej.editor.moe;
10
11 /**
12  * MoeSyntaxView.java - adapted from
13  * SyntaxView.java - jEdit's own Swing view implementation
14  * to add Syntax highlighting to the BlueJ programming environment.
15  */

16
17 import javax.swing.text.*;
18
19 import java.awt.*;
20 import org.syntax.jedit.tokenmarker.*;
21 import org.syntax.jedit.*;
22
23 /**
24  * A Swing view implementation that colorizes lines of a
25  * SyntaxDocument using a TokenMarker.
26  *
27  * This class should not be used directly; a SyntaxEditorKit
28  * should be used instead.
29  *
30  * @author Slava Pestov
31  * @author Bruce Quig
32  * @author Michael Kolling
33  *
34  * @version $Id: BlueJSyntaxView.java,v 1.7 2005/05/02 03:23:32 davmac Exp $
35  */

36
37 public abstract class BlueJSyntaxView extends PlainView
38 {
39     /** width of tag area for setting breakpoints */
40     public static final short TAG_WIDTH = 14;
41     protected static final int BREAKPOINT_OFFSET = TAG_WIDTH + 2;
42
43     // private members
44
private Segment line;
45
46     private Font defaultFont;
47     // protected FontMetrics metrics; is inherited from PlainView
48
private Font lineNumberFont;
49     private Font smallLineNumberFont;
50     FontMetrics lineNumberMetrics;
51     private boolean initialised = false;
52     
53     /**
54      * Creates a new BlueJSyntaxView.
55      * @param elem The element
56      */

57     public BlueJSyntaxView(Element elem)
58     {
59         super(elem);
60         line = new Segment();
61     }
62
63     /**
64      * Paints the specified line.
65      *
66      * This method performs the following:
67      *
68      * - Gets the token marker and color table from the current document,
69      * typecast to a SyntaxDocument.
70      * - Tokenizes the required line by calling the
71      * markTokens() method of the token marker.
72      * - Paints each token, obtaining the color by looking up the
73      * the Token.id value in the color table.
74      *
75      * If either the document doesn't implement
76      * SyntaxDocument, or if the returned token marker is
77      * null, the line will be painted with no colorization.
78      *
79      * Currently, we assume that the whole document uses the same font.
80      * To support font changes, some of the code from "initilise" needs
81      * to be here to be done repeatedly for each line.
82      *
83      * @param lineIndex The line number
84      * @param g The graphics context
85      * @param x The x co-ordinate where the line should be painted
86      * @param y The y co-ordinate where the line should be painted
87      */

88     protected void drawLine(int lineIndex, Graphics g, int x, int y)
89     {
90         if(!initialised)
91             initialise(g);
92
93         SyntaxDocument document = (SyntaxDocument)getDocument();
94         TokenMarker tokenMarker = document.getTokenMarker();
95
96         Color def = getDefaultColor();
97
98         try {
99             Element lineElement = getElement().getElement(lineIndex);
100             int start = lineElement.getStartOffset();
101             int end = lineElement.getEndOffset();
102
103             document.getText(start, end - (start + 1), line);
104             g.setColor(def);
105             
106             paintTaggedLine(line, lineIndex, g, x, y, document, tokenMarker, def, lineElement);
107         }
108         catch(BadLocationException bl) {
109             // shouldn't happen
110
bl.printStackTrace();
111         }
112     }
113
114     /**
115      * Draw a line for this view, including the tag mark.
116      */

117     public abstract void paintTaggedLine(Segment line, int lineIndex, Graphics g, int x, int y,
118             SyntaxDocument document, TokenMarker tokenMarker, Color def, Element lineElement);
119
120     /**
121      * Draw the line number in front of the line
122      */

123     protected void drawLineNumber(Graphics g, int lineNumber, int x, int y)
124     {
125         g.setColor(Color.darkGray);
126
127         String JavaDoc number = Integer.toString(lineNumber);
128         int stringWidth = lineNumberMetrics.stringWidth(number);
129         int xoffset = BREAKPOINT_OFFSET - stringWidth - 4;
130
131         if(xoffset < -2) // if it doesn't fit, shift one pixel over.
132
xoffset++;
133
134         if(xoffset < -2) { // if it still doesn't fit...
135
g.setFont(smallLineNumberFont);
136             g.drawString(number, x-3, y);
137         }
138         else {
139             g.setFont(lineNumberFont);
140             g.drawString(number, x + xoffset, y);
141         }
142         g.setFont(defaultFont);
143     }
144
145     /**
146      * paints a line with syntax highlighting,
147      * redefined from DefaultSyntaxDocument.
148      *
149      */

150     protected void paintSyntaxLine(Segment line, int lineIndex, int x, int y,
151                                  Graphics g, SyntaxDocument document,
152                                  TokenMarker tokenMarker, Color def)
153     {
154         Color[] colors = document.getColors();
155         Token tokens = tokenMarker.markTokens(line, lineIndex);
156         int offset = 0;
157         for(;;) {
158             byte id = tokens.id;
159             if(id == Token.END)
160                 break;
161
162             int length = tokens.length;
163             Color color;
164             if(id == Token.NULL)
165                 color = def;
166             else {
167                 // check we are within the array bounds
168
// safeguard for updated syntax package
169
if(id < colors.length)
170                     color = colors[id];
171                 else color = def;
172             }
173             g.setColor(color == null ? def : color);
174
175             line.count = length;
176             x = Utilities.drawTabbedText(line,x,y,g,this,offset);
177             line.offset += length;
178             offset += length;
179
180             tokens = tokens.next;
181         }
182     }
183
184     
185     /**
186      * Check whether a given line is tagged with a given tag.
187      * @param line The line to check
188      * @param tag The name of the tag
189      * @return True, if the tag is set
190      */

191     protected final boolean hasTag(Element line, String JavaDoc tag)
192     {
193         return Boolean.TRUE.equals(line.getAttributes().getAttribute(tag));
194     }
195     
196     
197     /**
198      * Initialise some fields after we get a graphics context for the first time
199      */

200     private void initialise(Graphics g)
201     {
202         defaultFont = g.getFont();
203         lineNumberFont = defaultFont.deriveFont(9.0f);
204         smallLineNumberFont = defaultFont.deriveFont(7.0f);
205         Component c = getContainer();
206         lineNumberMetrics = c.getFontMetrics(lineNumberFont);
207         initialised = true;
208     }
209
210     /**
211      * Return default foreground colour
212      */

213     protected Color getDefaultColor()
214     {
215         return getContainer().getForeground();
216     }
217
218     /**
219      * Provides a mapping from the document model coordinate space
220      * to the coordinate space of the view mapped to it. This is a
221      * redefined method from PlainView that adds an offset for the
222      * view to allow for a breakpoint area in the associated editor.
223      *
224      * @param pos the position to convert >= 0
225      * @param a the allocated region to render into
226      * @return the bounding box of the given position
227      * @exception BadLocationException if the given position does not
228      * represent a valid location in the associated document
229      * @see View#modelToView
230      */

231     public Shape modelToView(int pos, Shape a, Position.Bias b)
232          throws BadLocationException
233     {
234         // line coordinates
235
Document doc = getDocument();
236         Element map = getElement();
237         int lineIndex = map.getElementIndex(pos);
238         Rectangle lineArea = lineToRect(a, lineIndex);
239
240         // determine span from the start of the line
241
int tabBase = lineArea.x + TAG_WIDTH + 2;
242
243         Element line = map.getElement(lineIndex);
244         int p0 = line.getStartOffset();
245         Segment buffer = getLineBuffer();
246         doc.getText(p0, pos - p0, buffer);
247         int xOffs = Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this, p0);
248
249         // fill in the results and return, include breakpoint area offset
250
lineArea.x += xOffs + (TAG_WIDTH + 2);
251         lineArea.width = 1;
252         lineArea.height = metrics.getHeight();
253         return lineArea;
254     }
255
256
257   /**
258      * Provides a mapping from the view coordinate space to the logical
259      * coordinate space of the model.
260      *
261      * @param fx the X coordinate >= 0
262      * @param fy the Y coordinate >= 0
263      * @param a the allocated region to render into
264      * @return the location within the model that best represents the
265      * given point in the view >= 0
266      * @see View#viewToModel
267      */

268     public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)
269     {
270         bias[0] = Position.Bias.Forward;
271
272         Rectangle alloc = a.getBounds();
273         Document doc = getDocument();
274         int x = (int) fx;
275         int y = (int) fy;
276         if (y < alloc.y) {
277             // above the area covered by this icon, so the the position
278
// is assumed to be the start of the coverage for this view.
279
return getStartOffset();
280         } else if (y > alloc.y + alloc.height) {
281             // below the area covered by this icon, so the the position
282
// is assumed to be the end of the coverage for this view.
283
return getEndOffset() - 1;
284         } else {
285             // positioned within the coverage of this view vertically,
286
// so we figure out which line the point corresponds to.
287
// if the line is greater than the number of lines contained, then
288
// simply use the last line as it represents the last possible place
289
// we can position to.
290
Element map = doc.getDefaultRootElement();
291             int lineIndex = Math.abs((y - alloc.y) / metrics.getHeight() );
292             if (lineIndex >= map.getElementCount()) {
293                 return getEndOffset() - 1;
294             }
295             Element line = map.getElement(lineIndex);
296             if (x < alloc.x) {
297                 // point is to the left of the line
298
return line.getStartOffset();
299             } else if (x > alloc.x + alloc.width) {
300                 // point is to the right of the line
301
return line.getEndOffset() - 1;
302             } else {
303                 // Determine the offset into the text
304
try {
305                     Segment buffer = getLineBuffer();
306                     int p0 = line.getStartOffset();
307                     int p1 = line.getEndOffset() - 1;
308                     doc.getText(p0, p1 - p0, buffer);
309                     // add Moe breakpoint offset area width
310
int tabBase = alloc.x + TAG_WIDTH + 2;
311                     int offs = p0 + Utilities.getTabbedTextOffset(buffer, metrics,
312                                                                   tabBase, x, this, p0);
313                     return offs;
314                 } catch (BadLocationException e) {
315                     // should not happen
316
return -1;
317                 }
318             }
319         }
320     }
321
322
323     // --- TabExpander interface methods -----------------------------------
324

325     /**
326      * Returns the next tab stop position after a given reference position.
327      * This implementation does not support things like centering so it
328      * ignores the tabOffset argument.
329      *
330      * @param x the current position >= 0
331      * @param tabOffset the position within the text stream
332      * that the tab occurred at >= 0.
333      * @return the tab stop, measured in points >= 0
334      */

335     public float nextTabStop(float x, int tabOffset) {
336         // calculate tabsize using fontwidth and tab spaces
337
int tabSize = getTabSize() * metrics.charWidth('m');
338         if (tabSize == 0) {
339             return x;
340         }
341         int tabStopNumber = (int)((x - BREAKPOINT_OFFSET) / tabSize) + 1;
342         return (tabStopNumber * tabSize) + BREAKPOINT_OFFSET + 2;
343     }
344
345
346    /**
347     * redefined from PlainView private method to allow for redefinition of
348     * modelToView method
349     */

350     public Rectangle lineToRect(Shape a, int line) {
351         Rectangle r = null;
352         if (metrics != null) {
353             Rectangle alloc = a.getBounds();
354             r = new Rectangle(alloc.x, alloc.y + (line * metrics.getHeight()),
355                               alloc.width, metrics.getHeight());
356         }
357         return r;
358     }
359 }
360
Popular Tags