KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > DrawEngine


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor;
21
22 import java.awt.Graphics JavaDoc;
23 import java.awt.Graphics2D JavaDoc;
24 import java.awt.Font JavaDoc;
25 import java.awt.FontMetrics JavaDoc;
26 import java.awt.Color JavaDoc;
27 import java.awt.Component JavaDoc;
28 import java.awt.Rectangle JavaDoc;
29 import java.awt.Shape JavaDoc;
30 import java.awt.Insets JavaDoc;
31 import java.text.AttributedCharacterIterator JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Map JavaDoc;
35 import javax.swing.text.BadLocationException JavaDoc;
36 import javax.swing.text.Document JavaDoc;
37 import javax.swing.text.JTextComponent JavaDoc;
38 import javax.swing.text.Segment JavaDoc;
39 import javax.swing.text.View JavaDoc;
40
41 /**
42 * Class responsible for drawing the editor component.
43 *
44 * @author Miloslav Metelka
45 * @version 1.00
46 */

47 class DrawEngine {
48
49     /** Whether debug messages should be displayed */
50     private static final boolean debug
51         = Boolean.getBoolean("netbeans.debug.editor.draw"); // NOI18N
52
/** Whether debug messages for each token fragment should be displayed */
53     private static final boolean debugFragment
54         = Boolean.getBoolean("netbeans.debug.editor.draw.fragment"); // NOI18N
55

56     /** Only one instance of draw-engine */
57     private static DrawEngine drawEngine;
58     
59     private static final char[] SPACE = new char[] { ' ' };
60
61     /** Prevent creation */
62     private DrawEngine() {
63     }
64
65     /** Get the static instance of draw-engine */
66     public static DrawEngine getDrawEngine() {
67         if (drawEngine == null) {
68             drawEngine = new DrawEngine();
69         }
70         return drawEngine;
71     }
72     
73     public PreinitializedDrawEngine getDrawEngine(View JavaDoc view,
74     DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
75     int startX, int startY, int targetOffset) throws BadLocationException JavaDoc
76     {
77                   
78         // Some correctness tests at the begining
79
if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
80                 || startX < 0 || startY < 0
81            ) {
82             return null;
83         }
84                   
85         BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument());
86         PreinitializedDrawEngine preinitializedDrawEngine = new PreinitializedDrawEngine(drawGraphics);
87         preinitializedDrawEngine.preinitialize(doc, editorUI, startOffset, endOffset, startX, startY, targetOffset);
88         return preinitializedDrawEngine;
89     }
90     
91
92     private void initLineNumbering(DrawInfo ctx) {
93         // Resolve whether line numbers will be painted
94
ctx.lineNumbering = ctx.editorUI.lineNumberVisible
95                             && ctx.drawGraphics.supportsLineNumbers();
96
97         // create buffer for showing line numbers
98
if (ctx.lineNumbering) {
99             try {
100                 ctx.startLineNumber = Utilities.getLineOffset(ctx.doc, ctx.startOffset) + 1;
101             } catch (BadLocationException JavaDoc e) {
102                 Utilities.annotateLoggable(e);
103             }
104
105             ctx.lineNumberColoring = ctx.editorUI.getColoring(SettingsNames.LINE_NUMBER_COLORING);
106             if (ctx.lineNumberColoring == null) {
107                 ctx.lineNumberColoring = ctx.defaultColoring; // no number coloring found
108

109             } else { // lineNumberColoring not null
110
ctx.lineNumberColoring = ctx.lineNumberColoring.apply(ctx.defaultColoring);
111             }
112
113             Font JavaDoc lnFont = ctx.lineNumberColoring.getFont();
114             if (lnFont == null) {
115                 lnFont = ctx.defaultColoring.getFont();
116             }
117
118             Color JavaDoc lnBackColor = ctx.lineNumberColoring.getBackColor();
119             if (lnBackColor == null) {
120                 lnBackColor = ctx.defaultColoring.getBackColor();
121             }
122
123             Color JavaDoc lnForeColor = ctx.lineNumberColoring.getForeColor();
124             if (lnForeColor == null) {
125                 lnForeColor = ctx.defaultColoring.getForeColor();
126             }
127
128             ctx.lineNumberChars = new char[Math.max(ctx.editorUI.lineNumberMaxDigitCount, 1)];
129             if (ctx.graphics == null) {
130                 ctx.syncedLineNumbering = true;
131
132             } else { // non-synced line numbering - need to remember line start offsets
133
try {
134                     int endLineNumber = Utilities.getLineOffset(ctx.doc, ctx.endOffset) + 1;
135                     ctx.lineStartOffsets = new int[endLineNumber - ctx.startLineNumber + 2]; // reserve more
136
} catch (BadLocationException JavaDoc e) {
137                     Utilities.annotateLoggable(e);
138                 }
139             }
140         }
141     }
142
143     private void initInfo(DrawInfo ctx) throws BadLocationException JavaDoc {
144         ctx.x = ctx.startX;
145         ctx.y = ctx.startY;
146         ctx.lineHeight = ctx.editorUI.getLineHeight();
147         ctx.defaultColoring = ctx.editorUI.getDefaultColoring();
148         ctx.tabSize = ctx.doc.getTabSize();
149         ctx.fragmentOffset = ctx.startOffset; // actual painting position
150
ctx.graphics = ctx.drawGraphics.getGraphics();
151
152         if (ctx.graphics != null) {
153             if (ctx.editorUI.renderingHints != null) {
154                 ((Graphics2D JavaDoc)ctx.graphics).setRenderingHints(ctx.editorUI.renderingHints);
155             }
156         }
157
158         initLineNumbering(ctx);
159
160         // Initialize draw context
161
ctx.foreColor = ctx.defaultColoring.getForeColor();
162         ctx.backColor = ctx.defaultColoring.getBackColor();
163         ctx.font = ctx.defaultColoring.getFont();
164         ctx.bol = true; // draw must always start at line begin
165

166         // Init draw graphics
167
ctx.drawGraphics.init(ctx);
168         ctx.drawGraphics.setDefaultBackColor(ctx.defaultColoring.getBackColor());
169         ctx.drawGraphics.setLineHeight(ctx.lineHeight);
170         ctx.drawGraphics.setLineAscent(ctx.editorUI.getLineAscent());
171         ctx.drawGraphics.setX(ctx.x);
172         ctx.drawGraphics.setY(ctx.y);
173
174         // Init all draw-layers
175
ctx.layers = ctx.editorUI.getDrawLayerList().currentLayers();
176         int layersLength = ctx.layers.length;
177         ctx.layerActives = new boolean[layersLength];
178         ctx.layerActivityChangeOffsets = new int[layersLength];
179
180         for (int i = 0; i < layersLength; i++) {
181             ctx.layers[i].init(ctx); // init all layers
182
}
183
184         ctx.drawMarkList = new ArrayList JavaDoc();
185         Map JavaDoc docMarks = ctx.doc.marks;
186         MarkVector docMarksStorage = ctx.doc.marksStorage;
187         synchronized (docMarks) {
188             int offset = ctx.startOffset;
189             int low = 0;
190             int markCount = docMarksStorage.getMarkCount();
191             int high = markCount - 1;
192
193             while (low <= high) {
194                 int mid = (low + high) >> 1;
195                 int cmp = docMarksStorage.getMarkOffsetInternal(mid) - offset;
196
197                 if (cmp < 0)
198                     low = mid + 1;
199                 else if (cmp > 0)
200                     high = mid - 1;
201                 else { // found
202
while (--mid >= 0
203                         && docMarksStorage.getMarkOffsetInternal(mid) == offset
204                     ) { }
205                     low = mid + 1;
206                     break;
207                 }
208
209             }
210
211             // the low contains first mark to consider
212
offset = ctx.endOffset;
213             while (low < markCount) {
214                 MultiMark m = (MultiMark)docMarksStorage.getMark(low);
215                 if (m.isValid()) {
216                     if (m.getOffset() > offset) {
217                         break;
218                     }
219
220                     Mark mark = (Mark)docMarks.get(m);
221                     if (mark == null) {
222                         throw new IllegalStateException JavaDoc("No mark for m=" + m); // NOI18N
223
}
224                     if (mark instanceof MarkFactory.DrawMark) {
225                         ctx.drawMarkList.add(mark);
226                     }
227                 }
228
229                 low++;
230             }
231         }
232         
233         
234         // Get current draw mark
235
ctx.drawMarkIndex = 0;
236         ctx.drawMarkOffset = Integer.MAX_VALUE;
237         if (ctx.drawMarkList.size() > 0) {
238             ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++);
239             try {
240                 ctx.drawMarkOffset = ctx.drawMark.getOffset();
241             } catch (InvalidMarkException e) {
242                 throw new IllegalStateException JavaDoc(e.toString());
243             }
244             if (ctx.drawMarkOffset < ctx.updateOffset) {
245                 ctx.updateOffset = ctx.drawMarkOffset;
246                 ctx.drawMarkUpdate = true;
247             }
248         }
249
250         // Prepare syntax scanner and then cycle through all the syntax segments
251
int endTokenSafeOffset = ctx.doc.getTokenSafeOffset(ctx.endOffset);
252         ctx.doc.prepareSyntax(ctx.text, ctx.syntax,
253             ctx.startOffset, endTokenSafeOffset - ctx.startOffset,
254             false, false
255         );
256
257         ctx.textArray = ctx.text.array;
258         ctx.buffer = ctx.textArray;
259         ctx.bufferStartOffset = ctx.startOffset - ctx.syntax.getOffset();
260         ctx.drawGraphics.setBuffer(ctx.textArray);
261         if (ctx.drawGraphics instanceof DrawGraphics.GraphicsDG) {
262             ((DrawGraphics.GraphicsDG)ctx.drawGraphics).setBufferStartOffset(ctx.bufferStartOffset);
263         }
264
265         ctx.continueDraw = true;
266     }
267
268     private void handleBOL(DrawInfo ctx) {
269         if (ctx.lineNumbering) {
270             if (ctx.syncedLineNumbering) {
271                 // Draw line numbers synchronously at begining of each line
272

273                 // Init context
274
ctx.foreColor = ctx.lineNumberColoring.getForeColor();
275                 ctx.backColor = ctx.lineNumberColoring.getBackColor();
276                 ctx.font = ctx.lineNumberColoring.getFont();
277                 ctx.strikeThroughColor = null;
278                 ctx.underlineColor = null;
279                 ctx.waveUnderlineColor = null;
280
281                 int lineNumber = ctx.startLineNumber + ctx.lineIndex;
282                 // Update line-number by layers
283
int layersLength = ctx.layers.length;
284                 for (int i = 0; i < layersLength; i++) {
285                     lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx);
286                 }
287
288                 // Fill the buffer with digit chars
289
int i = Math.max(ctx.lineNumberChars.length - 1, 0);
290                 do {
291                     ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10));
292                     lineNumber /= 10;
293                 } while (lineNumber != 0 && i >= 0);
294
295                 // Fill the rest with spaces
296
while (i >= 0) {
297                     ctx.lineNumberChars[i--] = ' ';
298                 }
299
300                 // Fill the DG's attributes and draw
301
int numX = ctx.x - ctx.editorUI.lineNumberWidth;
302                 if (ctx.editorUI.getLineNumberMargin() != null) {
303                     numX += ctx.editorUI.getLineNumberMargin().left;
304                 }
305                 ctx.drawGraphics.setX(numX);
306
307                 ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
308                 ctx.drawGraphics.setForeColor(ctx.foreColor);
309                 ctx.drawGraphics.setBackColor(ctx.backColor);
310                 ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
311                 ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
312                 ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
313                 ctx.drawGraphics.setFont(ctx.font);
314                 ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length,
315                     ctx.editorUI.lineNumberWidth);
316
317                 // When printing there should be an additional space between
318
// line number and the text
319
if (ctx.drawGraphics.getGraphics() == null) {
320                     ctx.drawGraphics.setBuffer(SPACE);
321                     ctx.drawGraphics.drawChars(0, 1,
322                         ctx.editorUI.lineNumberDigitWidth);
323                 }
324
325                 ctx.drawGraphics.setX(ctx.x);
326                 ctx.drawGraphics.setBuffer(ctx.textArray);
327
328             } else { // non-synced line numbering
329
ctx.lineStartOffsets[ctx.lineIndex] = ctx.fragmentOffset; // store the line number
330
}
331         }
332
333         ctx.lineIndex++;
334     }
335
336
337     /** Handle the end-of-line */
338     private void handleEOL(DrawInfo ctx) {
339         ctx.drawGraphics.setX(ctx.x);
340         ctx.drawGraphics.setY(ctx.y);
341         
342         ctx.drawGraphics.eol(); // sign EOL to DG
343
ctx.widestWidth = Math.max(ctx.widestWidth, ctx.x); // update widest width
344
ctx.visualColumn = 0;
345         ctx.x = ctx.startX;
346         ctx.y += ctx.lineHeight;
347
348         ctx.drawGraphics.setX(ctx.x);
349         ctx.drawGraphics.setY(ctx.y);
350
351     }
352
353
354     /** Called when the current fragment starts at the offset that corresponds
355     * to the update-offset.
356     */

357     private void updateOffsetReached(DrawInfo ctx) {
358         if (ctx.drawMarkUpdate) { // update because of draw mark
359
// means no-mark update yet performed
360
int layersLength = ctx.layers.length;
361             for (int i = 0; i < layersLength; i++) {
362                 DrawLayer l = ctx.layers[i];
363                 if (l.getName().equals(ctx.drawMark.layerName)
364                         && (ctx.drawMark.isDocumentMark()
365                             || ctx.editorUI == ctx.drawMark.getEditorUI())
366                    ) {
367                     ctx.layerActives[i] = l.isActive(ctx, ctx.drawMark);
368                     int naco = l.getNextActivityChangeOffset(ctx);
369                     ctx.layerActivityChangeOffsets[i] = naco;
370                     if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
371                         ctx.layerUpdateOffset = naco;
372                     }
373                 }
374             }
375
376             // Get next mark
377
if (ctx.drawMarkIndex < ctx.drawMarkList.size()) {
378                 ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++);
379                 try {
380                     ctx.drawMarkOffset = ctx.drawMark.getOffset();
381                 } catch (InvalidMarkException e) {
382                     throw new IllegalStateException JavaDoc(e.toString());
383                 }
384
385             } else { // no more draw marks
386
ctx.drawMark = null;
387                 ctx.drawMarkOffset = Integer.MAX_VALUE;
388             }
389
390         } else { // update because activity-change-offset set in some layer
391
ctx.layerUpdateOffset = Integer.MAX_VALUE;
392             int layersLength = ctx.layers.length;
393             for (int i = 0; i < layersLength; i++) {
394                 // Update only layers with the same offset as fragmentOffset
395
int naco = ctx.layerActivityChangeOffsets[i];
396                 if (naco == ctx.fragmentOffset) {
397                     DrawLayer l = ctx.layers[i];
398                     ctx.layerActives[i] = l.isActive(ctx, null);
399                     naco = l.getNextActivityChangeOffset(ctx);
400                     ctx.layerActivityChangeOffsets[i] = naco;
401                 }
402
403                 if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
404                     ctx.layerUpdateOffset = naco;
405                 }
406             }
407         }
408
409         // Check next update position
410
if (ctx.drawMarkOffset < ctx.layerUpdateOffset) {
411             ctx.drawMarkUpdate = true;
412             ctx.updateOffset = ctx.drawMarkOffset;
413
414         } else {
415             ctx.drawMarkUpdate = false;
416             ctx.updateOffset = ctx.layerUpdateOffset;
417         }
418
419     }
420
421     /** Compute the length of the fragment. */
422     private void computeFragmentLength(DrawInfo ctx) {
423         // Compute initial fragment (of token) length
424
ctx.fragmentStartIndex = ctx.fragmentOffset - ctx.bufferStartOffset;
425         ctx.fragmentLength = Math.min(ctx.updateOffset - ctx.fragmentOffset,
426                                       ctx.tokenLength - ctx.drawnLength);
427
428         // Find first TAB or LF
429
int stopIndex = Analyzer.findFirstTabOrLF(ctx.textArray,
430                         ctx.fragmentStartIndex, ctx.fragmentLength);
431
432         // There must be extra EOL at the end of the document
433
ctx.eol = (ctx.fragmentOffset == ctx.docLen);
434         ctx.tabsFragment = false;
435
436         // Check whether there are no tabs in the fragment and possibly shrink
437
// Get the first offset of the tab character or -1 if no tabs in fragment
438
if (stopIndex >= 0) { // either '\t' or '\n' found
439
if (stopIndex == ctx.fragmentStartIndex) { // since fragment start
440
if (ctx.textArray[stopIndex] == '\t') { //
441
ctx.tabsFragment = true;
442                     // Find first non-tab char
443
int ntInd = Analyzer.findFirstNonTab(ctx.textArray, ctx.fragmentStartIndex,
444                                                          ctx.fragmentLength);
445
446                     if (ntInd != -1) { // not whole fragment are tabs
447
ctx.fragmentLength = ntInd - ctx.fragmentStartIndex;
448                     }
449
450
451                 } else { // '\n' found
452
ctx.eol = true;
453                     ctx.fragmentLength = 1; // only one EOL in fragment
454
}
455
456             } else { // inside fragment start
457
ctx.fragmentLength = stopIndex - ctx.fragmentStartIndex; // shrink fragment size
458
}
459         }
460     }
461
462     /** Compute the display width of the fragment */
463     private void computeFragmentDisplayWidth(DrawInfo ctx) {
464         // First go through all layers to update draw context
465
// to get up-to-date fonts and colors
466
if (!ctx.eol) { // handled later
467
int layersLength = ctx.layers.length;
468             for (int i = 0; i < layersLength; i++) {
469                 if (ctx.layerActives[i]) {
470                     ctx.layers[i].updateContext(ctx); // Let the layer to update the context
471
}
472             }
473         }
474
475         // Handle possible white space expansion and compute display width
476
FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(ctx.font);
477         ctx.spaceWidth = (ctx.component != null)
478             ? fmcInfo.getSpaceWidth(ctx.component) : ctx.editorUI.defaultSpaceWidth;
479
480         // Compute real count of chars in fragment - can differ if tabs
481
ctx.fragmentCharCount = ctx.fragmentLength;
482         if (ctx.tabsFragment) { // tabs in fragment
483
ctx.fragmentCharCount = Analyzer.getColumn(ctx.textArray,
484                                     ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn) - ctx.visualColumn;
485             ctx.fragmentWidth = ctx.fragmentCharCount * ctx.spaceWidth;
486
487         } else if (ctx.eol) { // EOL will have the spaceWidth
488
ctx.fragmentWidth = ctx.spaceWidth;
489
490         } else { // regular fragment
491
if (ctx.fragmentLength > 0) {
492                 if (ctx.component != null) {
493                     ctx.fragmentWidth = FontMetricsCache.getFontMetrics(ctx.font, ctx.component).charsWidth(
494                                             ctx.textArray, ctx.fragmentStartIndex, ctx.fragmentLength);
495
496                 } else { // non-valid component
497
ctx.fragmentWidth = ctx.fragmentLength * ctx.spaceWidth;
498                 }
499
500             } else {
501                 ctx.fragmentWidth = 0; // empty fragment
502
}
503         }
504
505     }
506
507
508     /** Draw the fragment. Handle the EOL in special way
509     * as it needs to care about the empty-lines.
510     */

511     private void drawFragment(DrawInfo ctx) {
512         if (ctx.eol) { // special handling for EOL
513
int layersLength = ctx.layers.length;
514             boolean emptyLine = false;
515             int blankWidth = ctx.fragmentWidth;
516
517             /** Need to do one or two cycles.
518             * In the first pass
519             * the check is performed whether the line is empty.
520             * If so all the layers that extend the empty line are
521             * called to update the context and the resulting half-space
522             * is drawn.
523             * In the second pass all the layers that extend EOL
524             * are called to update the context and the resulting
525             * whitespace is drawn.
526             */

527             do {
528                 blankWidth = 0;
529                 if (ctx.bol) { // empty line found
530
if (!emptyLine) { // not yet processed
531
for (int i = 0; i < layersLength; i++) {
532                             if (ctx.layerActives[i]) {
533                                 DrawLayer l = ctx.layers[i];
534                                 if (l.extendsEmptyLine()) {
535                                     emptyLine = true; // for at least one layer
536
l.updateContext(ctx);
537                                 }
538                             }
539                         }
540
541                         if (emptyLine) { // count only if necessary
542
blankWidth = ctx.spaceWidth / 2; // display half of char
543
}
544                     } else { // already went through the cycle once for empty line
545
emptyLine = false;
546                     }
547                 }
548
549                 if (!emptyLine) { // EOL and currently not servicing empty line
550
boolean extendEOL = false;
551                     for (int i = 0; i < layersLength; i++) {
552                         if (ctx.layerActives[i]) {
553                             DrawLayer l = ctx.layers[i];
554                             if (l.extendsEOL()) {
555                                 extendEOL = true; // for at least one layer
556
l.updateContext(ctx);
557                             }
558                         }
559                     }
560                     if (extendEOL && ctx.component != null) {
561                         // Disable the underlines and strikethrough for EOL extension
562
ctx.drawGraphics.setStrikeThroughColor(null);
563                         ctx.drawGraphics.setUnderlineColor(null);
564                         ctx.drawGraphics.setWaveUnderlineColor(null);
565                         blankWidth = ctx.component.getWidth();
566                     }
567                 }
568                 if (blankWidth > 0) {
569                     ctx.drawGraphics.setBackColor(ctx.backColor);
570                     ctx.drawGraphics.fillRect(blankWidth);
571                     if (emptyLine &&
572                             ctx.x <= ctx.editorUI.getTextMargin().left) { //#58652
573
ctx.x += blankWidth;
574                     }
575                 }
576             } while (emptyLine);
577
578         } else { // Draw regular fragment
579

580             ctx.drawGraphics.setBackColor(ctx.backColor);
581             ctx.drawGraphics.setForeColor(ctx.foreColor);
582             ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
583             ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
584             ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
585             ctx.drawGraphics.setFont(ctx.font);
586
587             if (ctx.tabsFragment) {
588                 ctx.drawGraphics.drawTabs(ctx.fragmentStartIndex,
589                     ctx.fragmentLength, ctx.fragmentCharCount, ctx.fragmentWidth);
590
591             } else { // non-tabs
592
ctx.drawGraphics.drawChars(ctx.fragmentStartIndex,
593                     ctx.fragmentLength, ctx.fragmentWidth);
594             }
595         }
596     }
597
598
599     /** Check whether the target offset was reached. */
600     private void checkTargetOffsetReached(DrawInfo ctx) {
601         ctx.continueDraw = true;
602
603         // Check whether at the end of the line
604
if (ctx.eol
605             && (ctx.targetOffset == ctx.fragmentOffset || (ctx.targetOffset == -1))
606         ) {
607             /** Special case for the emulating the EOL at the end of the document
608             * The EOL is emulated to process the layers that extend empty-line or EOL
609             */

610             char ch = '\n';
611             ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
612                     ctx.fragmentOffset, ch, ctx.x, ctx.spaceWidth, ctx);
613
614         // Check whether targeting all characters
615
} else if (ctx.targetOffset == -1
616           && ctx.fragmentLength > 0 // Not sure whether it's necessary
617
) { // When targeting all chars
618
FontMetrics JavaDoc fm = FontMetricsCache.getFontMetrics(ctx.font, ctx.component);
619             
620             // Use binary search to find the right offset
621
int low = -1;
622             int high = ctx.fragmentLength - 1;
623             
624             // Cache the widths and first check whether past the end of fragment
625
int lastMid = high;
626             int lastWidth; // cache
627
if (ctx.tabsFragment) { // fragment contains tabs
628
int spaceCount = Analyzer.getColumn(ctx.textArray,
629                     ctx.fragmentStartIndex, high, ctx.tabSize, ctx.visualColumn)
630                         - ctx.visualColumn;
631                 lastWidth = spaceCount * ctx.spaceWidth;
632
633             } else { // no tabs inside fragment
634
lastWidth = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, high);
635             }
636
637             int lastWidthP1; // plus one char
638
if (ctx.tabsFragment) { // fragment contains tabs
639
int spaceCount = Analyzer.getColumn(ctx.textArray,
640                     ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn)
641                         - ctx.visualColumn;
642                 lastWidthP1 = spaceCount * ctx.spaceWidth;
643
644             } else { // no tabs inside fragment
645
lastWidthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, ctx.fragmentLength);
646             }
647
648             // Test whether the end of the fragment is accepted
649
ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
650                 ctx.fragmentOffset + high, ctx.textArray[ctx.fragmentStartIndex + high],
651                 ctx.x + lastWidth, lastWidthP1 - lastWidth, ctx
652             );
653             
654             if (!ctx.continueDraw) {
655                 // Binary search of the first offset that returns false follows
656
while (low <= high) {
657                     int mid =(low + high) / 2;
658
659                     // Compute width that will be passed as x coordinate
660
int width = 0;
661                     if (mid == lastMid + 1) { // try to use cached value
662
width = lastWidthP1;
663
664                     } else {
665                         if (ctx.tabsFragment) { // fragment contains tabs
666
int spaceCount = Analyzer.getColumn(ctx.textArray,
667                                 ctx.fragmentStartIndex, mid, ctx.tabSize, ctx.visualColumn)
668                                 - ctx.visualColumn;
669                             width = spaceCount * ctx.spaceWidth;
670
671                         } else { // no tabs inside fragment
672
width = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, mid);
673                         }
674                     }
675
676                     // Compute width plus one char and substract the previous width
677
// to get the width of the char
678
int widthP1 = 0;
679                     if (mid == lastMid - 1) { // try to use cached value
680
widthP1 = lastWidth;
681
682                     } else {
683                         if (ctx.tabsFragment) { // fragment contains tabs
684
int spaceCount = Analyzer.getColumn(ctx.textArray,
685                                 ctx.fragmentStartIndex, mid + 1, ctx.tabSize, ctx.visualColumn)
686                                 - ctx.visualColumn;
687                             widthP1 = spaceCount * ctx.spaceWidth;
688
689                         } else { // no tabs inside fragment
690
widthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex, mid + 1);
691                         }
692                     }
693
694                     lastWidth = width;
695                     lastWidthP1 = widthP1;
696                     lastMid = mid;
697
698                     ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
699                         ctx.fragmentOffset + mid, ctx.textArray[ctx.fragmentStartIndex + mid],
700                         ctx.x + width, widthP1 - width, ctx
701                     );
702
703                     if (ctx.continueDraw) {
704                         low = mid + 1;
705                     } else {
706                         //Bug #34130, search should not end when mid == low + 1
707
if (mid > low && mid != high) {
708                             high = mid; // last that rejected
709
} else {
710                             break;
711                         }
712                     }
713                 }
714             }
715             
716
717
718         // Check whether target offset is inside current fragment
719
} else if (ctx.targetOffset < ctx.fragmentOffset + ctx.fragmentLength
720                    && ctx.fragmentOffset <= ctx.targetOffset
721         ) {
722             int curWidth;
723             int prevWidth = 0;
724             int i = (ctx.targetOffset - ctx.fragmentOffset);
725
726             if (i > 0) {
727                 if (ctx.tabsFragment) { // fragment contains tabs
728
int spaceCount = Analyzer.getColumn(ctx.textArray,
729                         ctx.fragmentStartIndex, i, ctx.tabSize, ctx.visualColumn)
730                             - ctx.visualColumn;
731                     prevWidth = spaceCount * ctx.spaceWidth;
732
733                 } else { // no tabs inside fragment
734
prevWidth = FontMetricsCache.getFontMetrics(ctx.font,
735                         ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i);
736                 }
737             }
738
739             if (ctx.tabsFragment) { // fragment contains tabs
740
int spaceCount = Analyzer.getColumn(ctx.textArray,
741                     ctx.fragmentStartIndex, i + 1, ctx.tabSize, ctx.visualColumn)
742                         - ctx.visualColumn;
743                 curWidth = spaceCount * ctx.spaceWidth;
744
745             } else { // no tabs inside fragment
746
curWidth = FontMetricsCache.getFontMetrics(ctx.font,
747                            ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i + 1);
748             }
749
750             ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
751                 ctx.fragmentOffset + i, ctx.textArray[ctx.fragmentStartIndex + i],
752                 ctx.x + prevWidth, curWidth - prevWidth, ctx);
753         }
754     }
755
756     /** Draw current fragment of the token. */
757     private void drawCurrentTokenFragment(DrawInfo ctx) {
758         // Fill in the draw context
759
ctx.foreColor = ctx.defaultColoring.getForeColor();
760         ctx.backColor = ctx.defaultColoring.getBackColor();
761         ctx.font = ctx.defaultColoring.getFont();
762         ctx.strikeThroughColor = null;
763         ctx.underlineColor = null;
764         ctx.waveUnderlineColor = null;
765
766         if (ctx.bol) { // if we are on the line begining
767
Color JavaDoc fg = ctx.foreColor;
768             Color JavaDoc bg = ctx.backColor;
769             handleBOL(ctx);
770             ctx.foreColor = fg;
771             ctx.backColor = bg;
772         }
773
774         // Check for status updates in planes at the begining of this fragment
775
while (ctx.fragmentOffset == ctx.updateOffset) {
776             updateOffsetReached(ctx); // while() because can be more marks at same pos
777
}
778
779         // Compute the length of the fragment
780
computeFragmentLength(ctx);
781
782         // Compute the display width of the fragment
783
computeFragmentDisplayWidth(ctx);
784
785         // Draw the fragment
786
drawFragment(ctx);
787
788         if (debugFragment) {
789             System.err.println("DrawEngine: FRAGMENT='" // NOI18N
790
+ EditorDebug.debugChars(ctx.buffer, ctx.fragmentStartIndex, ctx.fragmentLength)
791                     + "', at pos=" + ctx.fragmentOffset // NOI18N
792
+ ", bol=" + ctx.bol + ", eol=" + ctx.eol // NOI18N
793
);
794         }
795
796         // Check whether target-offset was reached
797
if (ctx.component != null) {
798             checkTargetOffsetReached(ctx);
799         }
800
801         // Move the variables to the next fragment in token
802
ctx.fragmentOffset += ctx.fragmentLength;
803         ctx.drawnLength += ctx.fragmentLength;
804         ctx.visualColumn += ctx.fragmentCharCount;
805         ctx.x += ctx.fragmentWidth;
806         ctx.bol = false;
807
808         // Update coordinates at the end of each line
809
if (ctx.eol) {
810             handleEOL(ctx);
811             ctx.bol = true; // now at BOL
812
}
813
814         if (ctx.fragmentOffset >= ctx.endOffset && ctx.endOffset < ctx.docLen) {
815             ctx.continueDraw = false;
816         }
817     }
818
819
820     /** Draw one token. This is repeatedly called until all the tokens
821     * were drawn.
822     * @return true when the drawing of the next token should be done
823     * or false when the drawing should stop.
824     */

825     private void drawCurrentToken(DrawInfo ctx) {
826         // Get the token
827
if (ctx.tokenID != null) {
828             ctx.tokenContextPath = ctx.syntax.getTokenContextPath();
829             ctx.tokenOffset = ctx.syntax.getTokenOffset() + ctx.bufferStartOffset;
830             ctx.tokenLength = ctx.syntax.getTokenLength();
831
832             /* Check whether the token isn't totally before the area to be drawn.
833              * It must cover it at least by one character. For the more complicated lexical
834              * analyzers it's possible that they return several tokens that
835              * will cover only the prescan area.
836              */

837             if (ctx.tokenOffset + ctx.tokenLength <= ctx.startOffset) {
838                 return;
839             }
840
841         } else { // end of drawing area
842
ctx.tokenContextPath = null;
843             ctx.tokenOffset = ctx.fragmentOffset;
844             ctx.tokenLength = 0;
845         }
846
847         // Ask all the contexts first to possibly find out
848
// the first fragment length
849
if (ctx.tokenOffset <= ctx.startOffset) {
850             ctx.layerUpdateOffset = Integer.MAX_VALUE;
851             int layersLength = ctx.layers.length;
852             for (int i = 0; i < layersLength; i++) { // update status of all layers
853
DrawLayer l = ctx.layers[i];
854                 ctx.layerActives[i] = l.isActive(ctx, null);
855
856                 int naco = l.getNextActivityChangeOffset(ctx);
857                 ctx.layerActivityChangeOffsets[i] = naco;
858                 if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
859                     ctx.layerUpdateOffset = naco;
860                 }
861             }
862             ctx.updateOffset = Math.min(ctx.layerUpdateOffset, ctx.drawMarkOffset);
863         }
864
865         ctx.drawnLength = ctx.fragmentOffset - ctx.tokenOffset;
866         ctx.fragmentLength = 0; // length of current token fragment
867

868         if (debug) {
869             System.err.println("DrawEngine: TOKEN='" // NOI18N
870
+ EditorDebug.debugChars(ctx.getBuffer(), ctx.getTokenOffset()
871                         - ctx.getBufferStartOffset(), ctx.getTokenLength())
872                     + "', tokenID=<" + (ctx.getTokenID() == null ? "null" : ctx.tokenID.getName()) // NOI18N
873
+ ">, tcp=" + ctx.getTokenContextPath() // NOI18N
874
+ ", pos=" + ctx.getTokenOffset() // NOI18N
875
);
876         }
877
878         // Process all the fragments of one token
879
do {
880             drawCurrentTokenFragment(ctx);
881         } while (ctx.continueDraw && ctx.drawnLength < ctx.tokenLength);
882
883     }
884
885     private void graphicsSpecificUpdates(DrawInfo ctx) {
886         Rectangle JavaDoc bounds = ctx.editorUI.getExtentBounds();
887         Rectangle JavaDoc clip = ctx.graphics.getClipBounds();
888         Insets JavaDoc textMargin = ctx.editorUI.getTextMargin();
889         int leftMarginWidth = textMargin.left - ctx.editorUI.lineNumberWidth
890                               - ctx.editorUI.textLeftMarginWidth;
891
892         // Draw line numbers bar and all the line nummbers
893
if (ctx.lineNumbering && !ctx.syncedLineNumbering) {
894             Color JavaDoc lnBackColor = ctx.lineNumberColoring.getBackColor();
895             int numY = ctx.startY;
896             int lnBarX = bounds.x + leftMarginWidth;
897             if (!lnBackColor.equals(ctx.defaultColoring.getBackColor()) || bounds.x > 0) {
898                 ctx.graphics.setColor(lnBackColor);
899                 ctx.graphics.fillRect(lnBarX, numY, ctx.editorUI.lineNumberWidth,
900                                       ctx.lineIndex * ctx.lineHeight); // can't use dg because of height
901
}
902
903             ctx.drawGraphics.setDefaultBackColor(lnBackColor); // will paint into bar
904

905             int lastDigitInd = Math.max(ctx.lineNumberChars.length - 1, 0);
906             int numX = lnBarX;
907             if (ctx.editorUI.getLineNumberMargin() != null) {
908                 numX += ctx.editorUI.getLineNumberMargin().left;
909             }
910
911             ctx.bol = true; //
912
for (int j = 0; j < ctx.lineIndex; j++) { // draw all line numbers
913

914                 // Init the context
915
ctx.fragmentOffset = ctx.lineStartOffsets[j];
916                 ctx.foreColor = ctx.lineNumberColoring.getForeColor();
917                 ctx.backColor = lnBackColor;
918                 ctx.font = ctx.lineNumberColoring.getFont();
919                 ctx.strikeThroughColor = null;
920                 ctx.underlineColor = null;
921                 ctx.waveUnderlineColor = null;
922
923                 int lineNumber = ctx.startLineNumber + j;
924                 // Update line-number by layers
925
int layersLength = ctx.layers.length;
926                 for (int i = 0; i < layersLength; i++) {
927                     lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx);
928                 }
929
930                 int i = lastDigitInd;
931                 // Fill in the digit chars
932
do {
933                     ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10));
934                     lineNumber /= 10;
935                 } while (lineNumber != 0 && i >= 0);
936                 // Fill in the spaces
937
while (i >= 0) {
938                     ctx.lineNumberChars[i--] = ' ';
939                 }
940
941                 ctx.drawGraphics.setY(numY);
942                 ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
943                 ctx.drawGraphics.setForeColor(ctx.foreColor);
944                 ctx.drawGraphics.setBackColor(ctx.backColor);
945                 ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
946                 ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
947                 ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
948                 ctx.drawGraphics.setFont(ctx.font);
949
950                 ctx.drawGraphics.setX(lnBarX);
951                 ctx.drawGraphics.fillRect(ctx.editorUI.lineNumberWidth);
952                 ctx.drawGraphics.setX(numX);
953
954                 ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length,
955                     ctx.lineNumberChars.length * ctx.editorUI.lineNumberDigitWidth);
956                 
957                 ctx.drawGraphics.setBuffer(null); // will do changes in buffer
958

959                 numY += ctx.lineHeight;
960             }
961         }
962
963         // Clear margins
964
ctx.graphics.setColor(ctx.defaultColoring.getBackColor());
965     }
966
967     /** Draw on the specified area.
968      * @param drawGraphics draw graphics through which the drawing is done
969      * @param editorUI extended UI to use
970      * @param startOffset position from which the drawing starts.
971      * It must BOL of the first line to be drawn.
972      * @param endOffset position where the drawing stops.
973      * It must be EOL of the last line to be drawn.
974      * @param startX x-coordinate at which the drawing starts
975      * @param startY x-coordinate at which the drawing starts
976      * @param targetOffset position where the targetOffsetReached() method
977      * of drawGraphics is called. This is useful for caret update or modelToView.
978      * The Integer.MAX_VALUE can be passed to ignore that behavior. The -1 value
979      * has special meaning there so that it calls targetOffsetReached() after each
980      * character processed. This is used by viewToModel to find the position
981      * for some point.
982      */

983     void draw(DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
984               int startX, int startY, int targetOffset) throws BadLocationException JavaDoc {
985                   
986         draw(null, drawGraphics, editorUI, startOffset, endOffset,
987             startX, startY, targetOffset);
988     }
989
990     void draw(View JavaDoc view, DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
991               int startX, int startY, int targetOffset) throws BadLocationException JavaDoc {
992         // Some correctness tests at the begining
993
if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
994                 || startX < 0 || startY < 0
995            ) {
996             return;
997         }
998
999         BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument());
1000        if (debug) {
1001            javax.swing.text.Element JavaDoc lineRoot = doc.getParagraphElement(0).getParentElement();
1002            int startLine = lineRoot.getElementIndex(startOffset);
1003            int startLineOffset = lineRoot.getElement(startLine).getStartOffset();
1004            int endLine = lineRoot.getElementIndex(endOffset);
1005            int endLineOffset = lineRoot.getElement(endLine).getStartOffset();
1006            Graphics JavaDoc g = drawGraphics.getGraphics();
1007            
1008            System.err.println("DrawEngine:---------- DRAWING startOffset="
1009                + startOffset + ", startLine=" + startLine // NOI18N
1010
+ "(o=" + startLineOffset + "), endOffset=" + endOffset // NOI18N
1011
+ ", endLine=" + endLine + "(o=" + endLineOffset // NOI18N
1012
+ "), clip=" + ((g != null) ? g.getClipBounds().toString() : "null") // NOI18N
1013
+ " ------------------"); // NOI18N
1014
}
1015
1016        // Draw-context and other info
1017
DrawInfo ctx = new DrawInfo();
1018        ctx.drawGraphics = drawGraphics;
1019        ctx.drawGraphics.setView(view);
1020        ctx.view = view;
1021        ctx.editorUI = editorUI;
1022        ctx.startOffset = startOffset;
1023        ctx.endOffset = endOffset;
1024        ctx.startX = startX;
1025        ctx.startY = startY;
1026        ctx.targetOffset = targetOffset;
1027
1028        synchronized (editorUI) { // lock operations manipulating draw layer chain
1029
ctx.doc = doc;
1030            if (ctx.doc == null) { // no base-document available
1031
return;
1032            }
1033
1034            ctx.text = DocumentUtilities.SEGMENT_CACHE.getSegment();
1035            ctx.syntax = ctx.doc.getFreeSyntax();
1036            ctx.doc.readLock();
1037
1038            try {
1039                ctx.component = editorUI.getComponent();
1040                ctx.docLen = ctx.doc.getLength();
1041
1042                if (ctx.startOffset > ctx.docLen || ctx.endOffset > ctx.docLen) {
1043                    return;
1044                }
1045
1046                /* Correct the ending position to be at the begining
1047                 * of the next line. The only exception is when the
1048                 * endOffset is equal to docLen.
1049                 */

1050                if (ctx.endOffset < ctx.docLen) {
1051                    ctx.endOffset++;
1052                }
1053
1054                // Initialize the draw-info
1055
initInfo(ctx);
1056
1057                // Cycle through all the tokens found in the buffer
1058
do {
1059                    ctx.tokenID = ctx.syntax.nextToken();
1060
1061                    // Draw the current token
1062
drawCurrentToken(ctx);
1063
1064                } while (ctx.continueDraw && ctx.tokenID != null);
1065
1066                if (ctx.endOffset == ctx.docLen) {
1067                    handleEOL(ctx);
1068                    // paint text limit line till the end of visible editor area
1069

1070                    if (ctx.editorUI.textLimitLineVisible) { // draw limit line
1071
int lineX = ctx.startX + ctx.editorUI.textLimitWidth * ctx.editorUI.defaultSpaceWidth;
1072                        if (ctx.graphics !=null){
1073                            ctx.graphics.setColor(ctx.editorUI.textLimitLineColor);
1074                            Rectangle JavaDoc clip = ctx.graphics.getClipBounds();
1075                            if (clip.height>ctx.editorUI.getLineHeight()){
1076                                ctx.graphics.drawLine(lineX, ctx.y, lineX, ctx.y+clip.height);
1077                            }
1078                        }
1079                    }
1080                    
1081                }
1082
1083                // When drawing to graphics, the line numbers and insets will be drawn now
1084
if (ctx.graphics != null) {
1085                    graphicsSpecificUpdates(ctx);
1086                }
1087
1088                ctx.drawGraphics.setBuffer(null);
1089            } finally {
1090                ctx.drawGraphics.finish();
1091
1092                if (ctx.syntax != null) {
1093                    ctx.doc.releaseSyntax(ctx.syntax);
1094                }
1095                DocumentUtilities.SEGMENT_CACHE.releaseSegment(ctx.text);
1096                ctx.doc.readUnlock();
1097
1098            }
1099        } // synchronized on editorUI
1100
}
1101
1102
1103    static class DrawInfo implements DrawContext {
1104
1105        // DrawContext -----------------------------------------------
1106
/** Current foreground color. */
1107        Color JavaDoc foreColor;
1108
1109        /** Current background color. */
1110        Color JavaDoc backColor;
1111
1112        /** Current background color. */
1113        Color JavaDoc underlineColor;
1114
1115        /** Current background color. */
1116        Color JavaDoc waveUnderlineColor;
1117
1118        /** Color of the strike-through line or null. */
1119        Color JavaDoc strikeThroughColor;
1120
1121        /** Current font. */
1122        Font JavaDoc font;
1123
1124        /** Starting position of the drawing. */
1125        int startOffset;
1126
1127        /** Ending position of the drawing. */
1128        int endOffset;
1129
1130        /** Whether we are currently at the line begining. */
1131        boolean bol;
1132
1133        /** Whether we are currently at the line end. */
1134        boolean eol;
1135
1136        /** Editor-UI of the component for which we are drawing. */
1137        EditorUI editorUI;
1138
1139        /** Buffer from which the chars are being drawn. */
1140        char[] buffer;
1141
1142        /** Starting poisition of the buffer inside the document. */
1143        int bufferStartOffset;
1144
1145        /** Token-id of the token being drawn. */
1146        TokenID tokenID;
1147
1148        /** Token-context-path of the token being drawn. */
1149        TokenContextPath tokenContextPath;
1150
1151        /** Position of the token in the document */
1152        int tokenOffset;
1153
1154        /** Length of the token's text. */
1155        int tokenLength;
1156
1157        /** Position of the fragment of the token being drawn in the document. */
1158        int fragmentOffset;
1159
1160        /** Length of the fragment of the token in the document. */
1161        int fragmentLength;
1162
1163        // Other variables ---------------------------------------------
1164

1165        /** Draw graphics */
1166        DrawGraphics drawGraphics;
1167
1168        /** Target document position for the drawing or -1 if all the positions
1169        * are the potential targets.
1170        */

1171        int targetOffset;
1172
1173        /** Text of the document being used by drawing. */
1174        Segment JavaDoc text;
1175
1176        /** Char array in the text segment. */
1177        char[] textArray;
1178
1179        /** Syntax scanning the input. */
1180        Syntax syntax;
1181
1182        /** View being painted */
1183        View JavaDoc view;
1184
1185        /** Component being painted */
1186        JTextComponent JavaDoc component;
1187
1188        /** Document of the component. */
1189        BaseDocument doc;
1190
1191        /** Current length of the document */
1192        int docLen;
1193
1194        /** Current visual column. */
1195        int visualColumn;
1196
1197        /** Current x-coordinate. */
1198        int x;
1199
1200        /** Current y-coordinate. */
1201        int y;
1202
1203        /** Starting x-coordinate. */
1204        int startX;
1205
1206        /** Starting y-coordinate. */
1207        int startY;
1208
1209        /** Height of the line being drawn. */
1210        int lineHeight;
1211
1212        /** Default coloring of the component. */
1213        Coloring defaultColoring;
1214
1215        /** Size of the TAB character being drawn. */
1216        int tabSize;
1217
1218        /** Widest width of the line that was painted. */
1219        int widestWidth;
1220
1221        /** Whether the draw should continue or not. */
1222        boolean continueDraw;
1223
1224        /** Line number of the first painted line. */
1225        int startLineNumber;
1226
1227        /** Index of the line being drawn. It is added
1228        * to the startLineNumber to form the resulting line number.
1229        */

1230        int lineIndex;
1231
1232        /** Array of the start positions of all the lines drawn. */
1233        int[] lineStartOffsets;
1234
1235        /** Characters forming the line-number. It is reused for drawing
1236        * all the lines.
1237        */

1238        char[] lineNumberChars;
1239
1240        /** Coloring for the line-number. */
1241        Coloring lineNumberColoring;
1242
1243        /** Graphics object. It can be null when not drawing to the component. */
1244        Graphics JavaDoc graphics;
1245
1246        /** Whether line-numbers are visible and allowed by the draw-graphics. */
1247        boolean lineNumbering;
1248
1249        /** Whether the line-numbers should be painted after each is painted.
1250        * By default the line-numbers are drawn as one block at the end
1251        * of the drawing.
1252        */

1253        boolean syncedLineNumbering;
1254
1255        /** Array of the draw-layers to be used in the painting */
1256        DrawLayer[] layers;
1257
1258        /** Whether the particular layer is currently active or not. */
1259        boolean[] layerActives;
1260
1261        /** Next position where the layer will be asked whether it's active
1262        * or not.
1263        */

1264        int[] layerActivityChangeOffsets;
1265
1266        /** Position where either the next draw-mark is
1267        * or which matches the activity-change-offset of one or more layers.
1268        */

1269        int updateOffset;
1270
1271        /** Next activity-change-offset of one or more layers. */
1272        int layerUpdateOffset;
1273
1274        /** Update of the layers because of the draw-mark is at the given position.
1275        * False means the update is because the activity-change-offset
1276        * was reached.
1277        */

1278        boolean drawMarkUpdate;
1279
1280        /** List of draw marks in the painted area. */
1281        List JavaDoc drawMarkList;
1282        
1283        /** Current index in the drawMarkList */
1284        int drawMarkIndex;
1285
1286        /** Current draw-mark */
1287        MarkFactory.DrawMark drawMark;
1288
1289        /** Position of the current draw-mark */
1290        int drawMarkOffset;
1291
1292        /** Length of the current token that was already drawn. */
1293        int drawnLength;
1294
1295        /** Offset of the fragment starting character in the buffer */
1296        int fragmentStartIndex;
1297
1298        /** Whether the fragment contains TABs only. */
1299        boolean tabsFragment;
1300
1301        /** Width of one space character for the current context font. */
1302        int spaceWidth;
1303
1304        /** Display width of the fragment */
1305        int fragmentWidth;
1306
1307        /** Number of characters that the fragment stands for. It can differ
1308        * from fragmentLength for tabsFragment.
1309        */

1310        int fragmentCharCount;
1311
1312        public Color JavaDoc getForeColor() {
1313            return foreColor;
1314        }
1315
1316        public void setForeColor(Color JavaDoc foreColor) {
1317            this.foreColor = foreColor;
1318        }
1319
1320        public Color JavaDoc getBackColor() {
1321            return backColor;
1322        }
1323
1324        public void setBackColor(Color JavaDoc backColor) {
1325            this.backColor = backColor;
1326        }
1327
1328        public Color JavaDoc getUnderlineColor() {
1329            return underlineColor;
1330        }
1331
1332        public void setUnderlineColor(Color JavaDoc underlineColor) {
1333            this.underlineColor = underlineColor;
1334        }
1335
1336        public Color JavaDoc getWaveUnderlineColor() {
1337            return waveUnderlineColor;
1338        }
1339
1340        public void setWaveUnderlineColor(Color JavaDoc waveUnderlineColor) {
1341            this.waveUnderlineColor = waveUnderlineColor;
1342        }
1343
1344        public Color JavaDoc getStrikeThroughColor() {
1345            return strikeThroughColor;
1346        }
1347
1348        public void setStrikeThroughColor(Color JavaDoc strikeThroughColor) {
1349            this.strikeThroughColor = strikeThroughColor;
1350        }
1351
1352        public Font JavaDoc getFont() {
1353            return font;
1354        }
1355
1356        public void setFont(Font JavaDoc font) {
1357            this.font = font;
1358        }
1359
1360        public int getStartOffset() {
1361            return startOffset;
1362        }
1363
1364        public int getEndOffset() {
1365            return endOffset;
1366        }
1367
1368        public boolean isBOL() {
1369            return bol;
1370        }
1371
1372        public boolean isEOL() {
1373            return eol;
1374        }
1375
1376        public EditorUI getEditorUI() {
1377            return editorUI;
1378        }
1379
1380        public char[] getBuffer() {
1381            return buffer;
1382        }
1383
1384        public int getBufferStartOffset() {
1385            return bufferStartOffset;
1386        }
1387
1388        public TokenID getTokenID() {
1389            return tokenID;
1390        }
1391
1392        public TokenContextPath getTokenContextPath() {
1393            return tokenContextPath;
1394        }
1395
1396        public int getTokenOffset() {
1397            return tokenOffset;
1398        }
1399
1400        public int getTokenLength() {
1401            return tokenLength;
1402        }
1403
1404        public int getFragmentOffset() {
1405            return fragmentOffset;
1406        }
1407
1408        public int getFragmentLength() {
1409            return fragmentLength;
1410        }
1411
1412    }
1413    
1414    class PreinitializedDrawEngine extends DrawEngine{
1415        
1416        DrawGraphics drawGraphics;
1417        DrawInfo drawInfo;
1418        
1419        public PreinitializedDrawEngine(DrawGraphics drawGraphics){
1420            super();
1421            this.drawGraphics = drawGraphics;
1422        }
1423
1424        public void release(){
1425            drawInfo.drawGraphics.setBuffer(null);
1426            drawInfo.drawGraphics.finish();
1427            
1428            if (drawInfo.syntax != null) {
1429                drawInfo.doc.releaseSyntax(drawInfo.syntax);
1430            }
1431            DocumentUtilities.SEGMENT_CACHE.releaseSegment(drawInfo.text);
1432        }
1433        
1434        public void preinitialize(BaseDocument doc, EditorUI editorUI, int startOffset, int endOffset,
1435        int startX, int startY, int targetOffset) throws BadLocationException JavaDoc{
1436            // Draw-context and other info
1437
drawInfo = new DrawInfo();
1438            drawInfo.drawGraphics = drawGraphics;
1439            drawInfo.editorUI = editorUI;
1440            drawInfo.startOffset = startOffset;
1441            drawInfo.endOffset = endOffset;
1442            drawInfo.startX = startX;
1443            drawInfo.startY = startY;
1444            drawInfo.targetOffset = targetOffset;
1445            
1446            synchronized (editorUI) { // lock operations manipulating draw layer chain
1447
drawInfo.doc = doc;
1448                if (drawInfo.doc == null) { // no base-document available
1449
return;
1450                }
1451                
1452                try{
1453                    drawInfo.text = DocumentUtilities.SEGMENT_CACHE.getSegment();
1454                    drawInfo.syntax = drawInfo.doc.getFreeSyntax();
1455                    drawInfo.doc.readLock();
1456                    
1457                    drawInfo.component = editorUI.getComponent();
1458                    drawInfo.docLen = drawInfo.doc.getLength();
1459                    
1460                    if (drawInfo.startOffset > drawInfo.docLen || drawInfo.endOffset > drawInfo.docLen) {
1461                        return;
1462                    }
1463                    
1464                    /* Correct the ending position to be at the begining
1465                     * of the next line. The only exception is when the
1466                     * endOffset is equal to docLen.
1467                     */

1468                    if (drawInfo.endOffset < drawInfo.docLen) {
1469                        drawInfo.endOffset++;
1470                    }
1471                    
1472                    // Initialize the draw-info
1473
initInfo(drawInfo);
1474                } finally{
1475                    drawInfo.doc.readUnlock();
1476                }
1477            }
1478        }
1479        
1480        void draw(DrawGraphics dg, EditorUI editorUI, int startOffset, int endOffset,
1481        int startX, int startY, int targetOffset) throws BadLocationException JavaDoc {
1482            // Some correctness tests at the begining
1483
if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
1484            || startX < 0 || startY < 0
1485            ) {
1486                return;
1487            }
1488            if (dg == null){
1489                dg = drawGraphics;
1490            }else{
1491                super.draw(dg, editorUI, startOffset, endOffset, startX, startY, targetOffset);
1492                return;
1493            }
1494            
1495            synchronized (editorUI){
1496                DrawInfo ctx = drawInfo;
1497                ctx.doc.readLock();
1498                try{
1499                    do {
1500                        ctx.tokenID = ctx.syntax.nextToken();
1501
1502                        // Draw the current token
1503
drawCurrentToken(ctx);
1504
1505                    } while (ctx.continueDraw && ctx.tokenID != null);
1506
1507                    if (ctx.endOffset == ctx.docLen) {
1508                        handleEOL(ctx);
1509                    }
1510
1511                    ctx.editorUI.updateVirtualWidth(ctx.widestWidth
1512                    + ctx.editorUI.lineNumberWidth + 2 * ctx.editorUI.defaultSpaceWidth);
1513
1514                    // When drawing to graphics, the line numbers and insets will be drawn now
1515
if (ctx.graphics != null) {
1516                        graphicsSpecificUpdates(ctx);
1517                    }
1518                }finally{
1519                    ctx.doc.readUnlock();
1520                }
1521            }
1522        }
1523        
1524    }
1525    
1526
1527}
1528
Popular Tags