KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > definitions > ColoringGlyphPainter


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.definitions;
35
36 import javax.swing.text.*;
37 import java.awt.*;
38 import javax.swing.event.DocumentEvent JavaDoc;
39 // TODO: Check synchronization.
40
import java.util.Vector JavaDoc;
41
42 import edu.rice.cs.drjava.DrJava;
43 import edu.rice.cs.drjava.model.*;
44 import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
45 import edu.rice.cs.drjava.config.OptionConstants;
46 import edu.rice.cs.drjava.config.OptionEvent;
47 import edu.rice.cs.drjava.config.OptionListener;
48 import edu.rice.cs.drjava.model.definitions.reducedmodel.*;
49
50
51 public class ColoringGlyphPainter extends GlyphView.GlyphPainter implements OptionConstants {
52   
53   public static Color COMMENTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_COMMENT_COLOR);
54   public static Color DOUBLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_DOUBLE_QUOTED_COLOR);
55   public static Color SINGLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_SINGLE_QUOTED_COLOR);
56   public static Color NORMAL_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR);
57   public static Color KEYWORD_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_KEYWORD_COLOR);
58   public static Color NUMBER_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NUMBER_COLOR);
59   public static Color TYPE_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_TYPE_COLOR);
60   public static Font MAIN_FONT = DrJava.getConfig().getSetting(FONT_MAIN);
61   
62   //Interactions only colors
63
public static Color INTERACTIONS_SYSTEM_ERR_COLOR = DrJava.getConfig().getSetting(SYSTEM_ERR_COLOR);
64   public static Color INTERACTIONS_SYSTEM_IN_COLOR = DrJava.getConfig().getSetting(SYSTEM_IN_COLOR);
65   public static Color INTERACTIONS_SYSTEM_OUT_COLOR = DrJava.getConfig().getSetting(SYSTEM_OUT_COLOR);
66   //Renamed as to avoid confusion with the one in option constants
67
public static Color ERROR_COLOR = DrJava.getConfig().getSetting(INTERACTIONS_ERROR_COLOR);
68   public static Color DEBUGGER_COLOR = DrJava.getConfig().getSetting(DEBUG_MESSAGE_COLOR);
69   
70   private boolean _listenersAttached;
71   private Runnable JavaDoc _lambdaRepaint;
72   private FontMetrics _metrics;
73   
74   public ColoringGlyphPainter(Runnable JavaDoc lambdaRepaint) {
75     _listenersAttached = false;
76     _lambdaRepaint = lambdaRepaint;
77     // _metrics is initialized by sync(), which thus must be called before any use of _metrics
78
}
79   
80   /** Paints the glyphs representing the given range. */
81   public void paint(GlyphView v, Graphics g, Shape a, int start, int end) {
82     
83     // If there's nothing to show, don't do anything!
84
// For some reason I don't understand we tend to get called sometimes to render a zero-length area.
85
if (start == end) return;
86     
87     sync(v);
88     
89     // Might be a PlainDocument (when AbstractDJPane is first constructed).
90
// See comments for DefinitionsEditorKit.createNewDocument() for details.
91
Document doc = v.getDocument();
92     AbstractDJDocument djdoc = null;
93     if (doc instanceof AbstractDJDocument)
94       djdoc = (AbstractDJDocument) doc;
95     else
96       return; // return if there is no AbstracDJDocument
97

98     Segment text;
99     TabExpander expander = v.getTabExpander();
100     Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
101     
102     // determine the x coordinate to render the glyphs
103
int x = alloc.x;
104     int p = v.getStartOffset();
105     if (p != start) {
106       text = v.getText(p, start);
107       int width = Utilities.getTabbedTextWidth(text, _metrics, x, expander, p);
108       x += width;
109     }
110     
111     // determine the y coordinate to render the glyphs
112
int y = alloc.y + _metrics.getHeight() - _metrics.getDescent();
113     
114     text = v.getText(start, end);
115     
116     Vector JavaDoc<HighlightStatus> stats = djdoc.getHighlightStatus(start, end);
117     if (stats.size() < 1) throw new RuntimeException JavaDoc("GetHighlightStatus returned nothing!");
118     try {
119       for (int i = 0; i < stats.size(); i++) {
120         HighlightStatus stat = stats.get(i);
121         int length = stat.getLength();
122         int location = stat.getLocation();
123         
124         
125         if((location < end) && ((location + length) > start)) {
126           
127           // Adjust the length and location to fit within the bounds of
128
// the element we're about to render
129
if (location < start) {
130             length -= (start-location);
131             location = start;
132           }
133           if ((location + length) > end) {
134             length = end - location;
135           }
136           
137           if (!(djdoc instanceof InteractionsDJDocument) || !((InteractionsDJDocument)djdoc).setColoring((start+end)/2,g))
138             setFormattingForState(g, stat.getState());
139           
140           djdoc.getText(location, length, text);
141           x = Utilities.drawTabbedText(text, x, y, g, v.getTabExpander(), location);
142         }
143       }
144     }
145     catch(BadLocationException ble) {
146       // don't continue rendering if such an exception is found
147
}
148   }
149   
150   /**
151    * Determine the span the glyphs given a start location
152    * (for tab expansion).
153    */

154   public float getSpan(GlyphView v, int start, int end,
155                        TabExpander e, float x) {
156     sync(v);
157     Segment text = v.getText(start, end);
158     int width = Utilities.getTabbedTextWidth(text, _metrics, (int) x, e, start);
159     return width;
160   }
161   
162   public float getHeight(GlyphView v) {
163     sync(v);
164     return _metrics.getHeight();
165   }
166   
167   /**
168    * Fetches the ascent above the baseline for the glyphs
169    * corresponding to the given range in the model.
170    */

171   public float getAscent(GlyphView v) {
172     sync(v);
173     return _metrics.getAscent();
174   }
175   
176   /**
177    * Fetches the descent below the baseline for the glyphs
178    * corresponding to the given range in the model.
179    */

180   public float getDescent(GlyphView v) {
181     sync(v);
182     return _metrics.getDescent();
183   }
184   
185   public Shape modelToView(GlyphView v, int pos, Position.Bias bias,
186                            Shape a) throws BadLocationException {
187     
188     sync(v);
189     Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
190     int start = v.getStartOffset();
191     int end = v.getEndOffset();
192     TabExpander expander = v.getTabExpander();
193     Segment text;
194     
195     if(pos == end) {
196       // The caller of this is left to right and borders a right to
197
// left view, return our end location.
198
return new Rectangle(alloc.x + alloc.width, alloc.y, 0,
199                            _metrics.getHeight());
200     }
201     if ((pos >= start) && (pos <= end)) {
202       // determine range to the left of the position
203
text = v.getText(start, pos);
204       int width = Utilities.getTabbedTextWidth(text, _metrics, alloc.x, expander, start);
205       return new Rectangle(alloc.x + width, alloc.y, 0, _metrics.getHeight());
206     }
207     throw new BadLocationException("modelToView - can't convert", end);
208   }
209   
210   /**
211    * Provides a mapping from the view coordinate space to the logical
212    * coordinate space of the model.
213    *
214    * @param v the view containing the view coordinates
215    * @param x the X coordinate
216    * @param y the Y coordinate
217    * @param a the allocated region to render into
218    * @param biasReturn always returns <code>Position.Bias.Forward</code>
219    * as the zero-th element of this array
220    * @return the location within the model that best represents the
221    * given point in the view
222    * @see View#viewToModel
223    */

224   public int viewToModel(GlyphView v, float x, float y, Shape a,
225                          Position.Bias[] biasReturn) {
226     sync(v);
227     Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
228     int start = v.getStartOffset();
229     int end = v.getEndOffset();
230     TabExpander expander = v.getTabExpander();
231     Segment text = v.getText(start, end);
232     
233     int offs = Utilities.getTabbedTextOffset(text, _metrics,
234                                              alloc.x, (int) x, expander, start);
235     int retValue = start + offs;
236     if(retValue == end) {
237       // No need to return backward bias as GlyphPainter1 is used for
238
// ltr text only.
239
retValue--;
240     }
241     biasReturn[0] = Position.Bias.Forward;
242     return retValue;
243   }
244
245   /**
246    * Determines the best location (in the model) to break the given view. This method attempts to break on a
247    * whitespace location. If a whitespace location can't be found, the nearest character location is returned.
248    *
249    * @param v The view
250    * @param start The location in the model where the fragment should start its representation >= 0
251    * @param x The graphic location along the axis that the broken view would occupy >= 0; this may be useful for
252    * things like tab calculations
253    * @param len Specifies the distance into the view where a potential break is desired >= 0
254    * @return The model location desired for a break
255    * @see View#breakView
256    */

257   public int getBoundedPosition(GlyphView v, int start, float x, float len) {
258     sync(v);
259     TabExpander expander = v.getTabExpander();
260     Segment s = v.getText(start, v.getEndOffset());
261     int index = Utilities.getTabbedTextOffset(s, _metrics, (int)x, (int)(x+len),
262                                               expander, start, false);
263     int end = start + index;
264     return end;
265   }
266   
267   void sync(GlyphView v) {
268     Font f = v.getFont();
269     if ((_metrics == null) || (! f.equals(_metrics.getFont()))) {
270       // fetch a new FontMetrics
271
Toolkit kit;
272       Component c = v.getContainer();
273       if (c != null) {
274         kit = c.getToolkit();
275       } else {
276         kit = Toolkit.getDefaultToolkit();
277       }
278       /* The deprecated method here is necessary to get a handle on a FontMetrics object. This is required by our
279        * dependence on the javax.swing.text.Utilities class, which does a lot of Java 1.1-style calculation (presumably
280        * these methods should be deprecated, too). The deprecated use can't be fixed without an in-depth understanding
281        * of fonts, glyphs, and font rendering. Where _metrics is currently used, the Font methods getLineMetrics,
282        * getStringBounds, getHeight, getAscent, and getDescent will probably be helpful.
283        */

284       @SuppressWarnings JavaDoc("deprecation") FontMetrics newMetrics = kit.getFontMetrics(f);
285       _metrics = newMetrics;
286     }
287     
288     Document doc = v.getDocument();
289     if (!_listenersAttached && (doc instanceof AbstractDJDocument)) {
290       attachOptionListeners((AbstractDJDocument)doc);
291     }
292   }
293   
294   /** Given a particular state, assign it a color.
295    * @param g Graphics object
296    * @param state a given state
297    */

298   private void setFormattingForState(Graphics g, int state) {
299     switch (state) {
300       case HighlightStatus.NORMAL:
301         g.setColor(NORMAL_COLOR);
302         break;
303       case HighlightStatus.COMMENTED:
304         g.setColor(COMMENTED_COLOR);
305         break;
306       case HighlightStatus.SINGLE_QUOTED:
307         g.setColor(SINGLE_QUOTED_COLOR);
308         break;
309       case HighlightStatus.DOUBLE_QUOTED:
310         g.setColor(DOUBLE_QUOTED_COLOR);
311         break;
312       case HighlightStatus.KEYWORD:
313         g.setColor(KEYWORD_COLOR);
314         break;
315       case HighlightStatus.NUMBER:
316         g.setColor(NUMBER_COLOR);
317         break;
318       case HighlightStatus.TYPE:
319         g.setColor(TYPE_COLOR);
320         break;
321       default:
322         throw new RuntimeException JavaDoc("Can't get color for invalid state: " + state);
323     }
324     g.setFont(MAIN_FONT);
325   }
326   
327   
328   /** Called when a change occurs.
329    * @param changes document changes
330    * @param a a Shape
331    * @param f a ViewFactory
332    */

333 // public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
334
// super.changedUpdate(changes, a, f);
335
// // Make sure we redraw since something changed in the formatting
336
// Container c = getContainer();
337
// if (c != null) c.repaint();
338
// }
339

340   private void attachOptionListeners(AbstractDJDocument doc) {
341     // Listen for updates to configurable colors
342
final ColorOptionListener col = new ColorOptionListener();
343     final FontOptionListener fol = new FontOptionListener();
344     
345     // delete the old color listeners, because they're hanging onto the wrong coloringview
346
// add color listeners to highlight keywords etc
347
DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_COMMENT_COLOR, col);
348     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_DOUBLE_QUOTED_COLOR, col);
349     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_SINGLE_QUOTED_COLOR, col);
350     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_NORMAL_COLOR, col);
351     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_KEYWORD_COLOR, col);
352     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_NUMBER_COLOR, col);
353     DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_TYPE_COLOR, col);
354     DrJava.getConfig().addOptionListener( OptionConstants.FONT_MAIN, fol);
355     
356     DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_ERR_COLOR, col);
357     DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_IN_COLOR, col);
358     DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_OUT_COLOR, col);
359     DrJava.getConfig().addOptionListener( OptionConstants.INTERACTIONS_ERROR_COLOR, col);
360     DrJava.getConfig().addOptionListener( OptionConstants.DEBUG_MESSAGE_COLOR, col);
361     
362     // The listeners that were added in the above lines need to be removed from
363
// the config framework when the document corresponding to this painter is
364
// kicked out of the DocumentCache. Otherwise, this painter will remain
365
// un-garbage-collected and unused.
366
if (doc instanceof DefinitionsDocument) {
367       // remove the listeners when the document closes
368
((DefinitionsDocument)doc).addDocumentClosedListener(new DocumentClosedListener() {
369         public void close() {
370           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_COMMENT_COLOR, col);
371           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_DOUBLE_QUOTED_COLOR, col);
372           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_SINGLE_QUOTED_COLOR, col);
373           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_NORMAL_COLOR, col);
374           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_KEYWORD_COLOR, col);
375           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_NUMBER_COLOR, col);
376           DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_TYPE_COLOR, col);
377           DrJava.getConfig().removeOptionListener( OptionConstants.FONT_MAIN, fol);
378           DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_ERR_COLOR, col);
379           DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_IN_COLOR, col);
380           DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_OUT_COLOR, col);
381           DrJava.getConfig().removeOptionListener( OptionConstants.INTERACTIONS_ERROR_COLOR, col);
382           DrJava.getConfig().removeOptionListener( OptionConstants.DEBUG_MESSAGE_COLOR, col);
383         }
384       });
385     }
386     _listenersAttached = true;
387   }
388   
389   /** Called when an OptionListener perceives a change in any of the colors */
390   public void updateColors() {
391     
392     COMMENTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_COMMENT_COLOR);
393     DOUBLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_DOUBLE_QUOTED_COLOR);
394     SINGLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_SINGLE_QUOTED_COLOR);
395     NORMAL_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR);
396     KEYWORD_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_KEYWORD_COLOR);
397     NUMBER_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NUMBER_COLOR);
398     TYPE_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_TYPE_COLOR);
399     
400     INTERACTIONS_SYSTEM_ERR_COLOR = DrJava.getConfig().getSetting(SYSTEM_ERR_COLOR);
401     INTERACTIONS_SYSTEM_IN_COLOR = DrJava.getConfig().getSetting(SYSTEM_IN_COLOR);
402     INTERACTIONS_SYSTEM_OUT_COLOR = DrJava.getConfig().getSetting(SYSTEM_OUT_COLOR);
403     ERROR_COLOR = DrJava.getConfig().getSetting(INTERACTIONS_ERROR_COLOR);
404     DEBUGGER_COLOR = DrJava.getConfig().getSetting(DEBUG_MESSAGE_COLOR);
405     
406     edu.rice.cs.util.swing.Utilities.invokeLater(_lambdaRepaint);
407   }
408   
409   /** The OptionListeners for DEFINITIONS COLORs */
410   private class ColorOptionListener implements OptionListener<Color> {
411     public void optionChanged(OptionEvent<Color> oce) { updateColors(); }
412   }
413   
414   private static class FontOptionListener implements OptionListener<Font> {
415     public void optionChanged(OptionEvent<Font> oce) {
416       MAIN_FONT = DrJava.getConfig().getSetting(FONT_MAIN);
417     }
418   }
419
420 }
421
Popular Tags