KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicTextUI


1 /*
2  * @(#)BasicTextUI.java 1.106 05/06/03
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.plaf.basic;
8
9 import java.util.*;
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.awt.font.*;
13 import java.awt.datatransfer.*;
14 import java.awt.dnd.*;
15 import java.awt.im.InputContext JavaDoc;
16 import java.beans.*;
17 import java.io.*;
18 import java.net.*;
19 import javax.swing.*;
20 import javax.swing.plaf.*;
21 import javax.swing.text.*;
22 import javax.swing.event.*;
23 import javax.swing.border.Border JavaDoc;
24 import javax.swing.plaf.UIResource JavaDoc;
25 import sun.swing.DefaultLookup;
26 import sun.awt.AppContext;
27 import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag JavaDoc;
28
29 /**
30  * <p>
31  * Basis of a text components look-and-feel. This provides the
32  * basic editor view and controller services that may be useful
33  * when creating a look-and-feel for an extension of
34  * <code>JTextComponent</code>.
35  * <p>
36  * Most state is held in the associated <code>JTextComponent</code>
37  * as bound properties, and the UI installs default values for the
38  * various properties. This default will install something for
39  * all of the properties. Typically, a LAF implementation will
40  * do more however. At a minimum, a LAF would generally install
41  * key bindings.
42  * <p>
43  * This class also provides some concurrency support if the
44  * <code>Document</code> associated with the JTextComponent is a subclass of
45  * <code>AbstractDocument</code>. Access to the View (or View hierarchy) is
46  * serialized between any thread mutating the model and the Swing
47  * event thread (which is expected to render, do model/view coordinate
48  * translation, etc). <em>Any access to the root view should first
49  * acquire a read-lock on the AbstractDocument and release that lock
50  * in a finally block.</em>
51  * <p>
52  * An important method to define is the {@link #getPropertyPrefix} method
53  * which is used as the basis of the keys used to fetch defaults
54  * from the UIManager. The string should reflect the type of
55  * TextUI (eg. TextField, TextArea, etc) without the particular
56  * LAF part of the name (eg Metal, Motif, etc).
57  * <p>
58  * To build a view of the model, one of the following strategies
59  * can be employed.
60  * <ol>
61  * <li>
62  * One strategy is to simply redefine the
63  * ViewFactory interface in the UI. By default, this UI itself acts
64  * as the factory for View implementations. This is useful
65  * for simple factories. To do this reimplement the
66  * {@link #create} method.
67  * <li>
68  * A common strategy for creating more complex types of documents
69  * is to have the EditorKit implementation return a factory. Since
70  * the EditorKit ties all of the pieces necessary to maintain a type
71  * of document, the factory is typically an important part of that
72  * and should be produced by the EditorKit implementation.
73  * <li>
74  * A less common way to create more complex types is to have
75  * the UI implementation create a.
76  * separate object for the factory. To do this, the
77  * {@link #createViewFactory} method should be reimplemented to
78  * return some factory.
79  * </ol>
80  * <p>
81  * <strong>Warning:</strong>
82  * Serialized objects of this class will not be compatible with
83  * future Swing releases. The current serialization support is
84  * appropriate for short term storage or RMI between applications running
85  * the same version of Swing. As of 1.4, support for long term storage
86  * of all JavaBeans<sup><font size="-2">TM</font></sup>
87  * has been added to the <code>java.beans</code> package.
88  * Please see {@link java.beans.XMLEncoder}.
89  *
90  * @author Timothy Prinzing
91  * @author Shannon Hickey (drag recognition)
92  * @version 1.106 06/03/05
93  */

94 public abstract class BasicTextUI extends TextUI implements ViewFactory {
95
96     /**
97      * Creates a new UI.
98      */

99     public BasicTextUI() {
100         painted = false;
101     }
102
103     /**
104      * Creates the object to use for a caret. By default an
105      * instance of BasicCaret is created. This method
106      * can be redefined to provide something else that implements
107      * the InputPosition interface or a subclass of JCaret.
108      *
109      * @return the caret object
110      */

111     protected Caret createCaret() {
112         return new BasicCaret();
113     }
114
115     /**
116      * Creates the object to use for adding highlights. By default
117      * an instance of BasicHighlighter is created. This method
118      * can be redefined to provide something else that implements
119      * the Highlighter interface or a subclass of DefaultHighlighter.
120      *
121      * @return the highlighter
122      */

123     protected Highlighter createHighlighter() {
124         return new BasicHighlighter();
125     }
126
127     /**
128      * Fetches the name of the keymap that will be installed/used
129      * by default for this UI. This is implemented to create a
130      * name based upon the classname. The name is the the name
131      * of the class with the package prefix removed.
132      *
133      * @return the name
134      */

135     protected String JavaDoc getKeymapName() {
136     String JavaDoc nm = getClass().getName();
137     int index = nm.lastIndexOf('.');
138     if (index >= 0) {
139         nm = nm.substring(index+1, nm.length());
140     }
141     return nm;
142     }
143
144     /**
145      * Creates the keymap to use for the text component, and installs
146      * any necessary bindings into it. By default, the keymap is
147      * shared between all instances of this type of TextUI. The
148      * keymap has the name defined by the getKeymapName method. If the
149      * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
150      * <p>
151      * The set of bindings used to create the keymap is fetched
152      * from the UIManager using a key formed by combining the
153      * {@link #getPropertyPrefix} method
154      * and the string <code>.keyBindings</code>. The type is expected
155      * to be <code>JTextComponent.KeyBinding[]</code>.
156      *
157      * @return the keymap
158      * @see #getKeymapName
159      * @see javax.swing.text.JTextComponent
160      */

161     protected Keymap createKeymap() {
162     String JavaDoc nm = getKeymapName();
163     Keymap map = JTextComponent.getKeymap(nm);
164     if (map == null) {
165         Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
166         map = JTextComponent.addKeymap(nm, parent);
167         String JavaDoc prefix = getPropertyPrefix();
168         Object JavaDoc o = DefaultLookup.get(editor, this,
169                 prefix + ".keyBindings");
170         if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
171         JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
172         JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
173         }
174     }
175     return map;
176     }
177
178     /**
179      * This method gets called when a bound property is changed
180      * on the associated JTextComponent. This is a hook
181      * which UI implementations may change to reflect how the
182      * UI displays bound properties of JTextComponent subclasses.
183      * This is implemented to do nothing (i.e. the response to
184      * properties in JTextComponent itself are handled prior
185      * to calling this method).
186      *
187      * @param evt the property change event
188      */

189     protected void propertyChange(PropertyChangeEvent evt) {
190     }
191
192     /**
193      * Gets the name used as a key to look up properties through the
194      * UIManager. This is used as a prefix to all the standard
195      * text properties.
196      *
197      * @return the name
198      */

199     protected abstract String JavaDoc getPropertyPrefix();
200
201     /**
202      * Initializes component properties, e.g. font, foreground,
203      * background, caret color, selection color, selected text color,
204      * disabled text color, and border color. The font, foreground, and
205      * background properties are only set if their current value is either null
206      * or a UIResource, other properties are set if the current
207      * value is null.
208      *
209      * @see #uninstallDefaults
210      * @see #installUI
211      */

212     protected void installDefaults()
213     {
214         String JavaDoc prefix = getPropertyPrefix();
215         Font f = editor.getFont();
216         if ((f == null) || (f instanceof UIResource JavaDoc)) {
217             editor.setFont(UIManager.getFont(prefix + ".font"));
218         }
219
220         Color bg = editor.getBackground();
221         if ((bg == null) || (bg instanceof UIResource JavaDoc)) {
222             editor.setBackground(UIManager.getColor(prefix + ".background"));
223         }
224         
225         Color fg = editor.getForeground();
226         if ((fg == null) || (fg instanceof UIResource JavaDoc)) {
227             editor.setForeground(UIManager.getColor(prefix + ".foreground"));
228         }
229
230         Color color = editor.getCaretColor();
231         if ((color == null) || (color instanceof UIResource JavaDoc)) {
232             editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
233         }
234
235         Color s = editor.getSelectionColor();
236         if ((s == null) || (s instanceof UIResource JavaDoc)) {
237             editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
238         }
239
240         Color sfg = editor.getSelectedTextColor();
241         if ((sfg == null) || (sfg instanceof UIResource JavaDoc)) {
242             editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
243         }
244
245         Color dfg = editor.getDisabledTextColor();
246         if ((dfg == null) || (dfg instanceof UIResource JavaDoc)) {
247             editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
248         }
249
250         Border JavaDoc b = editor.getBorder();
251         if ((b == null) || (b instanceof UIResource JavaDoc)) {
252             editor.setBorder(UIManager.getBorder(prefix + ".border"));
253         }
254
255         Insets margin = editor.getMargin();
256         if (margin == null || margin instanceof UIResource JavaDoc) {
257             editor.setMargin(UIManager.getInsets(prefix + ".margin"));
258         }
259     }
260
261     private void installDefaults2() {
262         editor.addMouseListener(dragListener);
263         editor.addMouseMotionListener(dragListener);
264     
265         String JavaDoc prefix = getPropertyPrefix();
266
267         Caret caret = editor.getCaret();
268         if (caret == null || caret instanceof UIResource JavaDoc) {
269             caret = createCaret();
270             editor.setCaret(caret);
271         
272             int rate = DefaultLookup.getInt(getComponent(), this, prefix + ".caretBlinkRate", 500);
273             caret.setBlinkRate(rate);
274         }
275
276         Highlighter highlighter = editor.getHighlighter();
277         if (highlighter == null || highlighter instanceof UIResource JavaDoc) {
278             editor.setHighlighter(createHighlighter());
279         }
280
281     TransferHandler th = editor.getTransferHandler();
282     if (th == null || th instanceof UIResource JavaDoc) {
283         editor.setTransferHandler(getTransferHandler());
284     }
285     DropTarget dropTarget = editor.getDropTarget();
286     if (dropTarget instanceof UIResource JavaDoc) {
287             if (defaultDropTargetListener == null) {
288                 defaultDropTargetListener = new TextDropTargetListener();
289             }
290         try {
291         dropTarget.addDropTargetListener(defaultDropTargetListener);
292         } catch (TooManyListenersException tmle) {
293         // should not happen... swing drop target is multicast
294
}
295     }
296     }
297
298     /**
299      * Sets the component properties that haven't been explicitly overridden to
300      * null. A property is considered overridden if its current value
301      * is not a UIResource.
302      *
303      * @see #installDefaults
304      * @see #uninstallUI
305      */

306     protected void uninstallDefaults()
307     {
308         editor.removeMouseListener(dragListener);
309         editor.removeMouseMotionListener(dragListener);
310
311         if (editor.getCaretColor() instanceof UIResource JavaDoc) {
312             editor.setCaretColor(null);
313         }
314                                                                                          
315         if (editor.getSelectionColor() instanceof UIResource JavaDoc) {
316             editor.setSelectionColor(null);
317         }
318
319         if (editor.getDisabledTextColor() instanceof UIResource JavaDoc) {
320             editor.setDisabledTextColor(null);
321         }
322
323         if (editor.getSelectedTextColor() instanceof UIResource JavaDoc) {
324             editor.setSelectedTextColor(null);
325         }
326
327         if (editor.getBorder() instanceof UIResource JavaDoc) {
328             editor.setBorder(null);
329         }
330
331         if (editor.getMargin() instanceof UIResource JavaDoc) {
332             editor.setMargin(null);
333         }
334
335         if (editor.getCaret() instanceof UIResource JavaDoc) {
336             editor.setCaret(null);
337         }
338
339         if (editor.getHighlighter() instanceof UIResource JavaDoc) {
340             editor.setHighlighter(null);
341         }
342
343     if (editor.getTransferHandler() instanceof UIResource JavaDoc) {
344         editor.setTransferHandler(null);
345     }
346     }
347
348     /**
349      * Installs listeners for the UI.
350      */

351     protected void installListeners() {
352     }
353
354     /**
355      * Uninstalls listeners for the UI.
356      */

357     protected void uninstallListeners() {
358     }
359
360     protected void installKeyboardActions() {
361     // backward compatibility support... keymaps for the UI
362
// are now installed in the more friendly input map.
363
editor.setKeymap(createKeymap());
364
365         InputMap km = getInputMap();
366     if (km != null) {
367         SwingUtilities.replaceUIInputMap(editor, JComponent.WHEN_FOCUSED,
368                          km);
369     }
370     
371     ActionMap map = getActionMap();
372     if (map != null) {
373         SwingUtilities.replaceUIActionMap(editor, map);
374     }
375
376     updateFocusAcceleratorBinding(false);
377     }
378
379     /**
380      * Get the InputMap to use for the UI.
381      */

382     InputMap getInputMap() {
383     InputMap map = new InputMapUIResource();
384
385     InputMap shared =
386         (InputMap)DefaultLookup.get(editor, this,
387             getPropertyPrefix() + ".focusInputMap");
388     if (shared != null) {
389         map.setParent(shared);
390     }
391     return map;
392     }
393
394     /**
395      * Invoked when the focus accelerator changes, this will update the
396      * key bindings as necessary.
397      */

398     void updateFocusAcceleratorBinding(boolean changed) {
399     char accelerator = editor.getFocusAccelerator();
400
401     if (changed || accelerator != '\0') {
402         InputMap km = SwingUtilities.getUIInputMap
403                 (editor, JComponent.WHEN_IN_FOCUSED_WINDOW);
404
405         if (km == null && accelerator != '\0') {
406         km = new ComponentInputMapUIResource(editor);
407         SwingUtilities.replaceUIInputMap(editor, JComponent.
408                          WHEN_IN_FOCUSED_WINDOW, km);
409         ActionMap am = getActionMap();
410         SwingUtilities.replaceUIActionMap(editor, am);
411         }
412         if (km != null) {
413         km.clear();
414         if (accelerator != '\0') {
415             km.put(KeyStroke.getKeyStroke(accelerator,
416                           ActionEvent.ALT_MASK),
417                "requestFocus");
418         }
419         }
420     }
421     }
422
423
424     /**
425      * Invoked when editable property is changed.
426      *
427      * removing 'TAB' and 'SHIFT-TAB' from traversalKeysSet in case
428      * editor is editable
429      * adding 'TAB' and 'SHIFT-TAB' to traversalKeysSet in case
430      * editor is non editable
431      */

432
433     void updateFocusTraversalKeys() {
434     /*
435      * Fix for 4514331 Non-editable JTextArea and similar
436      * should allow Tab to keyboard - accessibility
437      */

438     EditorKit editorKit = getEditorKit(editor);
439     if ( editorKit != null
440          && editorKit instanceof DefaultEditorKit) {
441         Set storedForwardTraversalKeys = editor.
442         getFocusTraversalKeys(KeyboardFocusManager.
443                       FORWARD_TRAVERSAL_KEYS);
444         Set storedBackwardTraversalKeys = editor.
445         getFocusTraversalKeys(KeyboardFocusManager.
446                       BACKWARD_TRAVERSAL_KEYS);
447         Set forwardTraversalKeys =
448         new HashSet(storedForwardTraversalKeys);
449         Set backwardTraversalKeys =
450         new HashSet(storedBackwardTraversalKeys);
451         if (editor.isEditable()) {
452         forwardTraversalKeys.
453             remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
454         backwardTraversalKeys.
455             remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
456                           InputEvent.SHIFT_MASK));
457         } else {
458         forwardTraversalKeys.add(KeyStroke.
459                      getKeyStroke(KeyEvent.VK_TAB, 0));
460         backwardTraversalKeys.
461             add(KeyStroke.
462             getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
463         }
464             LookAndFeel.installProperty(editor,
465                                         "focusTraversalKeysForward",
466                      forwardTraversalKeys);
467             LookAndFeel.installProperty(editor,
468                                         "focusTraversalKeysBackward",
469                      backwardTraversalKeys);
470     }
471
472     }
473
474     /**
475      * Returns the <code>TransferHandler</code> that will be installed if
476      * their isn't one installed on the <code>JTextComponent</code>.
477      */

478     TransferHandler getTransferHandler() {
479         return defaultTransferHandler;
480     }
481
482     /**
483      * Fetch an action map to use.
484      */

485     ActionMap getActionMap() {
486     String JavaDoc mapName = getPropertyPrefix() + ".actionMap";
487     ActionMap map = (ActionMap)UIManager.get(mapName);
488
489     if (map == null) {
490         map = createActionMap();
491         if (map != null) {
492         UIManager.getLookAndFeelDefaults().put(mapName, map);
493         }
494     }
495         ActionMap componentMap = new ActionMapUIResource();
496         componentMap.put("requestFocus", new FocusAction());
497     /*
498      * fix for bug 4515750
499      * JTextField & non-editable JTextArea bind return key - default btn not accessible
500      *
501      * Wrap the return action so that it is only enabled when the
502      * component is editable. This allows the default button to be
503      * processed when the text component has focus and isn't editable.
504      *
505      */

506     if (getEditorKit(editor) instanceof DefaultEditorKit) {
507         if (map != null) {
508         Object JavaDoc obj = map.get(DefaultEditorKit.insertBreakAction);
509         if (obj != null
510             && obj instanceof DefaultEditorKit.InsertBreakAction) {
511             Action action = new TextActionWrapper((TextAction)obj);
512             componentMap.put(action.getValue(Action.NAME),action);
513         }
514         }
515     }
516         if (map != null) {
517             componentMap.setParent(map);
518         }
519     return componentMap;
520     }
521
522     /**
523      * Create a default action map. This is basically the
524      * set of actions found exported by the component.
525      */

526     ActionMap createActionMap() {
527     ActionMap map = new ActionMapUIResource();
528     Action[] actions = editor.getActions();
529     //System.out.println("building map for UI: " + getPropertyPrefix());
530
int n = actions.length;
531     for (int i = 0; i < n; i++) {
532         Action a = actions[i];
533         map.put(a.getValue(Action.NAME), a);
534         //System.out.println(" " + a.getValue(Action.NAME));
535
}
536         map.put(TransferHandler.getCutAction().getValue(Action.NAME),
537                 TransferHandler.getCutAction());
538         map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
539                 TransferHandler.getCopyAction());
540         map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
541                 TransferHandler.getPasteAction());
542     return map;
543     }
544
545     protected void uninstallKeyboardActions() {
546         editor.setKeymap(null);
547     SwingUtilities.replaceUIInputMap(editor, JComponent.
548                      WHEN_IN_FOCUSED_WINDOW, null);
549     SwingUtilities.replaceUIActionMap(editor, null);
550     }
551     
552     /**
553      * Paints a background for the view. This will only be
554      * called if isOpaque() on the associated component is
555      * true. The default is to paint the background color
556      * of the component.
557      *
558      * @param g the graphics context
559      */

560     protected void paintBackground(Graphics g) {
561         g.setColor(editor.getBackground());
562         g.fillRect(0, 0, editor.getWidth(), editor.getHeight());
563     }
564
565     /**
566      * Fetches the text component associated with this
567      * UI implementation. This will be null until
568      * the ui has been installed.
569      *
570      * @return the editor component
571      */

572     protected final JTextComponent getComponent() {
573         return editor;
574     }
575
576     /**
577      * Flags model changes.
578      * This is called whenever the model has changed.
579      * It is implemented to rebuild the view hierarchy
580      * to represent the default root element of the
581      * associated model.
582      */

583     protected void modelChanged() {
584         // create a view hierarchy
585
ViewFactory f = rootView.getViewFactory();
586         Document doc = editor.getDocument();
587         Element elem = doc.getDefaultRootElement();
588         setView(f.create(elem));
589     }
590
591     /**
592      * Sets the current root of the view hierarchy and calls invalidate().
593      * If there were any child components, they will be removed (i.e.
594      * there are assumed to have come from components embedded in views).
595      *
596      * @param v the root view
597      */

598     protected final void setView(View v) {
599         rootView.setView(v);
600         painted = false;
601         editor.revalidate();
602         editor.repaint();
603     }
604
605     /**
606      * Paints the interface safely with a guarantee that
607      * the model won't change from the view of this thread.
608      * This does the following things, rendering from
609      * back to front.
610      * <ol>
611      * <li>
612      * If the component is marked as opaque, the background
613      * is painted in the current background color of the
614      * component.
615      * <li>
616      * The highlights (if any) are painted.
617      * <li>
618      * The view hierarchy is painted.
619      * <li>
620      * The caret is painted.
621      * </ol>
622      *
623      * @param g the graphics context
624      */

625     protected void paintSafely(Graphics g) {
626     painted = true;
627     Highlighter highlighter = editor.getHighlighter();
628     Caret caret = editor.getCaret();
629     
630     // paint the background
631
if (editor.isOpaque()) {
632         paintBackground(g);
633     }
634     
635     // paint the highlights
636
if (highlighter != null) {
637         highlighter.paint(g);
638     }
639
640     // paint the view hierarchy
641
Rectangle alloc = getVisibleEditorRect();
642         if (alloc != null) {
643             rootView.paint(g, alloc);
644         }
645         
646     // paint the caret
647
if (caret != null) {
648         caret.paint(g);
649     }
650     }
651
652     // --- ComponentUI methods --------------------------------------------
653

654     /**
655      * Installs the UI for a component. This does the following
656      * things.
657      * <ol>
658      * <li>
659      * Set the associated component to opaque (can be changed
660      * easily by a subclass or on JTextComponent directly),
661      * which is the most common case. This will cause the
662      * component's background color to be painted.
663      * <li>
664      * Install the default caret and highlighter into the
665      * associated component.
666      * <li>
667      * Attach to the editor and model. If there is no
668      * model, a default one is created.
669      * <li>
670      * create the view factory and the view hierarchy used
671      * to represent the model.
672      * </ol>
673      *
674      * @param c the editor component
675      * @see ComponentUI#installUI
676      */

677     public void installUI(JComponent c) {
678         if (c instanceof JTextComponent) {
679             editor = (JTextComponent) c;
680
681             // install defaults
682
installDefaults();
683             installDefaults2();
684
685             // common case is background painted... this can
686
// easily be changed by subclasses or from outside
687
// of the component.
688
LookAndFeel.installProperty(editor, "opaque", Boolean.TRUE);
689             LookAndFeel.installProperty(editor, "autoscrolls", Boolean.TRUE);
690
691             // attach to the model and editor
692
editor.addPropertyChangeListener(updateHandler);
693             Document doc = editor.getDocument();
694             if (doc == null) {
695                 // no model, create a default one. This will
696
// fire a notification to the updateHandler
697
// which takes care of the rest.
698
editor.setDocument(getEditorKit(editor).createDefaultDocument());
699             } else {
700                 doc.addDocumentListener(updateHandler);
701                 modelChanged();
702             }
703
704             // install keymap
705
installListeners();
706             installKeyboardActions();
707
708         LayoutManager oldLayout = editor.getLayout();
709         if ((oldLayout == null) || (oldLayout instanceof UIResource JavaDoc)) {
710         // by default, use default LayoutManger implementation that
711
// will position the components associated with a View object.
712
editor.setLayout(updateHandler);
713         }
714
715         } else {
716             throw new Error JavaDoc("TextUI needs JTextComponent");
717         }
718     }
719
720     /**
721      * Deinstalls the UI for a component. This removes the listeners,
722      * uninstalls the highlighter, removes views, and nulls out the keymap.
723      *
724      * @param c the editor component
725      * @see ComponentUI#uninstallUI
726      */

727     public void uninstallUI(JComponent c) {
728         // detach from the model
729
editor.removePropertyChangeListener(updateHandler);
730         editor.getDocument().removeDocumentListener(updateHandler);
731
732         // view part
733
painted = false;
734         uninstallDefaults();
735         rootView.setView(null);
736         c.removeAll();
737     LayoutManager lm = c.getLayout();
738     if (lm instanceof UIResource JavaDoc) {
739         c.setLayout(null);
740     }
741
742         // controller part
743
uninstallKeyboardActions();
744         uninstallListeners();
745
746         editor = null;
747     }
748
749     /**
750      * Superclass paints background in an uncontrollable way
751      * (i.e. one might want an image tiled into the background).
752      * To prevent this from happening twice, this method is
753      * reimplemented to simply paint.
754      * <p>
755      * <em>NOTE:</em> Superclass is also not thread-safe in
756      * it's rendering of the background, although that's not
757      * an issue with the default rendering.
758      */

759     public void update(Graphics g, JComponent c) {
760     paint(g, c);
761     }
762
763     /**
764      * Paints the interface. This is routed to the
765      * paintSafely method under the guarantee that
766      * the model won't change from the view of this thread
767      * while it's rendering (if the associated model is
768      * derived from AbstractDocument). This enables the
769      * model to potentially be updated asynchronously.
770      *
771      * @param g the graphics context
772      * @param c the editor component
773      */

774     public final void paint(Graphics g, JComponent c) {
775     if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
776         Document doc = editor.getDocument();
777         if (doc instanceof AbstractDocument) {
778         ((AbstractDocument)doc).readLock();
779         }
780         try {
781         paintSafely(g);
782         } finally {
783         if (doc instanceof AbstractDocument) {
784             ((AbstractDocument)doc).readUnlock();
785         }
786         }
787     }
788     }
789
790     /**
791      * Gets the preferred size for the editor component. If the component
792      * has been given a size prior to receiving this request, it will
793      * set the size of the view hierarchy to reflect the size of the component
794      * before requesting the preferred size of the view hierarchy. This
795      * allows formatted views to format to the current component size before
796      * answering the request. Other views don't care about currently formatted
797      * size and give the same answer either way.
798      *
799      * @param c the editor component
800      * @return the size
801      */

802     public Dimension getPreferredSize(JComponent c) {
803     Document doc = editor.getDocument();
804     Insets i = c.getInsets();
805     Dimension d = c.getSize();
806
807     if (doc instanceof AbstractDocument) {
808         ((AbstractDocument)doc).readLock();
809     }
810     try {
811         if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
812         rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
813         }
814             else if (d.width == 0 && d.height == 0) {
815                 // Probably haven't been layed out yet, force some sort of
816
// initial sizing.
817
rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
818             }
819         d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
820                      (long) i.left + (long) i.right, Integer.MAX_VALUE);
821         d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
822                       (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
823     } finally {
824         if (doc instanceof AbstractDocument) {
825         ((AbstractDocument)doc).readUnlock();
826         }
827     }
828     return d;
829     }
830
831     /**
832      * Gets the minimum size for the editor component.
833      *
834      * @param c the editor component
835      * @return the size
836      */

837     public Dimension getMinimumSize(JComponent c) {
838     Document doc = editor.getDocument();
839         Insets i = c.getInsets();
840     Dimension d = new Dimension();
841     if (doc instanceof AbstractDocument) {
842         ((AbstractDocument)doc).readLock();
843     }
844     try {
845         d.width = (int) rootView.getMinimumSpan(View.X_AXIS) + i.left + i.right;
846         d.height = (int) rootView.getMinimumSpan(View.Y_AXIS) + i.top + i.bottom;
847     } finally {
848         if (doc instanceof AbstractDocument) {
849         ((AbstractDocument)doc).readUnlock();
850         }
851     }
852         return d;
853     }
854
855     /**
856      * Gets the maximum size for the editor component.
857      *
858      * @param c the editor component
859      * @return the size
860      */

861     public Dimension getMaximumSize(JComponent c) {
862     Document doc = editor.getDocument();
863         Insets i = c.getInsets();
864     Dimension d = new Dimension();
865     if (doc instanceof AbstractDocument) {
866         ((AbstractDocument)doc).readLock();
867     }
868     try {
869         d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) +
870                      (long) i.left + (long) i.right, Integer.MAX_VALUE);
871         d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) +
872                       (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
873     } finally {
874         if (doc instanceof AbstractDocument) {
875         ((AbstractDocument)doc).readUnlock();
876         }
877     }
878         return d;
879     }
880
881     // ---- TextUI methods -------------------------------------------
882

883
884     /**
885      * Gets the allocation to give the root View. Due
886      * to an unfortunate set of historical events this
887      * method is inappropriately named. The Rectangle
888      * returned has nothing to do with visibility.
889      * The component must have a non-zero positive size for
890      * this translation to be computed.
891      *
892      * @return the bounding box for the root view
893      */

894     protected Rectangle getVisibleEditorRect() {
895     Rectangle alloc = editor.getBounds();
896     if ((alloc.width > 0) && (alloc.height > 0)) {
897         alloc.x = alloc.y = 0;
898         Insets insets = editor.getInsets();
899         alloc.x += insets.left;
900         alloc.y += insets.top;
901         alloc.width -= insets.left + insets.right;
902         alloc.height -= insets.top + insets.bottom;
903         return alloc;
904     }
905     return null;
906     }
907
908     /**
909      * Converts the given location in the model to a place in
910      * the view coordinate system.
911      * The component must have a non-zero positive size for
912      * this translation to be computed.
913      *
914      * @param tc the text component for which this UI is installed
915      * @param pos the local location in the model to translate >= 0
916      * @return the coordinates as a rectangle, null if the model is not painted
917      * @exception BadLocationException if the given position does not
918      * represent a valid location in the associated document
919      * @see TextUI#modelToView
920      */

921     public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
922     return modelToView(tc, pos, Position.Bias.Forward);
923     }
924
925     /**
926      * Converts the given location in the model to a place in
927      * the view coordinate system.
928      * The component must have a non-zero positive size for
929      * this translation to be computed.
930      *
931      * @param tc the text component for which this UI is installed
932      * @param pos the local location in the model to translate >= 0
933      * @return the coordinates as a rectangle, null if the model is not painted
934      * @exception BadLocationException if the given position does not
935      * represent a valid location in the associated document
936      * @see TextUI#modelToView
937      */

938     public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
939     Document doc = editor.getDocument();
940     if (doc instanceof AbstractDocument) {
941         ((AbstractDocument)doc).readLock();
942     }
943     try {
944         Rectangle alloc = getVisibleEditorRect();
945         if (alloc != null) {
946         rootView.setSize(alloc.width, alloc.height);
947         Shape s = rootView.modelToView(pos, alloc, bias);
948         if (s != null) {
949           return s.getBounds();
950         }
951         }
952     } finally {
953         if (doc instanceof AbstractDocument) {
954         ((AbstractDocument)doc).readUnlock();
955         }
956     }
957     return null;
958     }
959
960     /**
961      * Converts the given place in the view coordinate system
962      * to the nearest representative location in the model.
963      * The component must have a non-zero positive size for
964      * this translation to be computed.
965      *
966      * @param tc the text component for which this UI is installed
967      * @param pt the location in the view to translate. This
968      * should be in the same coordinate system as the mouse events.
969      * @return the offset from the start of the document >= 0,
970      * -1 if not painted
971      * @see TextUI#viewToModel
972      */

973     public int viewToModel(JTextComponent tc, Point pt) {
974     return viewToModel(tc, pt, discardBias);
975     }
976
977     /**
978      * Converts the given place in the view coordinate system
979      * to the nearest representative location in the model.
980      * The component must have a non-zero positive size for
981      * this translation to be computed.
982      *
983      * @param tc the text component for which this UI is installed
984      * @param pt the location in the view to translate. This
985      * should be in the same coordinate system as the mouse events.
986      * @return the offset from the start of the document >= 0,
987      * -1 if the component doesn't yet have a positive size.
988      * @see TextUI#viewToModel
989      */

990     public int viewToModel(JTextComponent tc, Point pt,
991                Position.Bias[] biasReturn) {
992     int offs = -1;
993     Document doc = editor.getDocument();
994     if (doc instanceof AbstractDocument) {
995         ((AbstractDocument)doc).readLock();
996     }
997     try {
998         Rectangle alloc = getVisibleEditorRect();
999         if (alloc != null) {
1000        rootView.setSize(alloc.width, alloc.height);
1001        offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
1002        }
1003    } finally {
1004        if (doc instanceof AbstractDocument) {
1005        ((AbstractDocument)doc).readUnlock();
1006<