001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.awt.AlphaComposite;
017: import java.awt.Color;
018: import java.awt.Composite;
019: import java.awt.Font;
020: import java.awt.Graphics;
021: import java.awt.Graphics2D;
022: import java.awt.Rectangle;
023: import java.awt.Shape;
024:
025: import javax.swing.text.JTextComponent;
026:
027: /**
028: * Draw graphics functions as abstraction over various kinds of drawing. It's
029: * used for drawing into classic graphics, printing and measuring. Generally
030: * there are only the setters for some properties because the draw-engine
031: * doesn't retrieve the values that it previously set.
032: *
033: * @author Miloslav Metelka
034: * @version 1.00
035: */
036: interface DrawGraphics {
037:
038: /** Set foreground color */
039: public void setForeColor(Color foreColor);
040:
041: /** Set background color */
042: public void setBackColor(Color backColor);
043:
044: /**
045: * Inform the draw-graphics about the current background color of the
046: * component.
047: */
048: public void setDefaultBackColor(Color defaultBackColor);
049:
050: /** Set current font */
051: public void setFont(Font font);
052:
053: /** Set the current x-coordinate */
054: public void setX(int x);
055:
056: /** Set the current y-coordinate */
057: public void setY(int y);
058:
059: /** Set the height of the line. */
060: public void setLineHeight(int lineHeight);
061:
062: /** Set the ascent of the line. */
063: public void setLineAscent(int lineAscent);
064:
065: /**
066: * Get the AWT-graphics to determine whether this draws to a graphics. This
067: * is useful for fast line numbering and others.
068: */
069: public Graphics getGraphics();
070:
071: /**
072: * Whether draw graphics supports displaying of line numbers. If not line
073: * number displaying is not done.
074: */
075: public boolean supportsLineNumbers();
076:
077: /** Initialize this draw graphics before drawing */
078: public void init(DrawContext ctx);
079:
080: /**
081: * Called when whole drawing ends. Can be used to deallocate some resources
082: * etc.
083: */
084: public void finish();
085:
086: /**
087: * Fill rectangle at the current [x, y] with the current background color.
088: *
089: * @param width
090: * width of the rectangle to fill in points. The current
091: * x-coordinate must be increased by width automatically.
092: */
093: public void fillRect(int width);
094:
095: /**
096: * Draw characters from the specified offset in the buffer
097: *
098: * @param offset
099: * offset in the buffer for drawn text; if the text contains
100: * tabs, then offset is set to -1 and length contains the count
101: * of the space characters that correspond to the expanded tabs
102: * @param length
103: * length of the text being drawn
104: * @param width
105: * width of the text being drawn in points. The current
106: * x-coordinate must be increased by width automatically.
107: * @param strikeThroughColor
108: * color to be used for strike-through or null if the
109: * strike-through shouldn't be done.
110: * @param underlineColor
111: * color to be used for underlining or null if the underlining
112: * shouldn't be done.
113: */
114: public void drawChars(int offset, int length, int width,
115: Color strikeThroughColor, Color underlineColor);
116:
117: /**
118: * Draw the expanded tab characters.
119: *
120: * @param offset
121: * offset in the buffer where the tab characters start.
122: * @param length
123: * number of the tab characters
124: * @param spaceCount
125: * number of spaces that replace the tabs
126: * @param width
127: * width of the spaces in points. The current x-coordinate must
128: * be increased by width automatically.
129: * @param strikeThroughColor
130: * color to be used for strike-through or null if the
131: * strike-through shouldn't be done.
132: * @param underlineColor
133: * color to be used for underlining or null if the underlining
134: * shouldn't be done.
135: */
136: public void drawTabs(int offset, int length, int spaceCount,
137: int width, Color strikeThroughColor, Color underlineColor);
138:
139: /** Set character buffer from which the characters are drawn. */
140: public void setBuffer(char[] buffer);
141:
142: /**
143: * This method is called to notify this draw graphics in response from
144: * targetPos parameter passed to draw().
145: *
146: * @param offset
147: * position that was reached during the drawing.
148: * @param ch
149: * character at offset
150: * @param charWidth
151: * visual width of the character ch
152: * @param ctx
153: * current draw context containing
154: * @return whether the drawing should continue or not. If it returns false
155: * it's guaranteed that this method will not be called again and the
156: * whole draw() method will be stopped. <BR>
157: * The only exception is when the -1 is used as the target offset
158: * when draw() is called which means that every offset is a
159: * potential target offset and must be checked. In this case the
160: * binary search is used when finding the target offset inside
161: * painted fragment. That greatly improves performance for long
162: * fragments because the font metrics measurements are relatively
163: * expensive.
164: */
165: public boolean targetOffsetReached(int offset, char ch, int x,
166: int charWidth, DrawContext ctx);
167:
168: /** EOL encountered and should be handled. */
169: public void eol();
170:
171: /**
172: * Abstract draw-graphics that maintains a fg and bg color, font, current x
173: * and y coordinates.
174: */
175: static abstract class AbstractDG implements DrawGraphics {
176:
177: /** Current foreground color */
178: Color foreColor;
179:
180: /** Current background color */
181: Color backColor;
182:
183: /** Default background color */
184: Color defaultBackColor;
185:
186: /** Current font */
187: Font font;
188:
189: /** Character buffer from which the data are drawn */
190: char[] buffer;
191:
192: /** Current x-coordinate */
193: int x;
194:
195: /** Current y-coordinate */
196: int y;
197:
198: /** Height of the line being drawn */
199: int lineHeight;
200:
201: /** Ascent of the line being drawn */
202: int lineAscent;
203:
204: public Color getForeColor() {
205: return foreColor;
206: }
207:
208: public void setForeColor(Color foreColor) {
209: this .foreColor = foreColor;
210: }
211:
212: public Color getBackColor() {
213: return backColor;
214: }
215:
216: public void setBackColor(Color backColor) {
217: this .backColor = backColor;
218: }
219:
220: public Color getDefaultBackColor() {
221: return defaultBackColor;
222: }
223:
224: public void setDefaultBackColor(Color defaultBackColor) {
225: this .defaultBackColor = defaultBackColor;
226: }
227:
228: public Font getFont() {
229: return font;
230: }
231:
232: public void setFont(Font font) {
233: this .font = font;
234: }
235:
236: public int getX() {
237: return x;
238: }
239:
240: public void setX(int x) {
241: this .x = x;
242: }
243:
244: public int getY() {
245: return y;
246: }
247:
248: public void setY(int y) {
249: this .y = y;
250: }
251:
252: public int getLineHeight() {
253: return lineHeight;
254: }
255:
256: public void setLineHeight(int lineHeight) {
257: this .lineHeight = lineHeight;
258: }
259:
260: public int getLineAscent() {
261: return lineAscent;
262: }
263:
264: public void setLineAscent(int lineAscent) {
265: this .lineAscent = lineAscent;
266: }
267:
268: public char[] getBuffer() {
269: return buffer;
270: }
271:
272: public void setBuffer(char[] buffer) {
273: this .buffer = buffer;
274: }
275:
276: public void drawChars(int offset, int length, int width,
277: Color strikeThroughColor, Color underlineColor) {
278: x += width;
279: }
280:
281: public void drawTabs(int offset, int length, int spaceCount,
282: int width, Color strikeThroughColor,
283: Color underlineColor) {
284: x += width;
285: }
286:
287: }
288:
289: static class SimpleDG extends AbstractDG {
290:
291: public Graphics getGraphics() {
292: return null;
293: }
294:
295: public boolean supportsLineNumbers() {
296: return false;
297: }
298:
299: public void init(DrawContext ctx) {
300: }
301:
302: public void finish() {
303: }
304:
305: public void fillRect(int width) {
306: }
307:
308: public boolean targetOffsetReached(int offset, char ch, int x,
309: int charWidth, DrawContext ctx) {
310: return true; // shouldn't reach this place
311: }
312:
313: public void eol() {
314: }
315:
316: }
317:
318: /**
319: * Implementation of DrawGraphics to delegate to some Graphics. It optimizes
320: * the drawing by joining together the pieces of the text drawn with the
321: * same font and fg/bg color.
322: */
323: static final class GraphicsDG extends SimpleDG {
324:
325: private Graphics graphics;
326:
327: /** Current graphics color */
328: private Color gColor;
329:
330: /** Current graphics font */
331: private Font gFont;
332:
333: /**
334: * Start of the chars that were not drawn yet. It can be -1 to indicate
335: * the buffered characters were just flushed.
336: */
337: private int startOffset = -1;
338:
339: /** End of the chars that were not drawn yet */
340: private int endOffset;
341:
342: /** X coordinate where the drawing of chars should occur */
343: private int startX;
344:
345: /** Y coordinate where the drawing of chars should occur */
346: private int startY;
347:
348: private int width;
349:
350: private Color strikeThroughColor;
351:
352: private Color underlineColor;
353:
354: /** Alpha used for drawing the glyphs on the background */
355: private AlphaComposite alpha = null;
356:
357: /**
358: * Access to annotations for this document which will be drawn on the
359: * background
360: */
361: private Annotations annos = null;
362:
363: GraphicsDG(Graphics graphics) {
364: this .graphics = graphics;
365: }
366:
367: public void setForeColor(Color foreColor) {
368: if (!foreColor.equals(this .foreColor)) {
369: flush();
370: this .foreColor = foreColor;
371: }
372: }
373:
374: public void setBackColor(Color backColor) {
375: if (!backColor.equals(this .backColor)) {
376: flush();
377: this .backColor = backColor;
378: }
379: }
380:
381: public void setFont(Font font) {
382: if (!font.equals(this .font)) {
383: flush();
384: this .font = font;
385: }
386: }
387:
388: public void setX(int x) {
389: if (x != this .x) {
390: flush();
391: this .x = x;
392: }
393: }
394:
395: public void setY(int y) {
396: if (y != this .y) {
397: flush();
398: this .y = y;
399: }
400: }
401:
402: public void init(DrawContext ctx) {
403: JTextComponent c = ctx.getEditorUI().getComponent();
404: gColor = graphics.getColor();
405: gFont = graphics.getFont();
406: // initialize reference to annotations
407: annos = ctx.getEditorUI().getDocument().getAnnotations();
408: }
409:
410: public void finish() {
411: flush();
412: }
413:
414: private void flush() {
415: if (startOffset < 0) {
416: return;
417: }
418:
419: if (startOffset == endOffset) {
420: startOffset = -1;
421: return;
422: }
423:
424: // First possibly fill the rectangle
425: fillRectImpl(startX, startY, x - startX);
426:
427: if (AnnotationTypes.getTypes().isBackgroundDrawing()
428: .booleanValue()) {
429:
430: if (alpha == null)
431: alpha = AlphaComposite.getInstance(
432: AlphaComposite.SRC_OVER, AnnotationTypes
433: .getTypes()
434: .getBackgroundGlyphAlpha()
435: .intValue() / 100f);
436:
437: AnnotationDesc[] annosArray = annos
438: .getPasiveAnnotations((int) ((float) startY / (float) lineHeight));
439: int glyphX = 2;
440: if (annosArray != null) {
441: Graphics2D g2d = (Graphics2D) graphics;
442:
443: Shape shape = graphics.getClip();
444:
445: // set alpha composite
446: Composite origin = g2d.getComposite();
447: g2d.setComposite(alpha);
448:
449: // clip the drawing area
450: Rectangle r = new Rectangle(startX, startY, x
451: - startX, lineHeight);
452: r = r.intersection(shape.getBounds());
453: graphics.setClip(r);
454:
455: for (int i = 0; i < annosArray.length; i++) {
456: g2d.drawImage(annosArray[i].getGlyph(), glyphX,
457: startY, null);
458: glyphX += annosArray[i].getGlyph().getWidth(
459: null) + 1;
460: }
461:
462: // restore original clip region
463: graphics.setClip(shape);
464:
465: // restore original ocmposite
466: g2d.setComposite(origin);
467: }
468: }
469:
470: // Check whether the graphics uses right color
471: if (foreColor != gColor) {
472: graphics.setColor(foreColor);
473: gColor = foreColor;
474: }
475: // Check whether the graphics uses right font
476: if (font != gFont) {
477: graphics.setFont(font);
478: gFont = font;
479: }
480:
481: graphics.drawChars(buffer, startOffset, endOffset
482: - startOffset, startX, startY + lineAscent);
483:
484: if (strikeThroughColor != null) { // draw strike-through
485: FontMetricsCache.Info fmcInfo = FontMetricsCache
486: .getInfo(font);
487: if (strikeThroughColor != gColor) {
488: graphics.setColor(strikeThroughColor);
489: gColor = strikeThroughColor;
490: }
491: graphics
492: .fillRect(
493: startX,
494: startY
495: + (int) (fmcInfo
496: .getStrikethroughOffset(graphics) + 0.5),
497: x - startX,
498: (int) (fmcInfo
499: .getStrikethroughThickness(graphics) + 0.5));
500: }
501:
502: if (underlineColor != null) { // draw underline
503: FontMetricsCache.Info fmcInfo = FontMetricsCache
504: .getInfo(font);
505: if (underlineColor != gColor) {
506: graphics.setColor(underlineColor);
507: gColor = underlineColor;
508: }
509: // Add one pixel to the underline offset
510: graphics
511: .fillRect(
512: startX,
513: startY
514: + (int) (fmcInfo
515: .getUnderlineOffset(graphics) + 1.5),
516: x - startX,
517: (int) (fmcInfo
518: .getUnderlineThickness(graphics) + 0.5));
519: }
520:
521: startOffset = -1; // signal no characters to draw
522: }
523:
524: public Graphics getGraphics() {
525: return graphics;
526: }
527:
528: public boolean supportsLineNumbers() {
529: return true;
530: }
531:
532: public void fillRect(int width) {
533: fillRectImpl(x, y, width);
534: x += width;
535: }
536:
537: private void fillRectImpl(int rx, int ry, int width) {
538: if (width > 0) { // only for non-zero width
539: // only fill for different color than current background
540: if (!backColor.equals(defaultBackColor)) {
541: if (backColor != gColor) {
542: graphics.setColor(backColor);
543: gColor = backColor;
544: }
545:
546: graphics.fillRect(rx, ry, width, lineHeight);
547: }
548:
549: }
550: }
551:
552: public void drawChars(int offset, int length, int width,
553: Color strikeThroughColor, Color underlineColor) {
554: // Check if strike-through and underline colors match the current
555: // ones
556: if ((strikeThroughColor == null && this .strikeThroughColor != null)
557: || (strikeThroughColor != null && !strikeThroughColor
558: .equals(this .strikeThroughColor))
559: || (underlineColor == null && this .underlineColor != null)
560: || (underlineColor != null && !underlineColor
561: .equals(this .underlineColor))) {
562: flush();
563: }
564: this .strikeThroughColor = strikeThroughColor;
565: this .underlineColor = underlineColor;
566:
567: if (length >= 0) {
568: if (startOffset < 0) { // no token yet
569: startOffset = offset;
570: endOffset = offset + length;
571: this .startX = x;
572: this .startY = y;
573: this .width = width;
574:
575: } else { // already token before
576: endOffset += length;
577: }
578: }
579:
580: x += width;
581: }
582:
583: public void drawTabs(int offset, int length, int spaceCount,
584: int width, Color strikeThroughColor,
585: Color underlineColor) {
586: if (width > 0) {
587: flush();
588: fillRectImpl(x, y, width);
589: x += width;
590: }
591: }
592:
593: public void setBuffer(char[] buffer) {
594: flush();
595: this .buffer = buffer;
596: startOffset = -1;
597: }
598:
599: public void eol() {
600: flush();
601: }
602:
603: }
604:
605: static final class PrintDG extends SimpleDG {
606:
607: PrintContainer container;
608:
609: /** Whether there were some paints already on the line */
610: boolean lineInited;
611:
612: /**
613: * Construct the new print graphics
614: *
615: * @param container
616: * print container to which the tokens are added.
617: */
618: public PrintDG(PrintContainer container) {
619: this .container = container;
620: }
621:
622: public boolean supportsLineNumbers() {
623: return true;
624: }
625:
626: public void drawChars(int offset, int length, int width,
627: Color strikeThroughColor, Color underlineColor) {
628: if (length > 0) {
629: char[] chars = new char[length];
630: System.arraycopy(buffer, offset, chars, 0, length);
631: container.add(chars, font, foreColor, backColor);
632: }
633: }
634:
635: private void printSpaces(int spaceCount) {
636: char[] chars = new char[spaceCount];
637: System.arraycopy(Analyzer.getSpacesBuffer(spaceCount), 0,
638: chars, 0, spaceCount);
639: container.add(chars, font, foreColor, backColor);
640: }
641:
642: public void drawTabs(int offset, int length, int spaceCount,
643: int width, Color strikeThroughColor,
644: Color underlineColor) {
645: printSpaces(spaceCount);
646: }
647:
648: public void eol() {
649: if (!lineInited && container.initEmptyLines()) {
650: printSpaces(1);
651: }
652: container.eol();
653: lineInited = false; // signal that the next line is not inited yet
654: }
655:
656: }
657:
658: }
|