KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > diff > builtin > visualizer > LinesComponent


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.modules.diff.builtin.visualizer;
21
22 import java.awt.Dimension JavaDoc;
23 import java.awt.Rectangle JavaDoc;
24 import java.awt.Graphics JavaDoc;
25 import java.awt.Color JavaDoc;
26 import java.awt.Font JavaDoc;
27 import java.util.*;
28 import javax.accessibility.AccessibleContext JavaDoc;
29 import javax.accessibility.AccessibleRole JavaDoc;
30 import javax.swing.*;
31 import javax.swing.text.*;
32 import java.awt.FontMetrics JavaDoc;
33 import java.awt.Insets JavaDoc;
34
35 import org.openide.util.NbBundle;
36
37 import org.netbeans.editor.Coloring;
38 import org.netbeans.editor.EditorUI;
39 import org.netbeans.editor.FontMetricsCache;
40 import org.netbeans.editor.Settings;
41 import org.netbeans.editor.SettingsChangeEvent;
42 import org.netbeans.editor.SettingsChangeListener;
43 import org.netbeans.editor.SettingsDefaults;
44 import org.netbeans.editor.SettingsNames;
45
46 /** GlyphGutter is component for displaying line numbers and annotation
47  * glyph icons. Component also allow to "cycle" through the annotations. It
48  * means that if there is more than one annotation on the line, only one of them
49  * might be visible. And clicking the special cycling button in the gutter the user
50  * can cycle through the annotations.
51  *
52  * @author David Konecny
53  * @since 07/2001
54  */

55
56 public class LinesComponent extends JComponent implements javax.accessibility.Accessible JavaDoc, SettingsChangeListener {
57
58     
59     /** Document to which this gutter is attached*/
60     private JEditorPane editorPane;
61     
62     /** Backroung color of the gutter */
63     private Color JavaDoc backgroundColor;
64     
65     /** Foreground color of the gutter. Used for drawing line numbers. */
66     private Color JavaDoc foreColor;
67     
68     /** Font used for drawing line numbers */
69     private Font JavaDoc font;
70     
71     /** Height of the line as it was calculated in EditorUI. */
72     private int lineHeight = 1;
73
74     private float lineHeightCorrection = 1.0f;
75
76     /** Map holding the [name, coloring] pairs */
77     private Map coloringMap;
78
79     /** Flag whther the gutter was initialized or not. The painting is disabled till the
80      * gutter is not initialized */

81     private boolean init;
82     
83     /** Width of the column used for drawing line numbers. The value contains
84      * also line number margins. */

85     private int numberWidth;
86
87     /** Whether the line numbers are shown or not */
88     private boolean showLineNumbers = true;
89     
90     /** The gutter height is enlarged by number of lines which specifies this constant */
91     private static final int ENLARGE_GUTTER_HEIGHT = 300;
92     
93     /** The hightest line number. This value is used for calculating width of the gutter */
94     private int highestLineNumber = 0;
95
96     /** Holds value of property lineNumberMargin. */
97     private Insets JavaDoc lineNumberMargin;
98     
99     /** Holds value of property lineNumberDigitWidth. */
100     private int lineNumberDigitWidth;
101     
102     /** Holds value of property lineAscent. */
103     private int lineAscent;
104     
105     private LinkedList<String JavaDoc> linesList;
106     
107     /** Holds value of property activeLine. */
108     private int activeLine = -1;
109
110     private static final long serialVersionUID = -4861542695772182147L;
111     
112     public LinesComponent(JEditorPane pane) {
113         super();
114         init = false;
115         editorPane = pane;
116         font = editorPane.getFont();
117         foreColor = editorPane.getForeground();
118         backgroundColor = editorPane.getBackground();
119         setLineNumberDigitWidth(10);
120         setLineNumberMargin(new Insets JavaDoc(2, 2, 2, 4));
121         Settings.addSettingsChangeListener(this); // Is added weakly.
122
init();
123     }
124
125     /* Read accessible context
126      * @return - accessible context
127      */

128     public AccessibleContext JavaDoc getAccessibleContext () {
129         if (accessibleContext == null) {
130             accessibleContext = new AccessibleJComponent() {
131                 public AccessibleRole JavaDoc getAccessibleRole() {
132                     return AccessibleRole.PANEL;
133                 }
134             };
135         }
136         return accessibleContext;
137     }
138
139     /** Do initialization of the glyph gutter*/
140     protected void init() {
141         createLines();
142         getAccessibleContext().setAccessibleName(NbBundle.getMessage(LinesComponent.class, "ACSN_Lines_Component")); // NOI18N
143
getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(LinesComponent.class, "ACSD_Lines_Component")); // NOI18N
144
}
145     
146     private void createLines() {
147         linesList = new LinkedList<String JavaDoc>();
148         int lineCnt;
149         StyledDocument doc = (StyledDocument)editorPane.getDocument();
150         int lastOffset = doc.getEndPosition().getOffset();
151         lineCnt = org.openide.text.NbDocument.findLineNumber(doc, lastOffset);
152         for (int i = 0; i < lineCnt; i++) {
153             linesList.add(Integer.toString(i + 1));
154         }
155     }
156     
157     public void addEmptyLines(int line, int count) {
158         boolean appending = line > linesList.size();
159         for (int i = 0; i < count; i++) {
160             if (appending) {
161                 linesList.add("");
162             } else {
163                 linesList.add(line, "");
164             }
165         }
166     }
167     
168     /**
169      * Insert line numbers. If at the end, then line numbers are added to the end of the component.
170      * If in the middle, subsequent lines are overwritten.
171      */

172     public void insertNumbers(int line, int startNum, int count) {
173         boolean appending = line >= linesList.size();
174         if (appending) {
175             for (int i = 0; i < count; i++, startNum++) {
176                 linesList.add(Integer.toString(startNum));
177             }
178         } else {
179             int toAdd = Math.max(line + count - linesList.size(), 0);
180             count -= toAdd;
181             for (int i = 0; i < count; i++, startNum++, line++) {
182                 linesList.set(line, Integer.toString(startNum));
183             }
184             for (int i = 0; i < toAdd; i++, startNum++) {
185                 linesList.add(Integer.toString(startNum));
186             }
187         }
188     }
189     
190     /*
191      * Test method.
192      *
193     private void dumpResultLineNumbers() {
194         System.out.print("LinesComponent: linesList = ");
195         boolean was = false;
196         for (int i = 0; i < linesList.size(); i++) {
197             System.out.print(linesList.get(i)+", ");
198         }
199         System.out.println("");
200     }
201      */

202     
203     /**
204      * Remove line numbers and leave the corresponding part of the lines component empty.
205      * If at the end, then an empty space is added to the end of the component.
206      * If in the middle, subsequent lines are overwritten by an empty space.
207      */

208     public void removeNumbers(int line, int count) {
209         boolean appending = line >= linesList.size();
210         if (appending) {
211             for (int i = 0; i < count; i++) {
212                 linesList.add("");
213             }
214         } else {
215             int toAdd = Math.max(line + count - linesList.size(), 0);
216             count -= toAdd;
217             for (int i = 0; i < count; i++, line++) {
218                 linesList.set(line, "");
219             }
220             for (int i = 0; i < toAdd; i++) {
221                 linesList.add("");
222             }
223         }
224     }
225     
226     /**
227      * Shrink the component, so that it will have <code>numLines</code> number of lines.
228      * @param numLines The new number of lines
229      */

230     public void shrink(int numLines) {
231         while (linesList.size() > numLines) {
232             linesList.remove(numLines);
233         }
234     }
235     
236     /** Update colors, fonts, sizes and invalidate itself. This method is
237      * called from EditorUI.update() */

238     private void updateState(Graphics JavaDoc g) {
239         Class JavaDoc kitClass = editorPane.getEditorKit().getClass();
240         Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.LINE_HEIGHT_CORRECTION);
241         //System.out.println("Line height correction = "+value);
242
if (!(value instanceof Float JavaDoc) || ((Float JavaDoc)value).floatValue() < 0) {
243             value = SettingsDefaults.defaultLineHeightCorrection;
244         }
245         lineHeightCorrection = ((Float JavaDoc)value).floatValue();
246         //System.out.println(" => correction = "+lineHeightCorrection);
247
Map cm = getColoringMap();
248         Object JavaDoc colValue = cm.get(SettingsNames.LINE_NUMBER_COLORING);
249         //System.out.println("Line number coloring = "+colValue);
250
Coloring col = null;
251         if (colValue != null && colValue instanceof Coloring) {
252             col = (Coloring)colValue;
253         } else {
254             col = SettingsDefaults.defaultLineNumberColoring;
255         }
256         foreColor = col.getForeColor();
257         if (foreColor == null) {
258             foreColor = ((Coloring) cm.get(SettingsNames.DEFAULT_COLORING)).getForeColor();
259         }
260         backgroundColor = col.getBackColor();
261         if (backgroundColor == null) {
262             backgroundColor = ((Coloring) cm.get(SettingsNames.DEFAULT_COLORING)).getBackColor();
263         }
264         //System.out.println(" => foreground = "+foreColor+", background = "+backgroundColor);
265

266         font = col.getFont();
267         if (font == null) {
268             font = ((Coloring) cm.get(SettingsNames.DEFAULT_COLORING)).getFont();
269         }
270         FontMetrics JavaDoc fm = g.getFontMetrics(font);
271         /*
272         int maxHeight = 1;
273         int maxAscent = 0;
274         if (fm != null) {
275             maxHeight = Math.max(maxHeight, fm.getHeight());
276             maxAscent = Math.max(maxAscent, fm.getAscent());
277         }
278
279         // Apply lineHeightCorrection
280         lineHeight = (int)(maxHeight * lineHeightCorrection);
281         lineAscent = (int)(maxAscent * lineHeightCorrection);
282          */

283         updateLineHeight(g);
284         //System.out.println("lineheight=" + lineHeight);//+", fm height = "+fm.getHeight());
285
//System.out.println("lineascent=" + lineAscent);//+", fm ascent = "+fm.getAscent());
286
showLineNumbers = true;
287
288         /*
289         lineHeight = editorUI.getLineHeight();
290         lineAscent = editorUI.getLineAscent();
291         System.out.println("lineHeight = "+lineHeight);
292         System.out.println("lineascent=" + lineAscent);
293
294         showLineNumbers = editorUI.isLineNumberEnabled();
295          */

296
297         init = true;
298
299         // initialize the value with current number of lines
300
if (highestLineNumber <= getLineCount()) {
301             highestLineNumber = getLineCount();
302         }
303 // System.out.println("highestLineNumber=" + highestLineNumber);
304
// width of a digit..
305
int maxWidth = 1;
306         char[] digit = new char[1]; // will be used for '0' - '9'
307
for (int i = 0; i <= 9; i++) {
308              digit[0] = (char)('0' + i);
309              maxWidth = Math.max(maxWidth, fm.charsWidth(digit, 0, 1));
310         }
311         setLineNumberDigitWidth(maxWidth);
312 // System.out.println("maxwidth=" + maxWidth);
313
// System.out.println("numner of lines=" + highestLineNumber);
314

315         resize();
316     }
317     
318     private void updateLineHeight(Graphics JavaDoc g) {
319         //System.err.println("EditorUI.updateLineHeight(): Computing lineHeight ...");
320
Map cm = getColoringMap();
321         Iterator i = cm.entrySet().iterator();
322         int maxHeight = 1;
323         int maxAscent = 0;
324         while (i.hasNext()) {
325             Map.Entry me = (Map.Entry)i.next();
326             String JavaDoc coloringName = (String JavaDoc)me.getKey();
327             Coloring c = (Coloring)me.getValue();
328             if (c != null) {
329                 Font JavaDoc font = c.getFont();
330                 if (font != null && (c.getFontMode() & Coloring.FONT_MODE_APPLY_SIZE) != 0) {
331                     FontMetrics JavaDoc fm = g.getFontMetrics(font);
332                     if (fm != null) {
333                         /*if (debugUpdateLineHeight) {
334                             if (maxHeight < fm.getHeight()) {
335                                 System.err.println("Updating maxHeight from "
336                                     + maxHeight + " to " + fm.getHeight()
337                                     + ", coloringName=" + coloringName
338                                     + ", font=" + font
339                                 );
340                             }
341
342                             if (maxHeight < fm.getHeight()) {
343                                 System.err.println("Updating maxAscent from "
344                                     + maxAscent + " to " + fm.getAscent()
345                                     + ", coloringName=" + coloringName
346                                     + ", font=" + font
347                                 );
348                             }
349                         }
350                         */

351                         maxHeight = Math.max(maxHeight, fm.getHeight());
352                         maxAscent = Math.max(maxAscent, fm.getAscent());
353                     }
354                 }
355             }
356         }
357
358         // Apply lineHeightCorrection
359
lineHeight = (int)(maxHeight * lineHeightCorrection);
360         lineAscent = (int)(maxAscent * lineHeightCorrection);
361
362     }
363     
364     private Map getColoringMap() {
365         if (coloringMap == null) {
366             coloringMap = EditorUIHelper.getSharedColoringMapFor(editorPane.getEditorKit().getClass());
367         }
368         return coloringMap;
369     }
370
371     protected void resize() {
372         Dimension JavaDoc dim = new Dimension JavaDoc();
373 // System.out.println("resizing...................");
374
dim.width = getWidthDimension();
375         dim.height = getHeightDimension();
376         // enlarge the gutter so that inserting new lines into
377
// document does not cause resizing too often
378
dim.height += ENLARGE_GUTTER_HEIGHT * lineHeight;
379         
380         numberWidth = getLineNumberWidth();
381         setPreferredSize(dim);
382
383         revalidate();
384     }
385
386     /** Return number of lines in the document */
387     protected int getLineCount() {
388         return linesList.size();
389     }
390
391     /** Gets number of digits in the number */
392     protected int getDigitCount(int number) {
393         return Integer.toString(number).length();
394     }
395
396     protected int getLineNumberWidth() {
397         int newWidth = 0;
398         Insets JavaDoc insets = getLineNumberMargin();
399         if (insets != null) {
400             newWidth += insets.left + insets.right;
401         }
402         newWidth += (getDigitCount(highestLineNumber) + 1) * getLineNumberDigitWidth();
403 // System.out.println("new width=" + newWidth);
404
return newWidth;
405     }
406
407     protected int getWidthDimension() {
408         int newWidth = 0;
409         
410         if (showLineNumbers) {
411             newWidth += getLineNumberWidth();
412         }
413
414         return newWidth;
415     }
416     
417     protected int getHeightDimension() {
418         return highestLineNumber * lineHeight /*TEMP+ (int)editorPane.getSize().getHeight() */;
419     }
420     
421     /** Paint the gutter itself */
422     public void paintComponent(Graphics JavaDoc g) {
423
424         super.paintComponent(g);
425         if (!init) {
426             updateState(g);
427         }
428         // return;
429

430         Rectangle JavaDoc drawHere = g.getClipBounds();
431
432         // Fill clipping area with dirty brown/orange.
433
g.setColor(backgroundColor);
434         g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
435
436         g.setFont(font);
437         g.setColor(foreColor);
438
439         FontMetrics JavaDoc fm = FontMetricsCache.getFontMetrics(font, this);
440         int rightMargin = 0;
441         Insets JavaDoc margin = getLineNumberMargin();
442         if (margin != null)
443             rightMargin = margin.right;
444         // calculate the first line which must be drawn
445
int line = (int)( (float)drawHere.y / (float)lineHeight );
446         if (line > 0)
447             line--;
448
449         // calculate the Y of the first line
450
int y = line * lineHeight;
451
452         if (showLineNumbers) {
453             int lastLine = (int)( (float)(drawHere.y+drawHere.height) / (float)lineHeight )+1;
454             if (lastLine > highestLineNumber) {
455                 int prevHighest = highestLineNumber;
456                 highestLineNumber = lastLine;
457                 if (getDigitCount(highestLineNumber) > getDigitCount(prevHighest)) {
458 // System.out.println("resizing in paintComponent()");
459
// System.out.println("lastline=" + lastLine);
460
// System.out.println("highestLineNumber=" + highestLineNumber);
461
resize();
462                     return;
463                 }
464             }
465         }
466         
467         
468         // draw liune numbers and annotations while we are in visible area
469
// "+(lineHeight/2)" means to don't draw less than half of the line number
470
while ( (y+(lineHeight/2)) <= (drawHere.y + drawHere.height) )
471         {
472             // draw line numbers if they are turned on
473
if (showLineNumbers) {
474                 String JavaDoc lineStr = null;
475                 if (line < linesList.size()) {
476                     lineStr = linesList.get(line);
477                 }
478                 if (lineStr == null) {
479                     lineStr = "";
480                 }
481                 String JavaDoc activeSymbol = "*";
482                 int lineNumberWidth = fm.stringWidth(lineStr);
483                 if (line == activeLine - 1) {
484                     lineStr = lineStr + activeSymbol;
485                 }
486                 int activeSymbolWidth = fm.stringWidth(activeSymbol);
487                 lineNumberWidth = lineNumberWidth + activeSymbolWidth;
488                 g.drawString(lineStr, numberWidth-lineNumberWidth-rightMargin, y + getLineAscent());
489             }
490             
491             y += lineHeight;
492             line++;
493         }
494     }
495
496     /** Data for the line has changed and the line must be redraw. */
497     public void changedLine(int line) {
498         
499         if (!init)
500             return;
501         
502         // redraw also lines around - three lines will be redrawn
503
if (line > 0)
504             line--;
505         int y = line * lineHeight;
506         
507         repaint(0, y, (int)getSize().getWidth(), 3*lineHeight);
508         checkSize();
509     }
510
511     /** Repaint whole gutter.*/
512     public void changedAll() {
513
514         if (!init)
515             return;
516
517 /* int lineCnt;
518         try {
519             lineCnt = Utilities.getLineOffset(doc, doc.getLength()) + 1;
520         } catch (BadLocationException e) {
521             lineCnt = 1;
522         }
523  */

524
525         repaint();
526         checkSize();
527     }
528
529     protected void checkSize() {
530         int count = getLineCount();
531         if (count > highestLineNumber) {
532             highestLineNumber = count;
533         }
534         Dimension JavaDoc dim = getPreferredSize();
535         if (getWidthDimension() > dim.width ||
536             getHeightDimension() > dim.height) {
537                 resize();
538         }
539     }
540     
541     /** Getter for property lineNumberMargin.
542      * @return Value of property lineNumberMargin.
543      */

544     public Insets JavaDoc getLineNumberMargin() {
545         return this.lineNumberMargin;
546     }
547     
548     /** Setter for property lineNumberMargin.
549      * @param lineNumberMargin New value of property lineNumberMargin.
550      */

551     public void setLineNumberMargin(Insets JavaDoc lineNumberMargin) {
552         this.lineNumberMargin = lineNumberMargin;
553     }
554     
555     /** Getter for property lineNumberDigitWidth.
556      * @return Value of property lineNumberDigitWidth.
557      */

558     public int getLineNumberDigitWidth() {
559         return this.lineNumberDigitWidth;
560     }
561     
562     /** Setter for property lineNumberDigitWidth.
563      * @param lineNumberDigitWidth New value of property lineNumberDigitWidth.
564      */

565     public void setLineNumberDigitWidth(int lineNumberDigitWidth) {
566         this.lineNumberDigitWidth = lineNumberDigitWidth;
567     }
568     
569     /** Getter for property lineAscent.
570      * @return Value of property lineAscent.
571      */

572     public int getLineAscent() {
573         return this.lineAscent;
574     }
575     
576     /** Setter for property lineAscent.
577      * @param lineAscent New value of property lineAscent.
578      */

579     public void setLineAscent(int lineAscent) {
580         this.lineAscent = lineAscent;
581     }
582     
583     /** Getter for property activeLine.
584      * @return Value of property activeLine.
585      */

586     public int getActiveLine() {
587         return this.activeLine;
588     }
589     
590     /** Setter for property activeLine.
591      * @param activeLine New value of property activeLine.
592      */

593     public void setActiveLine(int activeLine) {
594         this.activeLine = activeLine;
595     }
596     
597     public void settingsChange(SettingsChangeEvent evt) {
598         coloringMap = null;
599         init = false;
600         repaint();
601     }
602     
603     private static class EditorUIHelper extends EditorUI {
604         public EditorUIHelper () {}
605         
606         /** Gets the coloring map that can be shared by the components
607           * with the same kit. Only the component coloring map is provided.
608           */

609         public static Map getSharedColoringMapFor(Class JavaDoc kitClass) {
610             return EditorUIHelper.getSharedColoringMap(kitClass);
611         }
612         
613     }
614
615 }
616
Popular Tags