KickJava   Java API By Example, From Geeks To Geeks.

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


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.ActionEvent JavaDoc;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.beans.PropertyChangeEvent JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import javax.swing.text.*;
29 import javax.swing.event.DocumentListener JavaDoc;
30 import javax.swing.event.DocumentEvent JavaDoc;
31 import javax.swing.plaf.TextUI JavaDoc;
32 import javax.swing.JComponent JavaDoc;
33 import javax.swing.JEditorPane JavaDoc;
34 import javax.swing.SwingUtilities JavaDoc;
35 import javax.swing.Action JavaDoc;
36 import javax.swing.UIManager JavaDoc;
37 import javax.swing.plaf.basic.BasicTextUI JavaDoc;
38 import org.netbeans.editor.view.spi.LockView;
39
40 /**
41 * Text UI implementation
42 *
43 * @author Miloslav Metelka, Martin Roskanin
44 * @version 1.00
45 */

46
47 public class BaseTextUI extends BasicTextUI JavaDoc
48     implements PropertyChangeListener JavaDoc, DocumentListener JavaDoc, SettingsChangeListener {
49
50     /** Extended UI */
51     private EditorUI editorUI;
52
53     private boolean foldingEnabled;
54     
55     private boolean needsRefresh = false;
56     
57     /** ID of the component in registry */
58     int componentID = -1;
59     
60     private AbstractDocument lastDocument;
61     
62     /** Instance of the <tt>GetFocusedComponentAction</tt> */
63     private static final GetFocusedComponentAction gfcAction
64     = new GetFocusedComponentAction();
65
66     public BaseTextUI() {
67     }
68     
69     protected String JavaDoc getPropertyPrefix() {
70         return "EditorPane"; //NOI18N
71
}
72
73     public static JTextComponent getFocusedComponent() {
74         return gfcAction.getFocusedComponent2();
75     }
76
77     protected boolean isRootViewReplaceNecessary() {
78         boolean replaceNecessary = false;
79         
80         Document doc = getComponent().getDocument();
81         if (doc != lastDocument) {
82             replaceNecessary = true;
83         }
84         
85         return replaceNecessary;
86     }
87
88     protected void rootViewReplaceNotify() {
89         // update the newly used document
90
lastDocument = (AbstractDocument)getComponent().getDocument();
91     }
92
93     /** Called when the model of component is changed */
94     protected void modelChanged() {
95         JTextComponent component = getComponent();
96         // [TODO] assert (component != null);
97
Document doc = component.getDocument();
98         
99         if (doc != null && !(doc instanceof AbstractDocument)) {
100             // This UI works with AbstractDocument document instances only
101
return; // return silently
102
}
103         AbstractDocument adoc = (AbstractDocument)doc;
104         
105         // Possibly rebuild fold hierarchy prior to rebuilding views.
106
// Views have optimization in fold hierarchy rebuild listening
107
// so that the actual views rebuild is only done once.
108
// Readlock on both last and current docs.
109
/* if (doc != lastDocument) {
110             if (lastDocument != null) {
111                 lastDocument.readLock();
112             }
113             try {
114                 if (adoc != null) {
115                     adoc.readLock();
116                 }
117                 try {
118                     FoldHierarchySpi.get(component).rebuild();
119                 } finally {
120                     if (adoc != null) {
121                         adoc.readUnlock();
122                     }
123                 }
124             } finally {
125                 if (lastDocument != null) {
126                     lastDocument.readUnlock();
127                 }
128             }
129         }
130  */

131
132         if (doc != null) {
133             ViewFactory f = getRootView(component).getViewFactory();
134             BaseKit kit = (BaseKit)getEditorKit(component);
135
136             component.removeAll();
137
138             if (isRootViewReplaceNecessary()) {
139                 rootViewReplaceNotify();
140                 Element elem = doc.getDefaultRootElement();
141                 View v = f.create(elem);
142                 setView(v);
143             }
144             
145             component.revalidate();
146
147             // Execute actions related to document installaction into the component
148
Settings.KitAndValue[] kv = Settings.getValueHierarchy(kit.getClass(),
149                                         SettingsNames.DOC_INSTALL_ACTION_NAME_LIST);
150             for (int i = kv.length - 1; i >= 0; i--) {
151                 List JavaDoc actList = (List JavaDoc)kv[i].value;
152                 actList = kit.translateActionNameList(actList); // translate names to actions
153
if (actList != null) {
154                     for (Iterator JavaDoc iter = actList.iterator(); iter.hasNext();) {
155                         Action JavaDoc a = (Action JavaDoc)iter.next();
156                         a.actionPerformed(new ActionEvent JavaDoc(component,
157                                                           ActionEvent.ACTION_PERFORMED, "")); // NOI18N
158
}
159                 }
160             }
161         }
162     }
163
164     
165     /* XXX - workaround bugfix of issue #45487 and #45678
166      * The hack can be removed if JDK bug
167      * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5067948
168      * will be fixed.
169      */

170     protected void installKeyboardActions() {
171         String JavaDoc mapName = getPropertyPrefix() + ".actionMap"; //NOI18N
172
// XXX - workaround bugfix of issue #45487
173
// Because the ActionMap is cached in method BasicTextUI.getActionMap()
174
// the property 'mapName' is set to null to force new actionMap creation
175
UIManager.getLookAndFeelDefaults().put(mapName, null);
176         UIManager.getDefaults().put(mapName, null); //#45678
177
super.installKeyboardActions();
178     }
179
180     /** Installs the UI for a component. */
181     public void installUI(JComponent JavaDoc c) {
182         super.installUI(c);
183         
184         if (!(c instanceof JTextComponent)) {
185             return;
186         }
187         
188         JTextComponent component = getComponent();
189
190         // set margin
191
Object JavaDoc value = Settings.getValue(Utilities.getKitClass(component), SettingsNames.MARGIN);
192         Insets margin = (value instanceof Insets) ? (Insets)value : null;
193         component.setMargin(margin);
194
195         getEditorUI().installUI(component);
196         Boolean JavaDoc foldingEnabledBoolean = (Boolean JavaDoc)Settings.getValue(Utilities.getKitClass(component), SettingsNames.CODE_FOLDING_ENABLE);
197         foldingEnabled = foldingEnabledBoolean.booleanValue();
198         component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
199         
200         Settings.addSettingsChangeListener(this);
201         
202         // attach to the model and component
203
//component.addPropertyChangeListener(this); already done in super class
204
if (component.getClientProperty(UIWatcher.class) == null) {
205             UIWatcher uiWatcher = new UIWatcher(this.getClass());
206             component.addPropertyChangeListener(uiWatcher);
207             component.putClientProperty(UIWatcher.class, uiWatcher);
208         }
209         
210         BaseKit kit = (BaseKit)getEditorKit(component);
211         ViewFactory vf = kit.getViewFactory();
212         // Create and attach caret
213
Caret caret = kit.createCaret();
214         component.setCaretColor(Color.black); // will be changed by settings later
215
component.setCaret(caret);
216         
217         // assign blink rate
218
int br = SettingsUtil.getInteger(Utilities.getKitClass(component), SettingsNames.CARET_BLINK_RATE,
219         SettingsDefaults.defaultCaretBlinkRate.intValue());
220         caret.setBlinkRate(br);
221
222         // Create document
223
/* BaseDocument doc = Utilities.getDocument(component);
224         if (doc != null) {
225             modelChanged(null, doc);
226         }
227  */

228         
229         SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
230         
231         Registry.addComponent(component);
232         Registry.activate(component);
233         component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
234     }
235
236     /** Deinstalls the UI for a component */
237     public void uninstallUI(JComponent JavaDoc c) {
238         super.uninstallUI(c);
239
240         Settings.removeSettingsChangeListener(this);
241         //c.removePropertyChangeListener(this);
242

243         if (c instanceof JTextComponent){
244             JTextComponent comp = (JTextComponent)c;
245             BaseDocument doc = Utilities.getDocument(comp);
246             if (doc != null) {
247                 doc.removeDocumentListener(this);
248             }
249
250             comp.setKeymap(null);
251             comp.setCaret(null);
252
253             getEditorUI().uninstallUI(comp);
254             Registry.removeComponent(comp);
255         }
256         
257         // Clear the editorUI so it will be recreated according to the kit
258
// of the component for which the installUI() is called
259
editorUI = null;
260     }
261     
262     public int getYFromPos(int pos) throws BadLocationException {
263         Rectangle ret = modelToView(getComponent(), pos);
264         return (ret == null) ? 0 : ret.y;
265 /* BaseDocument doc = Utilities.getDocument(getComponent());
266         if (doc!=null){
267             int ret = (Utilities.getLineOffset(doc, pos)) * getEditorUI().getLineHeight();
268             return ret;
269         }
270         return 0;
271  */

272     }
273
274     public int getPosFromY(int y) throws BadLocationException {
275         return viewToModel(getComponent(), 0, y);
276     }
277
278     public int getBaseX(int y) {
279         return getEditorUI().getTextMargin().left;
280     }
281
282     public int viewToModel(JTextComponent c, int x, int y) {
283         return viewToModel(c, new Point(x, y));
284     }
285
286
287     /** Next visually represented model location where caret can be placed.
288     * This version works without placing read lock on the document.
289     */

290     public int getNextVisualPositionFrom(JTextComponent t, int pos,
291                                          Position.Bias b, int direction, Position.Bias[] biasRet)
292     throws BadLocationException{
293         if (biasRet == null) {
294             biasRet = new Position.Bias[1];
295             biasRet[0] = Position.Bias.Forward;
296         }
297         return super.getNextVisualPositionFrom(t, pos, b, direction, biasRet);
298     }
299
300
301
302     /** Fetches the EditorKit for the UI.
303     *
304     * @return the component capabilities
305     */

306     public EditorKit getEditorKit(JTextComponent c) {
307         JEditorPane JavaDoc pane = (JEditorPane JavaDoc)getComponent();
308         return (pane==null) ? null : pane.getEditorKit();
309     }
310
311
312     /** Get extended UI. This is called from views to get correct extended UI. */
313     public EditorUI getEditorUI() {
314         if (editorUI == null) {
315             JTextComponent c = getComponent();
316             BaseKit kit = (BaseKit)getEditorKit(c);
317             editorUI = kit.createEditorUI();
318             editorUI.initLineHeight(c);
319         }
320         return editorUI;
321     }
322
323     /**
324     * This method gets called when a bound property is changed.
325     * We are looking for document changes on the component.
326     */

327     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
328         String JavaDoc propName = evt.getPropertyName();
329         if ("document".equals(propName)) { // NOI18N
330
BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument)
331                                   ? (BaseDocument)evt.getOldValue() : null;
332                                   
333             if (oldDoc != null) {
334                 oldDoc.removeDocumentListener(this);
335             }
336
337             BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument)
338                                   ? (BaseDocument)evt.getNewValue() : null;
339                                   
340             if (newDoc != null) {
341                 newDoc.addDocumentListener(this);
342                 Registry.activate(newDoc); // Activate the new document
343
}
344         } else if ("ancestor".equals(propName)) { // NOI18N
345
JTextComponent comp = (JTextComponent)evt.getSource();
346             if (comp.isDisplayable() && editorUI != null && editorUI.hasExtComponent()) {
347                 // #41209: In case extComponent was retrieved set the ancestorOverride
348
// to true and expect that the editor kit that installed
349
// this UI will be deinstalled explicitly.
350
if (!Boolean.TRUE.equals(comp.getClientProperty("ancestorOverride"))) { // NOI18N
351
comp.putClientProperty("ancestorOverride", Boolean.TRUE); // NOI18N
352
}
353             }
354         }
355     }
356
357     /** Insert to document notification. */
358     public void insertUpdate(DocumentEvent JavaDoc evt) {
359         try {
360             BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
361             EditorUI editorUI = getEditorUI();
362             int y = getYFromPos(evt.getOffset());
363             int lineHeight = editorUI.getLineHeight();
364             int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
365             // !!! patch for case when DocMarksOp.eolMark is at the end of document
366
if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
367                 syntaxY += lineHeight;
368             }
369             if (getComponent().isShowing()) {
370                 editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
371             }
372         } catch (BadLocationException ex) {
373             Utilities.annotateLoggable(ex);
374         }
375     }
376     
377     /** Remove from document notification. */
378     public void removeUpdate(DocumentEvent JavaDoc evt) {
379         try {
380             BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
381             EditorUI editorUI = getEditorUI();
382             int y = getYFromPos(evt.getOffset());
383             int lineHeight = editorUI.getLineHeight();
384             int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
385             // !!! patch for case when DocMarksOp.eolMark is at the end of document
386
if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
387                 syntaxY += lineHeight;
388             }
389             if (getComponent().isShowing()) {
390                 editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
391             }
392
393         } catch (BadLocationException ex) {
394             Utilities.annotateLoggable(ex);
395         }
396     }
397
398     /** The change in document notification.
399     *
400     * @param evt The change notification from the currently associated document.
401     */

402     public void changedUpdate(DocumentEvent JavaDoc evt) {
403         if (evt instanceof BaseDocumentEvent) {
404             BaseDocumentEvent bdevt = (BaseDocumentEvent)evt;
405             BaseDocument doc = (BaseDocument)bdevt.getDocument();
406             String JavaDoc layerName = bdevt.getDrawLayerName();
407             if (layerName != null) {
408                 getEditorUI().addLayer(doc.findLayer(layerName),
409                         bdevt.getDrawLayerVisibility());
410             }else{ //temp
411
try {
412                     JTextComponent comp = getComponent();
413                     if (comp!=null && comp.isShowing()) {
414                         getEditorUI().repaintBlock(evt.getOffset(), evt.getOffset() + evt.getLength());
415                     }
416                 } catch (BadLocationException ex) {
417                     Utilities.annotateLoggable(ex);
418                 }
419             }
420         }
421     }
422
423     
424     
425     /** Creates a view for an element.
426     *
427     * @param elem the element
428     * @return the newly created view or null
429     */

430     public View create(Element elem) {
431         String JavaDoc kind = elem.getName();
432             /*
433             if (foldingEnabled){
434                 Element parent = elem.getParentElement();
435                 if (parent!=null){
436                     int index = parent.getElementIndex(elem.getStartOffset());
437                     if (index >=3 && index <=6){
438                         return new CollapsedView(parent.getElement(3), parent.getElement(6));
439                     }
440                 }
441             }
442             */

443             
444         if (kind != null) {
445         if (kind.equals(AbstractDocument.ContentElementName)) {
446                     return new LabelView(elem);
447         } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
448 // System.out.println("creating DrawEngineLineView for elem=" + elem);
449
return new DrawEngineLineView(elem);//.createFragment(elem.getStartOffset()+10,elem.getStartOffset()+30);
450
} else if (kind.equals(AbstractDocument.SectionElementName)) {
451 // return new LockView(new EditorUIBoxView(elem, View.Y_AXIS));
452
// System.out.println("creating DrawEngineDocView for elem=" + elem);
453
// return new DrawEngineDocView(getComponent()); // EditorUIBoxView(elem, View.Y_AXIS);
454
return new LockView(new DrawEngineDocView(elem)); // EditorUIBoxView(elem, View.Y_AXIS);
455
} else if (kind.equals(StyleConstants.ComponentElementName)) {
456             return new ComponentView(elem);
457         } else if (kind.equals(StyleConstants.IconElementName)) {
458             return new IconView(elem);
459         }
460         }
461     
462         // default to text display
463
return new DrawEngineLineView(elem);
464     }
465
466     /** Creates a view for an element.
467     * @param elem the element
468     * @param p0 the starting offset >= 0
469     * @param p1 the ending offset >= p0
470     * @return the view
471     */

472     public View create(Element elem, int p0, int p1) {
473         return null;
474     }
475
476     /** Specifies that some preference has changed. */
477     public void preferenceChanged(boolean width, boolean height) {
478         modelChanged();
479     }
480
481     public void invalidateStartY() {
482         // no longer available
483
}
484
485     public void settingsChange(SettingsChangeEvent evt) {
486         JTextComponent component = getComponent();
487         if (component == null) return;
488         
489         if (evt == null || Utilities.getKitClass(component) != evt.getKitClass()) return;
490         
491         if (SettingsNames.CODE_FOLDING_ENABLE.equals(evt.getSettingName())){
492             Boolean JavaDoc foldingEnabledBoolean =(Boolean JavaDoc)Settings.getValue(evt.getKitClass(), SettingsNames.CODE_FOLDING_ENABLE);
493             foldingEnabled = foldingEnabledBoolean.booleanValue();
494             component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
495             needsRefresh = true;
496             Utilities.runInEventDispatchThread(new Runnable JavaDoc() {
497                 public void run() {
498                     refresh();
499                 }
500             });
501         }
502     }
503     
504     boolean isFoldingEnabled() {
505         return foldingEnabled;
506     }
507
508     protected void refresh(){
509         if (getComponent().isShowing() && needsRefresh){
510             modelChanged();
511             needsRefresh = false;
512         }
513     }
514     
515     private static class GetFocusedComponentAction extends TextAction {
516
517         private GetFocusedComponentAction() {
518             super("get-focused-component"); // NOI18N
519
}
520
521         public void actionPerformed(ActionEvent JavaDoc evt) {
522         }
523
524         JTextComponent getFocusedComponent2() {
525             return super.getFocusedComponent();
526         }
527
528     }
529     
530     class EditorUIBoxView extends BoxView{
531         public EditorUIBoxView(Element elem, int axis){
532             super(elem, axis);
533         }
534         
535         private DrawEngine.PreinitializedDrawEngine getPreinitializedDrawEngine(Graphics g){
536             DrawEngine.PreinitializedDrawEngine drawEngine = null;
537             
538             Rectangle clip = g.getClipBounds();
539             if (clip.height <= 0 || clip.width < 0) {
540                 return null;
541             }
542             
543             int clipY = clip.y;
544             int clipHeight = clip.height;
545             int paintY = Math.max(clipY, 0); // relative start of area to paint
546

547             BaseDocument doc = (BaseDocument)getEditorUI().getDocument();
548             try {
549                 int startPos = getPosFromY(paintY);
550                 int pos = getPosFromY(clipY + clipHeight - 1);
551                 int endPos = Utilities.getRowEnd(doc, pos);
552                 
553                 int startOffset = startPos;
554                 int endOffset = endPos;
555                 int y = getYFromPos(startOffset);
556                 
557                 if (endOffset > startOffset){
558                     drawEngine = DrawEngine.getDrawEngine().getDrawEngine(
559                     this,
560                     new DrawGraphics.GraphicsDG(g),
561                     getEditorUI(),
562                     startOffset,
563                     endOffset,
564                     getBaseX(y),
565                     y,
566                     Integer.MAX_VALUE
567                     );
568                 }
569             }catch(BadLocationException ble){
570                 ble.printStackTrace();
571             }
572             return drawEngine;
573         }
574         
575         public void paint(Graphics g, Shape allocation) {
576             super.paint(g, allocation);
577             DrawEngine.PreinitializedDrawEngine pde = null;
578             JTextComponent component = getComponent();
579             
580             try{
581                 pde = getPreinitializedDrawEngine(g);
582                 
583                 // share the instance of PreinitializedDrawEngine among children views
584
if (component!=null){
585                     component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, pde); //NOI18N
586
}
587                 
588                 getEditorUI().paint(g);
589                 
590             }finally{
591                 // release created preinitialized instance
592
if (pde!=null) pde.release();
593                 if (component!=null){
594                     component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, null);
595                 }
596             }
597         }
598     }
599     
600     static void uninstallUIWatcher(JTextComponent c) {
601         UIWatcher uiWatcher = (UIWatcher)c.getClientProperty(UIWatcher.class);
602         if (uiWatcher != null) {
603             c.removePropertyChangeListener(uiWatcher);
604             c.putClientProperty(UIWatcher.class, null);
605         }
606     }
607     
608     /** Class that returns back BaseTextUI after its change
609      * by changing look-and-feel.
610      */

611     static class UIWatcher implements PropertyChangeListener JavaDoc {
612         
613         private Class JavaDoc uiClass;
614         
615         UIWatcher(Class JavaDoc uiClass) {
616             this.uiClass = uiClass;
617         }
618         
619         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
620             Object JavaDoc newValue = evt.getNewValue();
621             if ("UI".equals(evt.getPropertyName())
622                 && (newValue != null) && !(newValue instanceof BaseTextUI)
623             ) {
624                 JTextComponent c = (JTextComponent)evt.getSource();
625                 EditorKit kit = ((TextUI JavaDoc)newValue).getEditorKit(c);
626                 if (kit instanceof BaseKit) {
627                     // BaseKit but not BaseTextUI -> restore BaseTextUI
628
try {
629                         c.setUI((BaseTextUI)uiClass.newInstance());
630                     } catch (InstantiationException JavaDoc e) {
631                     } catch (IllegalAccessException JavaDoc e) {
632                     }
633                 }
634             }
635         }
636         
637     }
638     
639 }
640
Popular Tags