source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rtextarea/ChangeableHighlightPainter.java@ 25584

Last change on this file since 25584 was 25584, checked in by davidb, 12 years ago

Initial cut an a text edit area for GLI that supports color syntax highlighting

File size: 11.2 KB
Line 
1/*
2 * 11/10/2004
3 *
4 * ChangableHighlightPainter.java - A highlight painter whose color you can
5 * change.
6 *
7 * This library is distributed under a modified BSD license. See the included
8 * RSyntaxTextArea.License.txt file for details.
9 */
10package org.fife.ui.rtextarea;
11
12import java.awt.AlphaComposite;
13import java.awt.Color;
14import java.awt.Composite;
15import java.awt.Graphics;
16import java.awt.Graphics2D;
17import java.awt.Paint;
18import java.awt.Rectangle;
19import java.awt.Shape;
20import java.awt.SystemColor;
21import java.io.IOException;
22import java.io.ObjectInputStream;
23import java.io.ObjectOutputStream;
24import java.io.Serializable;
25import javax.swing.plaf.TextUI;
26import javax.swing.text.BadLocationException;
27import javax.swing.text.JTextComponent;
28import javax.swing.text.LayeredHighlighter;
29import javax.swing.text.Position;
30import javax.swing.text.View;
31
32
33/**
34 * An extension of <code>LayerPainter</code> that allows the user to
35 * change several of its properties:
36 *
37 * <ul>
38 * <li>Its color/fill style (can use a <code>GradientPaint</code>, for
39 * example).</li>
40 * <li>Whether the edges of a painted highlight are rounded.</li>
41 * <li>Whether painted highlights have translucency.</li>
42 * </ul>
43 *
44 * @author Robert Futrell
45 * @version 0.6
46 */
47public class ChangeableHighlightPainter
48 extends LayeredHighlighter.LayerPainter implements Serializable {
49
50
51 /**
52 * The <code>Paint</code>/<code>Color</code> of this highlight.
53 */
54 private Paint paint;
55
56 /**
57 * Whether selections have rounded edges.
58 */
59 private boolean roundedEdges;
60
61 /**
62 * The alpha composite used to render with translucency.
63 */
64 private transient AlphaComposite alphaComposite;
65
66 /**
67 * The alpha value used in computing translucency. This should stay in the
68 * range <code>0.0f</code> (completely invisible) to <code>1.0f</code>
69 * (completely opaque).
70 */
71 private float alpha;
72
73
74 private static final int ARCWIDTH = 8;
75 private static final int ARCHEIGHT = 8;
76
77
78 /**
79 * Creates a new <code>ChangableHighlightPainter</code> that paints
80 * highlights with the text area's selection color (i.e., behaves exactly
81 * like
82 * <code>javax.swing.text.DefaultHighlighter.DefaultHighlightPainter
83 * </code>).
84 */
85 public ChangeableHighlightPainter() {
86 this(null);
87 }
88
89
90 /**
91 * Creates a new highlight painter using the specified <code>Paint</code>
92 * without rounded edges.
93 *
94 * @param paint The <code>Paint</code> (usually a
95 * <code>java.awt.Color</code>) with which to paint the
96 * highlights.
97 */
98 public ChangeableHighlightPainter(Paint paint) {
99 this(paint, false);
100 }
101
102
103 /**
104 * Creates a new highlight painter.
105 *
106 * @param paint The <code>Paint</code> (usually a
107 * <code>java.awt.Color</code>) with which to paint the
108 * highlights.
109 * @param rounded Whether to use rounded edges on the highlights.
110 */
111 public ChangeableHighlightPainter(Paint paint, boolean rounded) {
112 this(paint, rounded, 1.0f);
113 }
114
115
116 /**
117 * Creates a new highlight painter.
118 *
119 * @param paint The <code>Paint</code> (usually a
120 * <code>java.awt.Color</code>) with which to paint the
121 * highlights.
122 * @param rounded Whether to use rounded edges on the highlights.
123 * @param alpha The alpha value to use when painting highlights. This
124 * value should be in the range <code>0.0f</code> (completely
125 * transparent) through <code>1.0f</code> (opaque).
126 */
127 public ChangeableHighlightPainter(Paint paint, boolean rounded,
128 float alpha) {
129 setPaint(paint);
130 setRoundedEdges(rounded);
131 setAlpha(alpha);
132 }
133
134
135 /**
136 * Returns the alpha value used in computing the translucency of these
137 * highlights. A value of <code>1.0f</code> (the default) means that no
138 * translucency is used; there is no performance hit for this value. For
139 * all other values (<code>[0.0f..1.0f)</code>), there will be a
140 * performance hit.
141 *
142 * @return The alpha value.
143 * @see #setAlpha
144 */
145 public float getAlpha() {
146 return alpha;
147 }
148
149
150 /**
151 * Returns the alpha composite to use when rendering highlights with this
152 * painter.
153 *
154 * @return The alpha composite.
155 */
156 private AlphaComposite getAlphaComposite() {
157 if (alphaComposite==null)
158 alphaComposite = AlphaComposite.getInstance(
159 AlphaComposite.SRC_OVER, alpha);
160 return alphaComposite;
161 }
162
163
164 /**
165 * Returns the <code>Paint</code> (usually a <code>java.awt.Color</code>)
166 * being used to paint highlights.
167 *
168 * @return The <code>Paint</code>.
169 * @see #setPaint
170 */
171 public Paint getPaint() {
172 return paint;
173 }
174
175
176 /**
177 * Returns whether rounded edges are used when painting selections with
178 * this highlight painter.
179 *
180 * @return Whether rounded edges are used.
181 * @see #setRoundedEdges
182 */
183 public boolean getRoundedEdges() {
184 return roundedEdges;
185 }
186
187
188 /**
189 * Paints a highlight.
190 *
191 * @param g the graphics context
192 * @param offs0 the starting model offset >= 0
193 * @param offs1 the ending model offset >= offs1
194 * @param bounds the bounding box for the highlight
195 * @param c the editor
196 */
197 public void paint(Graphics g, int offs0, int offs1, Shape bounds,
198 JTextComponent c) {
199
200 Rectangle alloc = bounds.getBounds();
201
202 // Set up translucency if necessary.
203 Graphics2D g2d = (Graphics2D)g;
204 Composite originalComposite = null;
205 if (getAlpha()<1.0f) {
206 originalComposite = g2d.getComposite();
207 g2d.setComposite(getAlphaComposite());
208 }
209
210 try {
211
212 // Determine locations.
213 TextUI mapper = c.getUI();
214 Rectangle p0 = mapper.modelToView(c, offs0);
215 Rectangle p1 = mapper.modelToView(c, offs1);
216 Paint paint = getPaint();
217 if (paint==null)
218 g2d.setColor(c.getSelectionColor());
219 else
220 g2d.setPaint(paint);
221
222 // Entire highlight is on one line.
223 if (p0.y == p1.y) {
224 Rectangle r = p0.union(p1);
225 g2d.fillRect(r.x, r.y, r.width, r.height);
226 }
227
228 // Highlight spans lines.
229 else {
230 int p0ToMarginWidth = alloc.x + alloc.width - p0.x;
231 g2d.fillRect(p0.x, p0.y, p0ToMarginWidth, p0.height);
232 if ((p0.y + p0.height) != p1.y) {
233 g2d.fillRect(alloc.x, p0.y + p0.height, alloc.width,
234 p1.y - (p0.y + p0.height));
235 }
236 g2d.fillRect(alloc.x, p1.y, (p1.x - alloc.x), p1.height);
237 }
238
239 } catch (BadLocationException e) {
240 // Never happens.
241 e.printStackTrace();
242 } finally {
243 // Restore state from before translucency if necessary.
244 if (getAlpha()<1.0f)
245 g2d.setComposite(originalComposite);
246 }
247
248 }
249
250
251 /**
252 * Paints a portion of a highlight.
253 *
254 * @param g the graphics context
255 * @param offs0 the starting model offset >= 0
256 * @param offs1 the ending model offset >= offs1
257 * @param bounds the bounding box of the view, which is not
258 * necessarily the region to paint.
259 * @param c the editor
260 * @param view View painting for
261 * @return region drawing occurred in
262 */
263 public Shape paintLayer(Graphics g, int offs0, int offs1,
264 Shape bounds, JTextComponent c, View view) {
265
266
267 // Set up translucency if necessary.
268 Graphics2D g2d = (Graphics2D)g;
269 Composite originalComposite = null;
270 if (getAlpha()<1.0f) {
271 originalComposite = g2d.getComposite();
272 g2d.setComposite(getAlphaComposite());
273 }
274
275 // Set the color (our own if defined, otherwise text area's).
276 Paint paint = getPaint();
277 if (paint==null)
278 g2d.setColor(c.getSelectionColor());
279 else
280 g2d.setPaint(paint);
281
282 // Contained in view, can just use bounds.
283 if (offs0==view.getStartOffset() && offs1==view.getEndOffset()) {
284
285 Rectangle alloc;
286 if (bounds instanceof Rectangle)
287 alloc = (Rectangle)bounds;
288 else
289 alloc = bounds.getBounds();
290
291 g2d.fillRect(alloc.x, alloc.y, alloc.width, alloc.height);
292
293 // Restore state from before translucency if necessary.
294 if (getAlpha()<1.0f)
295 g2d.setComposite(originalComposite);
296
297 return alloc;
298
299 }
300
301 // Should only render part of View.
302 else {
303
304 try {
305
306 Shape shape = view.modelToView(offs0, Position.Bias.Forward,
307 offs1,Position.Bias.Backward,
308 bounds);
309 Rectangle r = (shape instanceof Rectangle) ?
310 (Rectangle)shape : shape.getBounds();
311 if (roundedEdges) {
312 g2d.fillRoundRect(r.x,r.y, r.width,r.height, ARCWIDTH,
313 ARCHEIGHT);
314 }
315 else {
316 g2d.fillRect(r.x, r.y, r.width, r.height);
317 }
318
319 // Restore state from before translucency if necessary.
320 if (getAlpha()<1.0f)
321 g2d.setComposite(originalComposite);
322
323 return r;
324
325 } catch (BadLocationException ble) {
326 ble.printStackTrace();
327 } finally {
328 // Restore state from before translucency if necessary.
329 if (getAlpha()<1.0f)
330 g2d.setComposite(originalComposite);
331 }
332
333 }
334
335 // Only if exception
336 return null;
337
338 }
339
340
341 /**
342 * Deserializes a painter.
343 *
344 * @param s The stream to read from.
345 * @throws ClassNotFoundException
346 * @throws IOException
347 */
348 private void readObject(ObjectInputStream s)
349 throws ClassNotFoundException, IOException {
350 s.defaultReadObject();
351 // We cheat and always serialize the Paint as a Color. "-1" means
352 // no Paint (i.e. use system selection color when painting).
353 int rgb = s.readInt();
354 paint = rgb==-1 ? null : new Color(rgb);
355 alphaComposite = null; // Keep FindBugs happy. This will get set later
356 }
357
358
359 /**
360 * Sets the alpha value used in rendering highlights. If this value is
361 * <code>1.0f</code> (the default), the highlights are rendered completely
362 * opaque. This behavior matches that of
363 * <code>DefaultHighlightPainter</code> and imposes no performance hit. If
364 * this value is below <code>1.0f</code>, it represents how opaque the
365 * highlight will be. There will be a small performance hit for values
366 * less than <code>1.0f</code>.
367 *
368 * @param alpha The new alpha value to use for transparency.
369 * @see #getAlpha
370 */
371 public void setAlpha(float alpha) {
372 this.alpha = alpha;
373 this.alpha = Math.max(alpha, 0.0f);
374 this.alpha = Math.min(1.0f, alpha);
375 alphaComposite = null; // So it is recreated with new alpha.
376 }
377
378
379 /**
380 * Sets the <code>Paint</code> (usually a <code>java.awt.Color</code>)
381 * used to paint this highlight.
382 *
383 * @param paint The new <code>Paint</code>.
384 * @see #getPaint
385 */
386 public void setPaint(Paint paint) {
387 this.paint = paint;
388 }
389
390
391 /**
392 * Sets whether rounded edges are used when painting this highlight.
393 *
394 * @param rounded Whether rounded edges should be used.
395 * @see #getRoundedEdges
396 */
397 public void setRoundedEdges(boolean rounded) {
398 roundedEdges = rounded;
399 }
400
401
402 /**
403 * Serializes this painter.
404 *
405 * @param s The stream to write to.
406 * @throws IOException If an IO error occurs.
407 */
408 private void writeObject(ObjectOutputStream s) throws IOException {
409 s.defaultWriteObject();
410 int rgb = -1; // No Paint -> Use JTextComponent's selection color
411 if (paint!=null) {
412 // NOTE: We cheat and always serialize the Paint as a Color.
413 // This is (practically) always the case anyway.
414 Color c = (paint instanceof Color) ? ((Color)paint) :
415 SystemColor.textHighlight;
416 rgb = c.getRGB();
417 }
418 s.writeInt(rgb);
419 }
420
421
422}
Note: See TracBrowser for help on using the repository browser.