KickJava   Java API By Example, From Geeks To Geeks.

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


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.*;
23 import java.awt.event.FocusAdapter JavaDoc;
24 import java.awt.event.FocusEvent JavaDoc;
25 import java.util.Hashtable JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.beans.PropertyChangeListener JavaDoc;
30 import java.beans.PropertyChangeEvent JavaDoc;
31 import java.beans.PropertyChangeSupport JavaDoc;
32 import javax.swing.Action JavaDoc;
33 import javax.swing.JComponent JavaDoc;
34 import javax.swing.JViewport JavaDoc;
35 import javax.swing.JPanel JavaDoc;
36 import javax.swing.JScrollPane JavaDoc;
37 import javax.swing.JToolBar JavaDoc;
38 import javax.swing.SwingUtilities JavaDoc;
39 import javax.swing.event.ChangeListener JavaDoc;
40 import javax.swing.event.ChangeEvent JavaDoc;
41 import javax.swing.text.JTextComponent JavaDoc;
42 import javax.swing.text.Caret JavaDoc;
43 import javax.swing.text.BadLocationException JavaDoc;
44 import javax.swing.text.Position JavaDoc;
45 import javax.swing.text.View JavaDoc;
46 import javax.swing.plaf.TextUI JavaDoc;
47 import org.netbeans.modules.editor.lib.HighlightingDrawLayer;
48
49 /**
50 * Editor UI for the component. All the additional UI features
51 * like advanced scrolling, info about fonts, abbreviations,
52 * keyword matching are based on this class.
53 *
54 * @author Miloslav Metelka
55 * @version 1.00
56 */

57 public class EditorUI implements ChangeListener JavaDoc, PropertyChangeListener JavaDoc, SettingsChangeListener {
58
59     public static final String JavaDoc OVERWRITE_MODE_PROPERTY = "overwriteMode"; // NOI18N
60

61     public static final String JavaDoc COMPONENT_PROPERTY = "component"; // NOI18N
62

63     /** Default scrolling type is used for the standard
64     * setDot() call. If the area is on the screen, it
65     * jumps to it, otherwise it centers the requested area
66     * vertically in the middle of the window and it uses
67     * smallest covering on the right side.
68     */

69     public static final int SCROLL_DEFAULT = 0;
70
71     /** Scrolling type used for regular caret moves.
72     * The scrollJump is used when the caret requests area outside the screen.
73     */

74     public static final int SCROLL_MOVE = 1;
75
76     /** Scrolling type where the smallest covering
77     * for the requested rectangle is used. It's useful
78     * for going to the end of the line for example.
79     */

80     public static final int SCROLL_SMALLEST = 2;
81
82     /** Scrolling type for find operations, that can
83     * request additional configurable area in each
84     * direction, so the context around is visible too.
85     */

86     public static final int SCROLL_FIND = 3;
87
88
89     private static final Insets NULL_INSETS = new Insets(0, 0, 0, 0);
90     
91     private static final Insets DEFAULT_INSETS = new Insets(0, SettingsDefaults.defaultTextLeftMarginWidth.intValue(), 0, 0);
92
93     private static final Dimension NULL_DIMENSION = new Dimension(0, 0);
94
95     /** Default margin on the left and right side of the line number */
96     public static final Insets defaultLineNumberMargin = new Insets(0, 3, 0, 3);
97
98     private static final boolean debugUpdateLineHeight
99     = Boolean.getBoolean("netbeans.debug.editor.updateLineHeight");
100
101     /** Map holding the coloring maps for the different languages.
102      * It helps to minimize the amount of the coloring maps
103      * and also save the time necessary for their creation.
104      */

105     private static final HashMap JavaDoc sharedColoringMaps = new HashMap JavaDoc(57);
106     private static final SettingsChangeListener clearingListener
107         = new SettingsChangeListener() {
108             public void settingsChange(SettingsChangeEvent evt) {
109                 // Fired when the Settings are locked
110
sharedColoringMaps.clear();
111             }
112         };
113
114
115     static {
116         Settings.addSettingsChangeListener( clearingListener );
117     }
118
119
120     /** Component this extended UI is related to. */
121     private JTextComponent JavaDoc component;
122
123     private JComponent JavaDoc extComponent;
124     
125     private JToolBar JavaDoc toolBarComponent;
126
127     /** Property change support for firing property changes */
128     PropertyChangeSupport JavaDoc propertyChangeSupport = new PropertyChangeSupport JavaDoc(this);
129
130     /** Document for the case ext ui is constructed without the component */
131     private BaseDocument printDoc;
132
133     /** Draw layer chain */
134     private DrawLayerList drawLayerList = new DrawLayerList();
135
136     /** Map holding the [name, coloring] pairs */
137     private Map JavaDoc coloringMap;
138
139     /** Character (or better line) height. Particular view can use a different
140     * character height however most views will probably use this one.
141     */

142     private int lineHeight = 1; // prevent possible division by zero
143

144     private float lineHeightCorrection = 1.0f;
145
146     /** Ascent of the line which is maximum ascent of all the fonts used. */
147     private int lineAscent;
148
149     /** Width of the space in the default coloring's font */
150     int defaultSpaceWidth = 1;
151
152     /** Should the search words be colored? */
153     boolean highlightSearch;
154     
155     /** Enable displaying line numbers. Both this flag and <tt>lineNumberVisibleSetting</tt>
156     * must be true to have the line numbers visible in the window. This flag is false
157     * by default. It's turned on automatically if the getExtComponent is called.
158     */

159     boolean lineNumberEnabled;
160
161     /** This flag corresponds to the LINE_NUMBER_VISIBLE setting. */
162     boolean lineNumberVisibleSetting;
163
164     /** Whether to show line numbers or not. This flag is obtained using bitwise AND
165     * operation on lineNumberEnabled flag and lineNumberVisibleSetting flag.
166     */

167     boolean lineNumberVisible;
168
169     /** Line number total width with indentation. It includes left and right
170     * line-number margins and lineNumberDigitWidth * lineNumberMaxDigitCount.
171     */

172     int lineNumberWidth;
173
174     /** Width of one digit used for line numbering. It's based
175     * on the information from the line coloring.
176     */

177     int lineNumberDigitWidth;
178
179     /** Current maximum count of digits in line number */
180     int lineNumberMaxDigitCount;
181
182     /** Margin between the line-number bar and the text. */
183     int textLeftMarginWidth;
184
185     /** This is the full margin around the text. The left margin
186     * is an addition of component's margin and lineNumberWidth
187     * and textLeftMarginWidth.
188     */

189     Insets textMargin = DEFAULT_INSETS;
190
191     /** How much columns/lines to add when the scroll is performed
192     * so that the component is not scrolled so often.
193     * Negative number means portion of the extent width/height
194     */

195     Insets scrollJumpInsets;
196
197     /** How much columns/lines to add when the scroll is performed
198     * so that the component is not scrolled so often.
199     * Negative number means portion of the extent width/height
200     */

201     Insets scrollFindInsets;
202
203     /** EditorUI properties */
204     Hashtable JavaDoc props = new Hashtable JavaDoc(11);
205
206     boolean textLimitLineVisible;
207
208     Color textLimitLineColor;
209
210     int textLimitWidth;
211
212     private Rectangle lastExtentBounds = new Rectangle();
213
214     private Dimension componentSizeIncrement = new Dimension();
215
216     private Abbrev abbrev;
217
218     private WordMatch wordMatch;
219
220     private Object JavaDoc componentLock;
221
222     /** Status bar */
223     StatusBar statusBar;
224
225     private FocusAdapter JavaDoc focusL;
226
227     Map JavaDoc renderingHints;
228
229     /** Glyph gutter used for drawing of annotation glyph icons. */
230     private GlyphGutter glyphGutter = null;
231
232     /** The line numbers can be shown in glyph gutter and therefore it is necessary
233      * to disable drawing of lines here. During the printing on the the other hand, line
234      * numbers must be visible. */

235     private boolean disableLineNumbers = true;
236
237     /** Left right corner of the JScrollPane */
238     private JPanel JavaDoc glyphCorner;
239     
240     /** Whether printing coloring map or component coloring map should be used. */
241     private boolean usePrintColoringMap;
242     
243     public static final String JavaDoc LINE_HEIGHT_CHANGED_PROP = "line-height-changed-prop"; //NOI18N
244

245     /** init paste action #39678 */
246     private static boolean isPasteActionInited = false;
247
248     /** Construct extended UI for the use with a text component */
249     public EditorUI() {
250         this.usePrintColoringMap = false;
251         
252         Settings.addSettingsChangeListener(this);
253
254         focusL = new FocusAdapter JavaDoc() {
255                      public void focusGained(FocusEvent JavaDoc evt) {
256                          Registry.activate(getComponent());
257                          /* Fix of #25475 - copyAction's enabled flag
258                           * must be updated on focus change
259                           */

260                          stateChanged(null);
261                          if (component!=null){
262                             BaseTextUI ui = (BaseTextUI)component.getUI();
263                             if (ui!=null) ui.refresh();
264                          }
265                      }
266                  };
267
268         HighlightingDrawLayer.hookUp(this);
269     }
270
271     /** Construct extended UI for printing the given document */
272     public EditorUI(BaseDocument printDoc) {
273         this(printDoc, true, true);
274     }
275         
276     /**
277      * Construct extended UI for printing the given document
278      * and specify which set of colors should be used.
279      *
280      * @param printDoc document that should be printed.
281      * @param usePrintColoringMap use printing coloring settings instead
282      * of the regular ones.
283      * @param lineNumberEnabled if set to false the line numbers will not be printed.
284      * If set to true the visibility of line numbers depends on lineNumberVisibleSetting.
285      */

286     public EditorUI(BaseDocument printDoc, boolean usePrintColoringMap, boolean lineNumberEnabled) {
287         this.printDoc = printDoc;
288         this.usePrintColoringMap = usePrintColoringMap;
289
290         settingsChange(null);
291
292         setLineNumberEnabled(lineNumberEnabled);
293
294         updateLineNumberWidth(0);
295
296         drawLayerList.add(printDoc.getDrawLayerList());
297         HighlightingDrawLayer.hookUp(this);
298     }
299     
300     /** Gets the coloring map that can be shared by the components
301       * with the same kit. Only the component coloring map is provided.
302       */

303     protected static Map JavaDoc getSharedColoringMap(Class JavaDoc kitClass) {
304         synchronized (Settings.class) { // must sync like this against dedloks
305
Map JavaDoc cm = (Map JavaDoc)sharedColoringMaps.get(kitClass);
306             if (cm == null) {
307                 cm = SettingsUtil.getColoringMap(kitClass, false, true);
308                 // Test if there's a default coloring
309
if (cm.get(SettingsNames.DEFAULT_COLORING) == null) {
310                     cm.put(SettingsNames.DEFAULT_COLORING, SettingsDefaults.defaultColoring);
311                 }
312
313                 sharedColoringMaps.put(kitClass, cm);
314             }
315
316             return cm;
317         }
318     }
319
320     /**
321      * A workaround method to initialize lineHeight variable
322      * so that the DrawEngineLineView can use it.
323      */

324     void initLineHeight(JTextComponent JavaDoc c) {
325         // Initialize lineHeight variable from the given component
326
updateLineHeight(c);
327     }
328
329     /** Called when the <tt>BaseTextUI</tt> is being installed
330     * into the component.
331     */

332     protected void installUI(JTextComponent JavaDoc c) {
333         synchronized (getComponentLock()) {
334             this.component = c;
335             
336             putProperty(COMPONENT_PROPERTY, c);
337
338             // listen on component
339
component.addPropertyChangeListener(this);
340             component.addFocusListener(focusL);
341
342             // listen on caret
343
Caret JavaDoc caret = component.getCaret();
344             if (caret != null) {
345                 caret.addChangeListener(this);
346             }
347
348             BaseDocument doc = getDocument();
349             if (doc != null) {
350                 modelChanged(null, doc);
351             }
352         }
353
354         // Make sure all the things depending on non-null component will be updated
355
settingsChange(null);
356         
357         // fix for issue #16352
358
getDefaultColoring().apply(component);
359         
360         // enable drag and drop feature
361
component.setDragEnabled(true);
362     }
363
364     /** Called when the <tt>BaseTextUI</tt> is being uninstalled
365     * from the component.
366     */

367     protected void uninstallUI(JTextComponent JavaDoc c) {
368         synchronized (getComponentLock()) {
369             
370             // fix for issue 12996
371
if (component != null) {
372                 
373             // stop listening on caret
374
Caret JavaDoc caret = component.getCaret();
375             if (caret != null) {
376                 caret.removeChangeListener(this);
377             }
378
379             // stop listening on component
380
component.removePropertyChangeListener(this);
381             component.removeFocusListener(focusL);
382             
383             }
384
385             BaseDocument doc = getDocument();
386             if (doc != null) {
387                 modelChanged(doc, null);
388             }
389
390             component = null;
391             putProperty(COMPONENT_PROPERTY, null);
392
393             // Clear the font-metrics cache
394
FontMetricsCache.clear();
395         }
396     }
397
398     /** Get the lock assuring the component will not be changed
399     * by <tt>installUI()</tt> or <tt>uninstallUI()</tt>.
400     * It's useful for the classes that want to listen for the
401     * component change in <tt>EditorUI</tt>.
402     */

403     public Object JavaDoc getComponentLock() {
404         if (componentLock == null) {
405             componentLock = new ComponentLock();
406         }
407         return componentLock;
408     }
409     static class ComponentLock {};
410
411     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
412         propertyChangeSupport.addPropertyChangeListener(l);
413     }
414
415     public void addPropertyChangeListener(String JavaDoc propertyName, PropertyChangeListener JavaDoc l) {
416         propertyChangeSupport.addPropertyChangeListener(propertyName, l);
417     }
418
419     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
420         propertyChangeSupport.removePropertyChangeListener(l);
421     }
422
423     public void removePropertyChangeListener(String JavaDoc propertyName, PropertyChangeListener JavaDoc l) {
424         propertyChangeSupport.removePropertyChangeListener(propertyName, l);
425     }
426
427     protected final void firePropertyChange(String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
428         propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
429     }
430
431     protected void settingsChangeImpl(String JavaDoc settingName){
432         Class JavaDoc kitClass = getKitClass();
433
434         coloringMap = null; // reset coloring map so it's rebuilt upon a coloring retrieval
435

436         if (settingName == null || SettingsNames.LINE_NUMBER_VISIBLE.equals(settingName)) {
437             lineNumberVisibleSetting = SettingsUtil.getBoolean(kitClass,
438                                            SettingsNames.LINE_NUMBER_VISIBLE,
439                                            SettingsDefaults.defaultLineNumberVisible);
440             lineNumberVisible = lineNumberEnabled && lineNumberVisibleSetting;
441             
442             // if this is printing, the drawing of original line numbers must be enabled
443
if (component == null)
444                 disableLineNumbers = false;
445             
446             if (disableLineNumbers)
447                 lineNumberVisible = false;
448         }
449
450         BaseDocument doc = getDocument();
451         if (doc != null) {
452
453             if (settingName == null || SettingsNames.TEXT_LEFT_MARGIN_WIDTH.equals(settingName)) {
454                 textLeftMarginWidth = SettingsUtil.getInteger(kitClass,
455                                       SettingsNames.TEXT_LEFT_MARGIN_WIDTH,
456                                       SettingsDefaults.defaultTextLeftMarginWidth);
457             }
458
459             if (settingName == null || SettingsNames.LINE_HEIGHT_CORRECTION.equals(settingName)) {
460                 Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.LINE_HEIGHT_CORRECTION);
461                 if (!(value instanceof Float JavaDoc) || ((Float JavaDoc)value).floatValue() < 0) {
462                     value = SettingsDefaults.defaultLineHeightCorrection;
463                 }
464
465                 float newLineHeightCorrection = ((Float JavaDoc)value).floatValue();
466                 if (newLineHeightCorrection != lineHeightCorrection){
467                     lineHeightCorrection = newLineHeightCorrection;
468                     updateLineHeight(getComponent());
469                 }
470             }
471
472             if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_VISIBLE.equals(settingName)) {
473                 textLimitLineVisible = SettingsUtil.getBoolean(kitClass,
474                                        SettingsNames.TEXT_LIMIT_LINE_VISIBLE, SettingsDefaults.defaultTextLimitLineVisible);
475             }
476
477             if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_COLOR.equals(settingName)) {
478                 Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.TEXT_LIMIT_LINE_COLOR);
479                 textLimitLineColor = (value instanceof Color) ? (Color)value
480                                      : SettingsDefaults.defaultTextLimitLineColor;
481             }
482
483             if (settingName == null || SettingsNames.TEXT_LIMIT_WIDTH.equals(settingName)) {
484                 textLimitWidth = SettingsUtil.getPositiveInteger(kitClass,
485                                  SettingsNames.TEXT_LIMIT_WIDTH, SettingsDefaults.defaultTextLimitWidth);
486             }
487
488             // component only properties
489
if (component != null) {
490                 if (settingName == null || SettingsNames.SCROLL_JUMP_INSETS.equals(settingName)) {
491                     Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.SCROLL_JUMP_INSETS);
492                     scrollJumpInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS;
493                 }
494
495                 if (settingName == null || SettingsNames.SCROLL_FIND_INSETS.equals(settingName)) {
496                     Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.SCROLL_FIND_INSETS);
497                     scrollFindInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS;
498                 }
499
500                 if (settingName == null || SettingsNames.COMPONENT_SIZE_INCREMENT.equals(settingName)) {
501                     Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.COMPONENT_SIZE_INCREMENT);
502                     componentSizeIncrement = (value instanceof Dimension) ? (Dimension)value : NULL_DIMENSION;
503                 }
504
505                 if (settingName == null || SettingsNames.RENDERING_HINTS.equals(settingName)) {
506                     
507                     //is this ever really not a Map?
508
Object JavaDoc userSetHints = Settings.getValue(kitClass, SettingsNames.RENDERING_HINTS);
509                     renderingHints = (userSetHints instanceof Map JavaDoc && ((Map JavaDoc)userSetHints).size() > 0) ? (Map JavaDoc)userSetHints : null;
510                 }
511                 
512                 if (settingName == null || SettingsNames.CARET_COLOR_INSERT_MODE.equals(settingName)
513                         || SettingsNames.CARET_COLOR_OVERWRITE_MODE.equals(settingName)
514                    ) {
515                     Boolean JavaDoc b = (Boolean JavaDoc)getProperty(OVERWRITE_MODE_PROPERTY);
516                     Color caretColor;
517                     if (b == null || !b.booleanValue()) {
518                         Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_INSERT_MODE);
519                         caretColor = (value instanceof Color) ? (Color)value
520                                      : SettingsDefaults.defaultCaretColorInsertMode;
521
522                     } else {
523                         Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_OVERWRITE_MODE);
524                         caretColor = (value instanceof Color) ? (Color)value
525                                      : SettingsDefaults.defaultCaretColorOvwerwriteMode;
526                     }
527
528                     if (caretColor != null) {
529                         component.setCaretColor(caretColor);
530                     }
531                 }
532
533
534                 Utilities.runInEventDispatchThread(
535                     new Runnable JavaDoc() {
536                         public void run() {
537                             JTextComponent JavaDoc c = component;
538                             if (c != null) {
539                                 BaseKit kit = Utilities.getKit(c);
540                                 if (kit != null) {
541                                     c.setKeymap(kit.getKeymap());
542                                     updateComponentProperties();
543
544                                     ((BaseTextUI)c.getUI()).preferenceChanged(true, true);
545                                 }
546                             }
547                         }
548                     }
549                 );
550             }
551         }
552     }
553     
554     public void settingsChange(SettingsChangeEvent evt) {
555         if (component != null) {
556             if (Utilities.getKit(component) == null) {
557                 return; // prevent problems if not garbage collected and settings changed
558
}
559         }
560         String JavaDoc settingName = (evt != null) ? evt.getSettingName() : null;
561         settingsChangeImpl(settingName);
562     }
563
564     public void stateChanged(ChangeEvent JavaDoc evt) {
565         SwingUtilities.invokeLater(
566             new Runnable JavaDoc() {
567                 
568                 /** @return true if the document supports guarded sections
569                  * and when either the caret is in guarded block
570                  * or when selection spans any guarded block(s).
571                  */

572                 private boolean isCaretGuarded(){
573                     JTextComponent JavaDoc c = component;
574                     BaseDocument bdoc = getDocument();
575                     boolean inGuardedBlock = false;
576                     if (bdoc instanceof GuardedDocument){
577                         GuardedDocument gdoc = (GuardedDocument)bdoc;
578
579                         boolean selectionSpansGuardedSection = false;
580                         for (int i=c.getSelectionStart(); i<c.getSelectionEnd(); i++){
581                             if (gdoc.isPosGuarded(i)){
582                                 selectionSpansGuardedSection = true;
583                                 break;
584                             }
585                         }
586                         
587                         inGuardedBlock = (gdoc.isPosGuarded(c.getCaretPosition()) ||
588                             selectionSpansGuardedSection);
589                     }
590                     return inGuardedBlock;
591                 }
592                 
593                 public void run() {
594                     JTextComponent JavaDoc c = component;
595                     if (c != null) {
596                         BaseKit kit = Utilities.getKit(c);
597                         if (kit != null) {
598                             boolean isEditable = c.isEditable();
599                             boolean selectionVisible = c.getCaret().isSelectionVisible();
600                             boolean caretGuarded = isCaretGuarded();
601
602                             Action JavaDoc a = kit.getActionByName(BaseKit.copyAction);
603                             if (a != null) {
604                                 a.setEnabled(selectionVisible);
605                             }
606
607                             a = kit.getActionByName(BaseKit.cutAction);
608                             if (a != null) {
609                                 a.setEnabled(selectionVisible && !caretGuarded && isEditable);
610                             }
611
612                             a = kit.getActionByName(BaseKit.removeSelectionAction);
613                             if (a != null) {
614                                 a.setEnabled(selectionVisible && !caretGuarded && isEditable);
615                             }
616                             
617                             a = kit.getActionByName(BaseKit.pasteAction);
618                             if (a != null) {
619                                 if (!isPasteActionInited) {
620                                     // #39678
621
a.setEnabled(!a.isEnabled());
622                                     isPasteActionInited = true;
623                                 }
624                                 a.setEnabled(!caretGuarded && isEditable);
625                             }
626                         }
627                     }
628                 }
629             }
630         );
631     }
632
633     protected void modelChanged(BaseDocument oldDoc, BaseDocument newDoc) {
634         if (oldDoc != null) {
635             // remove all document layers
636
drawLayerList.remove(oldDoc.getDrawLayerList());
637         }
638
639         if (newDoc != null) {
640             settingsChange(null);
641
642             // add all document layers
643
drawLayerList.add(newDoc.getDrawLayerList());
644         }
645     }
646
647     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
648         String JavaDoc propName = evt.getPropertyName();
649
650         if ("document".equals(propName)) { // NOI18N
651
BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument)
652                                   ? (BaseDocument)evt.getOldValue() : null;
653             BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument)
654                                   ? (BaseDocument)evt.getNewValue() : null;
655             modelChanged(oldDoc, newDoc);
656
657         } else if ("margin".equals(propName)) { // NOI18N
658
updateTextMargin();
659
660         } else if ("caret".equals(propName)) { // NOI18N
661
if (evt.getOldValue() instanceof Caret JavaDoc) {
662                 ((Caret JavaDoc)evt.getOldValue()).removeChangeListener(this);
663             }
664             if (evt.getNewValue() instanceof Caret JavaDoc) {
665                 ((Caret JavaDoc)evt.getNewValue()).addChangeListener(this);
666             }
667
668         } else if ("enabled".equals(propName)) { // NOI18N
669
if (!component.isEnabled()) {
670                 component.getCaret().setVisible(false);
671             }
672         }
673     }
674
675     protected Map JavaDoc createColoringMap() {
676         Map JavaDoc cm;
677
678         if (!usePrintColoringMap) {
679             // Use the shared coloring-map to save space and time
680
cm = getSharedColoringMap(getKitClass());
681
682         } else { // print coloring-map must be created
683
cm = SettingsUtil.getColoringMap(getKitClass(), true, true);
684             // Test if there's a default coloring
685
if (cm.get(SettingsNames.DEFAULT_COLORING) == null) {
686                 cm.put(SettingsNames.DEFAULT_COLORING, SettingsDefaults.defaultColoring);
687             }
688         }
689
690         return cm;
691     }
692
693     public int getLineHeight() {
694         return lineHeight;
695     }
696
697     public int getLineAscent() {
698         return lineAscent;
699     }
700
701     public Map JavaDoc getColoringMap() {
702         // Fix of #57372
703
synchronized (Settings.class) {
704             if (coloringMap == null) {
705                 coloringMap = createColoringMap();
706             }
707             return coloringMap;
708         }
709     }
710
711     public Coloring getDefaultColoring() {
712         return (Coloring)getColoringMap().get(SettingsNames.DEFAULT_COLORING);
713     }
714
715     public Coloring getColoring(String JavaDoc coloringName) {
716         return (Coloring)getColoringMap().get(coloringName);
717     }
718
719     private void updateLineHeight(JTextComponent JavaDoc component) {
720         if (component == null) {
721             return;
722         }
723         if (debugUpdateLineHeight) {
724             System.err.println("EditorUI.updateLineHeight(): Computing lineHeight ...");
725         }
726         Map JavaDoc cm = getColoringMap();
727         Iterator JavaDoc i = cm.entrySet().iterator();
728         int maxHeight = 1;
729         int maxAscent = 0;
730         while (i.hasNext()) {
731             Map.Entry JavaDoc me = (Map.Entry JavaDoc)i.next();
732             String JavaDoc coloringName = (String JavaDoc)me.getKey();
733             if (SettingsNames.STATUS_BAR_COLORING.equals(coloringName)
734                 || SettingsNames.STATUS_BAR_BOLD_COLORING.equals(coloringName)) {
735                 //#57112
736
continue;
737             }
738             Coloring c = (Coloring)me.getValue();
739             if (c != null) {
740                 Font font = c.getFont();
741                 if (font != null && (c.getFontMode() & Coloring.FONT_MODE_APPLY_SIZE) != 0) {
742                     FontMetrics fm = FontMetricsCache.getFontMetrics(font, component);
743                     if (fm != null) {
744                         if (debugUpdateLineHeight) {
745                             if (maxHeight < fm.getHeight()) {
746                                 System.err.println("Updating maxHeight from "
747                                     + maxHeight + " to " + fm.getHeight() // NOI18N
748
+ ", coloringName=" + coloringName // NOI18N
749
+ ", font=" + font // NOI18N
750
);
751                             }
752
753                             if (maxHeight < fm.getHeight()) {
754                                 System.err.println("Updating maxAscent from "
755                                     + maxAscent + " to " + fm.getAscent() // NOI18N
756
+ ", coloringName=" + coloringName // NOI18N
757
+ ", font=" + font // NOI18N
758
);
759                             }
760                         }
761
762                         maxHeight = Math.max(maxHeight, fm.getHeight());
763                         maxAscent = Math.max(maxAscent, fm.getAscent());
764                     }
765                 }
766             }
767         }
768
769         boolean changePreferences = false;
770         if (lineHeight!=1 && lineHeight!=(int)(maxHeight * lineHeightCorrection)){
771             changePreferences = true;
772         }
773
774         int oldProp = lineHeight;
775         
776         // Apply lineHeightCorrection
777
lineHeight = (int)(maxHeight * lineHeightCorrection);
778         lineAscent = (int)(maxAscent * lineHeightCorrection);
779         if (changePreferences) {
780             firePropertyChange(LINE_HEIGHT_CHANGED_PROP, new Integer JavaDoc(oldProp), new Integer JavaDoc(lineHeight));
781         }
782
783     }
784     
785     /**
786      * Update various properties of the component in AWT thread.
787      */

788     void updateComponentProperties() {
789         Class JavaDoc kitClass = Utilities.getKitClass(component);
790
791         // Set the margin
792
if (kitClass != null) {
793             Object JavaDoc value = Settings.getValue(kitClass, SettingsNames.MARGIN);
794             Insets margin = (value instanceof Insets) ? (Insets)value : null;
795             component.setMargin(margin);
796         }
797
798         // Apply the default coloring to the component
799
getDefaultColoring().apply(component);
800
801         lineNumberDigitWidth = computeLineNumberDigitWidth();
802
803         // Update line height
804
updateLineHeight(getComponent());
805
806         // Update space width of the default coloring's font
807
FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(getDefaultColoring().getFont());
808         defaultSpaceWidth = fmcInfo.getSpaceWidth(component);
809
810         updateLineNumberWidth(0);
811
812         // update glyph gutter colors and fonts
813
if (isGlyphGutterVisible()) {
814             glyphGutter.update();
815             updateScrollPaneCornerColor();
816         }
817     }
818     
819     protected void update(Graphics g) {
820         // Possibly apply the rendering hints
821
if (renderingHints != null) {
822             ((Graphics2D)g).setRenderingHints(renderingHints);
823         }
824     }
825
826     public final JTextComponent JavaDoc getComponent() {
827         return component;
828     }
829
830     /** Get the document to work on. Either component's document or printed document
831     * is returned. It can return null in case the component's document is not instance
832     * of BaseDocument.
833     */

834     public final BaseDocument getDocument() {
835         return (component != null) ? Utilities.getDocument(component) : printDoc;
836     }
837
838     private Class JavaDoc getKitClass() {
839         return (component != null) ? Utilities.getKitClass(component)
840                : ((printDoc != null) ? printDoc.getKitClass() : null);
841     }
842
843     public Object JavaDoc getProperty(Object JavaDoc key) {
844         return props.get(key);
845     }
846
847     public void putProperty(Object JavaDoc key, Object JavaDoc value) {
848         Object JavaDoc oldValue;
849         if (value != null) {
850             oldValue = props.put(key, value);
851         } else {
852             oldValue = props.remove(key);
853         }
854         firePropertyChange(key.toString(), oldValue, value);
855     }
856
857     /** Get extended editor component.
858      * The extended component should normally be used
859      * for editing files instead of just the JEditorPane
860      * because it offers status bar and possibly
861      * other useful components.
862      * <br>
863      * The component no longer includes toolbar - it's returned
864      * by a separate method {@link #getToolbarComponent()}.
865      * <br>
866      * The getExtComponent() should not be used when
867      * the JEditorPane is included in dialog.
868      * @see #hasExtComponent()
869      */

870     public JComponent JavaDoc getExtComponent() {
871         if (extComponent == null) {
872             if (component != null) {
873                 extComponent = createExtComponent();
874             }
875         }
876         return extComponent;
877     }
878
879     /**
880      * Get the toolbar component appropriate for this editor.
881      */

882     public JToolBar JavaDoc getToolBarComponent() {
883         if (toolBarComponent == null) {
884             if (component != null) {
885                 toolBarComponent = createToolBarComponent();
886             }
887         }
888         return toolBarComponent;
889     }
890     
891     /**
892      * Construct the toolbar component appropriate for this editor.
893      *
894      * @return non-null toolbar component or null if there is no appropriate
895      * toolbar component for this editor.
896      */

897     protected JToolBar JavaDoc createToolBarComponent() {
898        return null;
899     }
900
901     protected void initGlyphCorner(JScrollPane JavaDoc scroller){
902         glyphCorner = new JPanel JavaDoc();
903         updateScrollPaneCornerColor();
904         scroller.setCorner(JScrollPane.LOWER_LEFT_CORNER, glyphCorner);
905     }
906     
907     protected void setGlyphGutter(GlyphGutter gutter){
908         glyphGutter = gutter;
909     }
910     
911     public final int getSideBarWidth(){
912         JScrollPane JavaDoc scroll = (JScrollPane JavaDoc)SwingUtilities.getAncestorOfClass(JScrollPane JavaDoc.class, getParentViewport());
913         if (scroll!=null && scroll.getRowHeader()!=null){
914             Rectangle bounds = scroll.getRowHeader().getBounds();
915             if (bounds!=null){
916                 return bounds.width;
917             }
918         }
919         return 40;
920     }
921     
922     protected JComponent JavaDoc createExtComponent() {
923         setLineNumberEnabled(true); // enable line numbering
924

925         // extComponent will be a panel
926
JComponent JavaDoc ec = new JPanel JavaDoc(new BorderLayout());
927         ec.putClientProperty(JTextComponent JavaDoc.class, component);
928
929         // Add the scroll-pane with the component to the center
930
JScrollPane JavaDoc scroller = new JScrollPane JavaDoc(component);
931         scroller.getViewport().setMinimumSize(new Dimension(4,4));
932         
933         // remove default scroll-pane border, winsys will handle borders itself
934
scroller.setBorder(null);
935         
936         setGlyphGutter(new GlyphGutter(this));
937         scroller.setRowHeaderView(glyphGutter);
938
939         initGlyphCorner(scroller);
940         
941         ec.add(scroller);
942
943         // Install the status-bar panel to the bottom
944
ec.add(getStatusBar().getPanel(), BorderLayout.SOUTH);
945         
946         return ec;
947     }
948     
949     /** Whether this ui uses extComponent or not.
950      * @see #getExtComponent()
951      */

952     public boolean hasExtComponent() {
953         return (extComponent != null);
954     }
955
956     public Abbrev getAbbrev() {
957         if (abbrev == null) {
958             abbrev = new Abbrev(this, true, true);
959         }
960         return abbrev;
961     }
962
963     public WordMatch getWordMatch() {
964         if (wordMatch == null) {
965             wordMatch = new WordMatch(this);
966         }
967         return wordMatch;
968     }
969
970     public StatusBar getStatusBar() {
971         if (statusBar == null) {
972             statusBar = new StatusBar(this);
973         }
974         return statusBar;
975     }
976     
977
978     final DrawLayerList getDrawLayerList() {
979         return drawLayerList;
980     }
981
982     /**
983      * Find the layer with some layer name in the layer hierarchy.
984      *
985      * <p>Using of <code>DrawLayer</code>s has been deprecated.
986      *
987      * @deprecated Please use Highlighting SPI instead, for details see
988      * <a HREF="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a>.
989      */

990     public DrawLayer findLayer(String JavaDoc layerName) {
991         return drawLayerList.findLayer(layerName);
992     }
993
994     /**
995      * Add new layer and use its priority to position it in the chain.
996      * If there's the layer with same visibility then the inserted layer
997      * will be placed after it.
998      *
999      * <p>Using of <code>DrawLayer</code>s has been deprecated.
1000     *
1001     * @param layer layer to insert into the chain
1002     *
1003     * @deprecated Please use Highlighting SPI instead, for details see
1004     * <a HREF="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a>.
1005     */

1006    public boolean addLayer(DrawLayer layer, int visibility) {
1007        return drawLayerList.add(layer, visibility);
1008    }
1009
1010    /**
1011     * Using of <code>DrawLayer</code>s has been deprecated.
1012     *
1013     * @deprecated Please use Highlighting SPI instead, for details see
1014     * <a HREF="@org-netbeans-modules-editor-lib2@/overview-summary.html">Editor Library 2</a>.
1015     */

1016    public DrawLayer removeLayer(String JavaDoc layerName) {
1017        return drawLayerList.remove(layerName);
1018    }
1019
1020    public void repaint(int startY) {
1021        repaint(startY, component.getHeight());
1022    }
1023
1024    public void repaint(int startY, int height) {
1025        if (height <= 0) {
1026            return;
1027        }
1028        int width = Math.max(component.getWidth(), 0);
1029        startY = Math.max(startY, 0);
1030        component.repaint(0, startY, width, height);
1031    }
1032
1033    public void repaintOffset(int pos) throws BadLocationException JavaDoc {
1034        repaintBlock(pos, pos);
1035    }
1036
1037    /** Repaint the block between the given positions. */
1038    public void repaintBlock(int startPos, int endPos)
1039    throws BadLocationException JavaDoc {
1040        BaseTextUI ui = (BaseTextUI)component.getUI();
1041        if (startPos > endPos) { // swap
1042
int tmpPos = startPos;
1043            startPos = endPos;
1044            endPos = tmpPos;
1045        }
1046        
1047        int yFrom;
1048        int yTo;
1049
1050        try {
1051            yFrom = ui.getYFromPos(startPos);
1052        } catch (BadLocationException JavaDoc e) {
1053            Utilities.annotateLoggable(e);
1054            yFrom = 0;
1055        }
1056        
1057        try {
1058            yTo = ui.getYFromPos(endPos) + lineHeight;
1059        } catch (BadLocationException JavaDoc e) {
1060            Utilities.annotateLoggable(e);
1061            yTo = (int) ui.getRootView(component).getPreferredSpan(View.Y_AXIS);
1062        }
1063        
1064        repaint(yFrom, yTo - yFrom);
1065    }
1066
1067    /** Is the parent of some editor component a viewport */
1068    private JViewport JavaDoc getParentViewport() {
1069        Component JavaDoc pc = component.getParent();
1070        return (pc instanceof JViewport JavaDoc) ? (JViewport JavaDoc)pc : null;
1071    }
1072
1073    /** Finds the frame - parent of editor component */
1074    public static Frame getParentFrame(Component JavaDoc c) {
1075        do {
1076            c = c.getParent();
1077            if (c instanceof Frame) {
1078                return (Frame)c;
1079            }
1080        } while (c != null);
1081        return null;
1082    }
1083
1084    /** Possibly update virtual width. If the width
1085    * is really updated, the method returns true.
1086    * @deprecated virtual size is no longer used and effects of this method are ignored
1087    */

1088    public boolean updateVirtualWidth(int width) {
1089        return false;
1090    }
1091
1092    /** Possibly update virtual height. If the height
1093    * is really updated, the method returns true. There is
1094    * a slight difference against virtual width in that
1095    * if the height is shrinked too much the virtual height
1096    * is shrinked too.
1097    * 0 can be used to update to the real height.
1098    * @deprecated virtual size is no longer used and effects of this method are ignored
1099    */

1100    public boolean updateVirtualHeight(int height) {
1101        return false;
1102    }
1103
1104    public boolean isLineNumberEnabled() {
1105        return lineNumberEnabled;
1106    }
1107
1108    public void setLineNumberEnabled(boolean lineNumberEnabled) {
1109        this.lineNumberEnabled = lineNumberEnabled;
1110        lineNumberVisible = lineNumberEnabled && lineNumberVisibleSetting;
1111        if (disableLineNumbers)
1112            lineNumberVisible = false;
1113    }
1114
1115    void setLineNumberVisibleSetting(boolean lineNumberVisibleSetting) {
1116        this.lineNumberVisibleSetting = lineNumberVisibleSetting;
1117    }
1118    
1119    /** Update the width that will be occupied by the line number.
1120    * @param maxDigitCount maximum digit count that can the line number have.
1121    * if it's lower or equal to zero it will be computed automatically.
1122    */

1123    public void updateLineNumberWidth(int maxDigitCount) {
1124        int oldWidth = lineNumberWidth;
1125
1126        if (lineNumberVisible) {
1127            try {
1128                if (maxDigitCount <= 0) {
1129                    BaseDocument doc = getDocument();
1130                    int lineCnt = Utilities.getLineOffset(doc, doc.getLength()) + 1;
1131                    maxDigitCount = Integer.toString(lineCnt).length();
1132                }
1133
1134                if (maxDigitCount > lineNumberMaxDigitCount) {
1135                    lineNumberMaxDigitCount = maxDigitCount;
1136                }
1137
1138            } catch (BadLocationException JavaDoc e) {
1139                lineNumberMaxDigitCount = 1;
1140            }
1141            lineNumberWidth = lineNumberMaxDigitCount * lineNumberDigitWidth;
1142            Insets lineMargin = getLineNumberMargin();
1143            if (lineMargin != null) {
1144                lineNumberWidth += lineMargin.left + lineMargin.right;
1145            }
1146
1147        } else {
1148            lineNumberWidth = 0;
1149        }
1150
1151        updateTextMargin();
1152        if (oldWidth != lineNumberWidth) { // changed
1153
if (component != null) {
1154                component.repaint();
1155            }
1156        }
1157    }
1158
1159    public void updateTextMargin() {
1160        if (!SwingUtilities.isEventDispatchThread()) {
1161            SwingUtilities.invokeLater(
1162                new Runnable JavaDoc() {
1163                    public void run() {
1164                        updateTextMargin();
1165                    }
1166                }
1167            );
1168        }
1169
1170        Insets orig = textMargin;
1171        Insets cm = (component != null) ? component.getMargin() : null;
1172        int leftWidth = lineNumberWidth + textLeftMarginWidth;
1173        if (cm != null) {
1174            textMargin = new Insets(cm.top, cm.left + leftWidth,
1175                                    cm.bottom, cm.right);
1176        } else {
1177            textMargin = new Insets(0, leftWidth, 0, 0);
1178        }
1179        if (orig.top != textMargin.top || orig.bottom != textMargin.bottom) {
1180            ((BaseTextUI)component.getUI()).invalidateStartY();
1181        }
1182    }
1183
1184    public Rectangle getExtentBounds() {
1185        return getExtentBounds(null);
1186    }
1187
1188    /** Get position of the component extent. The (x, y) are set to (0, 0) if there's
1189    * no viewport or (-x, -y) if there's one.
1190    */

1191    public Rectangle getExtentBounds(Rectangle r) {
1192        if (r == null) {
1193            r = new Rectangle();
1194        }
1195        if (component != null) {
1196            JViewport JavaDoc port = getParentViewport();
1197            if (port != null) {
1198                Point p = port.getViewPosition();
1199                r.width = port.getWidth();
1200                r.height = port.getHeight();
1201                r.x = p.x;
1202                r.y = p.y;
1203            } else { // no viewport
1204
r.setBounds(component.getVisibleRect());
1205            }
1206        }
1207        return r;
1208    }
1209
1210    /** Get the begining of the area covered by text */
1211    public Insets getTextMargin() {
1212        return textMargin;
1213    }
1214
1215    /**
1216     * Scroll the editor window so that the given rectangle is visible.
1217     *
1218     * @param r rectangle to which the editor window should be scrolled.
1219     * @param scrollPolicy the way how scrolling should be done.
1220     * One of <code>EditorUI.SCROLL_*</code> constants.
1221     *
1222     * @deprecated use <code>JComponent.scrollRectToVisible()</code> instead of this method.
1223     */

1224    public void scrollRectToVisible(final Rectangle r, final int scrollPolicy) {
1225        Utilities.runInEventDispatchThread(
1226            new Runnable JavaDoc() {
1227                public void run() {
1228                    scrollRectToVisibleFragile(r, scrollPolicy);
1229                }
1230            }
1231        );
1232    }
1233
1234    /** Must be called with EventDispatchThread */
1235    boolean scrollRectToVisibleFragile(Rectangle r, int scrollPolicy) {
1236        Insets margin = getTextMargin();
1237        Rectangle bounds = getExtentBounds();
1238        r = new Rectangle(r); // make copy of orig rect
1239
r.x -= margin.left;
1240        r.y -= margin.top;
1241        bounds.width -= margin.left + margin.right;
1242        bounds.height -= margin.top + margin.bottom;
1243        return scrollRectToVisibleImpl(r, scrollPolicy, bounds);
1244    }
1245
1246    /** Scroll the view so that requested rectangle is best visible.
1247     * There are different scroll policies available.
1248     * @return whether the extent has to be scrolled in any direction.
1249     */

1250    private boolean scrollRectToVisibleImpl(Rectangle r, int scrollPolicy,
1251                                            Rectangle bounds) {
1252        if (bounds.width <= 0 || bounds.height <= 0) {
1253            return false;
1254        }
1255
1256        // handle find scrolling specifically
1257
if (scrollPolicy == SCROLL_FIND) {
1258            // converted inset
1259
int cnvFI = (scrollFindInsets.left < 0)
1260                ? (- bounds.width * scrollFindInsets.left / 100)
1261                : scrollFindInsets.left * defaultSpaceWidth;
1262
1263            int nx = Math.max(r.x - cnvFI, 0);
1264            
1265            cnvFI = (scrollFindInsets.right < 0)
1266                ? (- bounds.width * scrollFindInsets.right / 100)
1267                : scrollFindInsets.right * defaultSpaceWidth;
1268
1269            r.width += (r.x - nx) + cnvFI;
1270            r.x = nx;
1271
1272            cnvFI = (scrollFindInsets.top < 0)
1273                ? (- bounds.height * scrollFindInsets.top / 100)
1274                : scrollFindInsets.top * lineHeight;
1275
1276            int ny = Math.max(r.y - cnvFI, 0);
1277
1278            cnvFI = (scrollFindInsets.bottom < 0)
1279                ? (- bounds.height * scrollFindInsets.bottom / 100)
1280                : scrollFindInsets.bottom * lineHeight;
1281
1282            r.height += (r.y - ny) + cnvFI;
1283            r.y = ny;
1284
1285            return scrollRectToVisibleImpl(r, SCROLL_SMALLEST, bounds); // recall
1286
}
1287        
1288        int viewWidth = (int)((TextUI JavaDoc)component.getUI()).getRootView(component).getPreferredSpan(View.X_AXIS);
1289        int viewHeight = (int)((TextUI JavaDoc)component.getUI()).getRootView(component).getPreferredSpan(View.Y_AXIS);
1290        
1291        // r must be within virtualSize's width
1292
if (r.x + r.width > viewWidth) {
1293            r.x = viewWidth - r.width;
1294            if (r.x < 0) {
1295                r.x = 0;
1296                r.width = viewWidth;
1297            }
1298            return scrollRectToVisibleImpl(r, scrollPolicy, bounds); // recall
1299
}
1300        // r must be within virtualSize's height
1301
if (r.y +r.height > viewHeight) {
1302            r.y = viewHeight - r.height;
1303            if (r.y < 0) {
1304                r.y = 0;
1305                r.height = viewHeight;
1306            }
1307            return scrollRectToVisibleImpl(r, scrollPolicy, bounds);
1308        }
1309
1310        // if r extends bounds dimension it must be corrected now
1311
if (r.width > bounds.width || r.height > bounds.height) {
1312            try {
1313                Rectangle caretRect = component.getUI().modelToView(
1314                                            component, component.getCaret().getDot(), Position.Bias.Forward);
1315                if (caretRect.x >= r.x
1316                        && caretRect.x + caretRect.width <= r.x + r.width
1317                        && caretRect.y >= r.y
1318                        && caretRect.y + caretRect.height <= r.y + r.height
1319                   ) { // caret inside requested rect
1320
// move scroll rect for best caret visibility
1321
int overX = r.width - bounds.width;
1322                    int overY = r.height - bounds.height;
1323                    if (overX > 0) {
1324                        r.x -= overX * (caretRect.x - r.x) / r.width;
1325                    }
1326                    if (overY > 0) {
1327                        r.y -= overY * (caretRect.y - r.y) / r.height;
1328                    }
1329                }
1330                r.height = bounds.height;
1331                r.width = bounds.width; // could be different algorithm
1332
return scrollRectToVisibleImpl(r, scrollPolicy, bounds);
1333            } catch (BadLocationException JavaDoc ble){
1334                ble.printStackTrace();
1335            }
1336        }
1337
1338        int newX = bounds.x;
1339        int newY = bounds.y;
1340        boolean move = false;
1341        // now the scroll rect is within bounds of the component
1342
// and can have size of the extent at maximum
1343
if (r.x < bounds.x) {
1344            move = true;
1345            switch (scrollPolicy) {
1346            case SCROLL_MOVE:
1347                newX = (scrollJumpInsets.left < 0)
1348                       ? (bounds.width * (-scrollJumpInsets.left) / 100)
1349                       : scrollJumpInsets.left * defaultSpaceWidth;
1350                newX = Math.min(newX, bounds.x + bounds.width - (r.x + r.width));
1351                newX = Math.max(r.x - newX, 0); // new bounds.x
1352
break;
1353            case SCROLL_DEFAULT:
1354            case SCROLL_SMALLEST:
1355            default:
1356                newX = r.x;
1357                break;
1358            }
1359            updateVirtualWidth(newX + bounds.width);
1360        } else if (r.x + r.width > bounds.x + bounds.width) {
1361            move = true;
1362            switch (scrollPolicy) {
1363            case SCROLL_SMALLEST:
1364                newX = r.x + r.width - bounds.width;
1365                break;
1366            default:
1367                newX = (scrollJumpInsets.right < 0)
1368                       ? (bounds.width * (-scrollJumpInsets.right) / 100 )
1369                       : scrollJumpInsets.right * defaultSpaceWidth;
1370                newX = Math.min(newX, bounds.width - r.width);
1371                newX = (r.x + r.width) + newX - bounds.width;
1372                break;
1373            }
1374
1375            updateVirtualWidth(newX + bounds.width);
1376        }
1377
1378        if (r.y < bounds.y) {
1379            move = true;
1380            switch (scrollPolicy) {
1381            case SCROLL_MOVE:
1382                newY = r.y;
1383                newY -= (scrollJumpInsets.top < 0)
1384                        ? (bounds.height * (-scrollJumpInsets.top) / 100 )
1385                        : scrollJumpInsets.top * lineHeight;
1386                break;
1387            case SCROLL_SMALLEST:
1388                newY = r.y;
1389                break;
1390            case SCROLL_DEFAULT:
1391            default:
1392                newY = r.y - (bounds.height - r.height) / 2; // center
1393
break;
1394            }
1395            newY = Math.max(newY, 0);
1396        } else if (r.y + r.height > bounds.y + bounds.height) {
1397            move = true;
1398            switch (scrollPolicy) {
1399            case SCROLL_MOVE:
1400                newY = (r.y + r.height) - bounds.height;
1401                newY += (scrollJumpInsets.bottom < 0)
1402                        ? (bounds.height * (-scrollJumpInsets.bottom) / 100 )
1403                        : scrollJumpInsets.bottom * lineHeight;
1404                break;
1405            case SCROLL_SMALLEST:
1406                newY = (r.y + r.height) - bounds.height;
1407                break;
1408            case SCROLL_DEFAULT:
1409            default:
1410                newY = r.y - (bounds.height - r.height) / 2; // center
1411
break;
1412            }
1413            newY = Math.max(newY, 0);
1414        }
1415
1416        if (move) {
1417            setExtentPosition(newX, newY);
1418        }
1419        return move;
1420    }
1421
1422    void setExtentPosition(int x, int y) {
1423        JViewport JavaDoc port = getParentViewport();
1424        if (port != null) {
1425            Point p = new Point(Math.max(x, 0), Math.max(y, 0));
1426            port.setViewPosition(p);
1427        }
1428    }
1429
1430    public void adjustWindow(int caretPercentFromWindowTop) {
1431        final Rectangle bounds = getExtentBounds();
1432        if (component != null && (component.getCaret() instanceof Rectangle)) {
1433            Rectangle caretRect = (Rectangle)component.getCaret();
1434            bounds.y = caretRect.y - (caretPercentFromWindowTop * bounds.height) / 100
1435                       + (caretPercentFromWindowTop * lineHeight) / 100;
1436            Utilities.runInEventDispatchThread(
1437                new Runnable JavaDoc() {
1438                    public void run() {
1439                        scrollRectToVisible(bounds, SCROLL_SMALLEST);
1440                    }
1441                }
1442            );
1443        }
1444    }
1445
1446    /** Set the dot according to the currently visible screen window.
1447    * #param percentFromWindowTop percentage giving the distance of the caret
1448    * from the top of the currently visible window.
1449    */

1450    public void adjustCaret(int percentFromWindowTop) {
1451        JTextComponent JavaDoc c = component;
1452        if (c != null) {
1453            Rectangle bounds = getExtentBounds();
1454            bounds.y += (percentFromWindowTop * bounds.height) / 100
1455                        - (percentFromWindowTop * lineHeight) / 100;
1456            try {
1457                int offset = ((BaseTextUI)c.getUI()).getPosFromY(bounds.y);
1458                if (offset >= 0) {
1459                    caretSetDot(offset, null, SCROLL_SMALLEST);
1460                }
1461            } catch (BadLocationException JavaDoc e) {
1462            }
1463        }
1464    }
1465
1466    /** Set the position of the caret and scroll the extent if necessary.
1467     * @param offset position where the caret should be placed
1468     * @param scrollRect rectangle that should become visible. It can be null
1469     * when no scrolling should be done.
1470     * @param scrollPolicy policy to be used when scrolling.
1471     * @deprecated
1472     */

1473    public void caretSetDot(int offset, Rectangle scrollRect, int scrollPolicy) {
1474        if (component != null) {
1475            Caret JavaDoc caret = component.getCaret();
1476            if (caret instanceof BaseCaret) {
1477                ((BaseCaret)caret).setDot(offset, scrollRect, scrollPolicy);
1478            } else {
1479                caret.setDot(offset);
1480            }
1481        }
1482    }
1483
1484    /** Set the position of the caret and scroll the extent if necessary.
1485     * @param offset position where the caret should be placed
1486     * @param scrollRect rectangle that should become visible. It can be null
1487     * when no scrolling should be done.
1488     * @param scrollPolicy policy to be used when scrolling.
1489     * @deprecated
1490     */

1491    public void caretMoveDot(int offset, Rectangle scrollRect, int scrollPolicy) {
1492        if (component != null) {
1493            Caret JavaDoc caret = component.getCaret();
1494            if (caret instanceof BaseCaret) {
1495                ((BaseCaret)caret).moveDot(offset, scrollRect, scrollPolicy);
1496            } else {
1497                caret.moveDot(offset);
1498            }
1499        }
1500    }
1501
1502    /** This method is called by textui to do the paint.
1503    * It is forwarded either to paint through the image
1504    * and then copy the image area to the screen or to
1505    * paint directly to this graphics. The real work occurs
1506    * in draw-engine.
1507    */

1508    protected void paint(Graphics g) {
1509        if (component != null) { // component must be installed
1510
update(g);
1511        }
1512    }
1513
1514    /** Returns the line number margin */
1515    public Insets getLineNumberMargin() {
1516        return defaultLineNumberMargin;
1517    }
1518
1519    private int computeLineNumberDigitWidth(){
1520        // Handle line number fonts and widths
1521
Coloring dc = getDefaultColoring();
1522        Coloring lnc = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING);
1523        if (lnc != null) {
1524            Font lnFont = lnc.getFont();
1525            if (lnFont == null) {
1526                lnFont = dc.getFont();
1527            }
1528            if (component == null) return lineNumberDigitWidth;
1529            FontMetrics lnFM = FontMetricsCache.getFontMetrics(lnFont, component);
1530            if (lnFM == null) return lineNumberDigitWidth;
1531            int maxWidth = 1;
1532            char[] digit = new char[1]; // will be used for '0' - '9'
1533
for (int i = 0; i <= 9; i++) {
1534                digit[0] = (char)('0' + i);
1535                maxWidth = Math.max(maxWidth, lnFM.charsWidth(digit, 0, 1));
1536            }
1537            return maxWidth;
1538        }
1539        return lineNumberDigitWidth;
1540    }
1541    
1542    /** Returns width of the one digit */
1543    public int getLineNumberDigitWidth() {
1544        return lineNumberDigitWidth;
1545    }
1546
1547    /** Is glyph gutter created and visible for the document or not */
1548    public boolean isGlyphGutterVisible() {
1549        return glyphGutter != null;
1550    }
1551    
1552    public final GlyphGutter getGlyphGutter() {
1553        return glyphGutter;
1554    }
1555    
1556    protected void updateScrollPaneCornerColor() {
1557        Coloring lineColoring = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING);
1558        Coloring defaultColoring = (Coloring)getDefaultColoring();
1559        
1560        Color backgroundColor;
1561        if (lineColoring.getBackColor() != null)
1562            backgroundColor = lineColoring.getBackColor();
1563        else
1564            backgroundColor = defaultColoring.getBackColor();
1565        
1566        if (glyphCorner != null){
1567            glyphCorner.setBackground(backgroundColor);
1568        }
1569    }
1570}
1571
Popular Tags