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        }
1007    }
1008        return offs;
1009    }
1010
1011    /**
1012     * Provides a way to determine the next visually represented model
1013     * location that one might place a caret. Some views may not be visible,
1014     * they might not be in the same order found in the model, or they just
1015     * might not allow access to some of the locations in the model.
1016     *
1017     * @param pos the position to convert >= 0
1018     * @param a the allocated region to render into
1019     * @param direction the direction from the current position that can
1020     * be thought of as the arrow keys typically found on a keyboard.
1021     * This may be SwingConstants.WEST, SwingConstants.EAST,
1022     * SwingConstants.NORTH, or SwingConstants.SOUTH.
1023     * @return the location within the model that best represents the next
1024     * location visual position.
1025     * @exception BadLocationException
1026     * @exception IllegalArgumentException for an invalid direction
1027     */

1028    public int getNextVisualPositionFrom(JTextComponent t, int pos,
1029            Position.Bias b, int direction, Position.Bias[] biasRet)
1030                throws BadLocationException{
1031    Document doc = editor.getDocument();
1032    if (doc instanceof AbstractDocument) {
1033        ((AbstractDocument)doc).readLock();
1034    }
1035    try {
1036        if (painted) {
1037        Rectangle alloc = getVisibleEditorRect();
1038                if (alloc != null) {
1039                    rootView.setSize(alloc.width, alloc.height);
1040                }
1041        return rootView.getNextVisualPositionFrom(pos, b, alloc, direction,
1042                              biasRet);
1043        }
1044    } finally {
1045        if (doc instanceof AbstractDocument) {
1046        ((AbstractDocument)doc).readUnlock();
1047        }
1048    }
1049    return -1;
1050    }
1051
1052    /**
1053     * Causes the portion of the view responsible for the
1054     * given part of the model to be repainted. Does nothing if
1055     * the view is not currently painted.
1056     *
1057     * @param tc the text component for which this UI is installed
1058     * @param p0 the beginning of the range >= 0
1059     * @param p1 the end of the range >= p0
1060     * @see TextUI#damageRange
1061     */

1062    public void damageRange(JTextComponent tc, int p0, int p1) {
1063    damageRange(tc, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
1064    }
1065
1066    /**
1067     * Causes the portion of the view responsible for the
1068     * given part of the model to be repainted.
1069     *
1070     * @param p0 the beginning of the range >= 0
1071     * @param p1 the end of the range >= p0
1072     */

1073    public void damageRange(JTextComponent t, int p0, int p1,
1074                Position.Bias p0Bias, Position.Bias p1Bias) {
1075        if (painted) {
1076            Rectangle alloc = getVisibleEditorRect();
1077            if (alloc != null) {
1078                Document doc = t.getDocument();
1079                if (doc instanceof AbstractDocument) {
1080                    ((AbstractDocument)doc).readLock();
1081                }
1082                try {
1083                    rootView.setSize(alloc.width, alloc.height);
1084                    Shape toDamage = rootView.modelToView(p0, p0Bias,
1085                            p1, p1Bias, alloc);
1086                    Rectangle rect = (toDamage instanceof Rectangle) ?
1087                            (Rectangle)toDamage : toDamage.getBounds();
1088                    editor.repaint(rect.x, rect.y, rect.width, rect.height);
1089                } catch (BadLocationException e) {
1090                } finally {
1091                    if (doc instanceof AbstractDocument) {
1092                        ((AbstractDocument)doc).readUnlock();
1093                    }
1094                }
1095            }
1096        }
1097    }
1098
1099    /**
1100     * Fetches the EditorKit for the UI.
1101     *
1102     * @param tc the text component for which this UI is installed
1103     * @return the editor capabilities
1104     * @see TextUI#getEditorKit
1105     */

1106    public EditorKit getEditorKit(JTextComponent tc) {
1107        return defaultKit;
1108    }
1109
1110    /**
1111     * Fetches a View with the allocation of the associated
1112     * text component (i.e. the root of the hierarchy) that
1113     * can be traversed to determine how the model is being
1114     * represented spatially.
1115     * <p>
1116     * <font color=red><b>NOTE:</b>The View hierarchy can
1117     * be traversed from the root view, and other things
1118     * can be done as well. Things done in this way cannot
1119     * be protected like simple method calls through the TextUI.
1120     * Therefore, proper operation in the presence of concurrency
1121     * must be arranged by any logic that calls this method!
1122     * </font>
1123     *
1124     * @param tc the text component for which this UI is installed
1125     * @return the view
1126     * @see TextUI#getRootView
1127     */

1128    public View getRootView(JTextComponent tc) {
1129        return rootView;
1130    }
1131
1132
1133    /**
1134     * Returns the string to be used as the tooltip at the passed in location.
1135     * This forwards the method onto the root View.
1136     *
1137     * @see javax.swing.text.JTextComponent#getToolTipText
1138     * @see javax.swing.text.View#getToolTipText
1139     * @since 1.4
1140     */

1141    public String JavaDoc getToolTipText(JTextComponent t, Point pt) {
1142        if (!painted) {
1143            return null;
1144        }
1145        Document doc = editor.getDocument();
1146        String JavaDoc tt = null;
1147        Rectangle alloc = getVisibleEditorRect();
1148
1149        if (alloc != null) {
1150            if (doc instanceof AbstractDocument) {
1151                ((AbstractDocument)doc).readLock();
1152            }
1153            try {
1154                tt = rootView.getToolTipText(pt.x, pt.y, alloc);
1155            } finally {
1156                if (doc instanceof AbstractDocument) {
1157                    ((AbstractDocument)doc).readUnlock();
1158                }
1159            }
1160        }
1161        return tt;
1162    }
1163
1164    // --- ViewFactory methods ------------------------------
1165

1166    /**
1167     * Creates a view for an element.
1168     * If a subclass wishes to directly implement the factory
1169     * producing the view(s), it should reimplement this
1170     * method. By default it simply returns null indicating
1171     * it is unable to represent the element.
1172     *
1173     * @param elem the element
1174     * @return the view
1175     */

1176    public View create(Element elem) {
1177        return null;
1178    }
1179
1180    /**
1181     * Creates a view for an element.
1182     * If a subclass wishes to directly implement the factory
1183     * producing the view(s), it should reimplement this
1184     * method. By default it simply returns null indicating
1185     * it is unable to represent the part of the element.
1186     *
1187     * @param elem the element
1188     * @param p0 the starting offset >= 0
1189     * @param p1 the ending offset >= p0
1190     * @return the view
1191     */

1192    public View create(Element elem, int p0, int p1) {
1193        return null;
1194    }
1195
1196    public static class BasicCaret extends DefaultCaret implements UIResource JavaDoc {}
1197
1198    public static class BasicHighlighter extends DefaultHighlighter implements UIResource JavaDoc {}
1199
1200
1201    // ----- member variables ---------------------------------------
1202

1203    private static final EditorKit defaultKit = new DefaultEditorKit();
1204    transient JTextComponent editor;
1205    transient boolean painted;
1206    transient RootView rootView = new RootView();
1207    transient UpdateHandler updateHandler = new UpdateHandler();
1208    private static final TransferHandler defaultTransferHandler = new TextTransferHandler();
1209    private static DropTargetListener defaultDropTargetListener = null;
1210    private final DragListener dragListener = getDragListener();
1211    private static final Position.Bias[] discardBias = new Position.Bias[1];
1212
1213    /**
1214     * Root view that acts as a gateway between the component
1215     * and the View hierarchy.
1216     */

1217    class RootView extends View {
1218
1219        RootView() {
1220            super(null);
1221        }
1222
1223        void setView(View v) {
1224            View oldView = view;
1225            view = null;
1226            if (oldView != null) {
1227                // get rid of back reference so that the old
1228
// hierarchy can be garbage collected.
1229
oldView.setParent(null);
1230            }
1231            if (v != null) {
1232                v.setParent(this);
1233            }
1234            view = v;
1235        }
1236
1237    /**
1238     * Fetches the attributes to use when rendering. At the root
1239     * level there are no attributes. If an attribute is resolved
1240     * up the view hierarchy this is the end of the line.
1241     */

1242        public AttributeSet getAttributes() {
1243        return null;
1244    }
1245
1246        /**
1247         * Determines the preferred span for this view along an axis.
1248         *
1249         * @param axis may be either X_AXIS or Y_AXIS
1250         * @return the span the view would like to be rendered into.
1251         * Typically the view is told to render into the span
1252         * that is returned, although there is no guarantee.
1253         * The parent may choose to resize or break the view.
1254         */

1255        public float getPreferredSpan(int axis) {
1256            if (view != null) {
1257                return view.getPreferredSpan(axis);
1258            }
1259            return 10;
1260        }
1261
1262        /**
1263         * Determines the minimum span for this view along an axis.
1264         *
1265         * @param axis may be either X_AXIS or Y_AXIS
1266         * @return the span the view would like to be rendered into.
1267         * Typically the view is told to render into the span
1268         * that is returned, although there is no guarantee.
1269         * The parent may choose to resize or break the view.
1270         */

1271        public float getMinimumSpan(int axis) {
1272            if (view != null) {
1273                return view.getMinimumSpan(axis);
1274            }
1275            return 10;
1276        }
1277
1278        /**
1279         * Determines the maximum span for this view along an axis.
1280         *
1281         * @param axis may be either X_AXIS or Y_AXIS
1282         * @return the span the view would like to be rendered into.
1283         * Typically the view is told to render into the span
1284         * that is returned, although there is no guarantee.
1285         * The parent may choose to resize or break the view.
1286         */

1287        public float getMaximumSpan(int axis) {
1288        return Integer.MAX_VALUE;
1289        }
1290
1291        /**
1292         * Specifies that a preference has changed.
1293         * Child views can call this on the parent to indicate that
1294         * the preference has changed. The root view routes this to
1295         * invalidate on the hosting component.
1296         * <p>
1297         * This can be called on a different thread from the
1298         * event dispatching thread and is basically unsafe to
1299         * propagate into the component. To make this safe,
1300         * the operation is transferred over to the event dispatching
1301         * thread for completion. It is a design goal that all view
1302         * methods be safe to call without concern for concurrency,
1303         * and this behavior helps make that true.
1304         *
1305         * @param child the child view
1306         * @param width true if the width preference has changed
1307         * @param height true if the height preference has changed
1308         */

1309        public void preferenceChanged(View child, boolean width, boolean height) {
1310            editor.revalidate();
1311        }
1312
1313        /**
1314         * Determines the desired alignment for this view along an axis.
1315         *
1316         * @param axis may be either X_AXIS or Y_AXIS
1317         * @return the desired alignment, where 0.0 indicates the origin
1318         * and 1.0 the full span away from the origin
1319         */

1320        public float getAlignment(int axis) {
1321            if (view != null) {
1322                return view.getAlignment(axis);
1323            }
1324            return 0;
1325        }
1326
1327        /**
1328         * Renders the view.
1329         *
1330         * @param g the graphics context
1331         * @param allocation the region to render into
1332         */

1333        public void paint(Graphics g, Shape allocation) {
1334            if (view != null) {
1335                Rectangle alloc = (allocation instanceof Rectangle) ?
1336                  (Rectangle)allocation : allocation.getBounds();
1337        setSize(alloc.width, alloc.height);
1338                view.paint(g, allocation);
1339            }
1340        }
1341        
1342        /**
1343         * Sets the view parent.
1344         *
1345         * @param parent the parent view
1346         */

1347        public void setParent(View parent) {
1348            throw new Error JavaDoc("Can't set parent on root view");
1349        }
1350
1351        /**
1352         * Returns the number of views in this view. Since
1353         * this view simply wraps the root of the view hierarchy
1354         * it has exactly one child.
1355         *
1356         * @return the number of views
1357         * @see #getView
1358         */

1359        public int getViewCount() {
1360            return 1;
1361        }
1362
1363        /**
1364         * Gets the n-th view in this container.
1365         *
1366         * @param n the number of the view to get
1367         * @return the view
1368         */

1369        public View getView(int n) {
1370            return view;
1371        }
1372
1373    /**
1374     * Returns the child view index representing the given position in
1375     * the model. This is implemented to return the index of the only
1376     * child.
1377     *
1378     * @param pos the position >= 0
1379     * @return index of the view representing the given position, or
1380     * -1 if no view represents that position
1381     * @since 1.3
1382     */

1383        public int getViewIndex(int pos, Position.Bias b) {
1384        return 0;
1385    }
1386    
1387        /**
1388         * Fetches the allocation for the given child view.
1389         * This enables finding out where various views
1390         * are located, without assuming the views store
1391         * their location. This returns the given allocation
1392         * since this view simply acts as a gateway between
1393         * the view hierarchy and the associated component.
1394         *
1395         * @param index the index of the child
1396         * @param a the allocation to this view.
1397         * @return the allocation to the child
1398         */

1399        public Shape getChildAllocation(int index, Shape a) {
1400            return a;
1401        }
1402
1403        /**
1404         * Provides a mapping from the document model coordinate space
1405         * to the coordinate space of the view mapped to it.
1406         *
1407         * @param pos the position to convert
1408         * @param a the allocated region to render into
1409         * @return the bounding box of the given position
1410         */

1411        public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
1412            if (view != null) {
1413                return view.modelToView(pos, a, b);
1414            }
1415            return null;
1416        }
1417
1418    /**
1419     * Provides a mapping from the document model coordinate space
1420     * to the coordinate space of the view mapped to it.
1421     *
1422     * @param p0 the position to convert >= 0
1423     * @param b0 the bias toward the previous character or the
1424     * next character represented by p0, in case the
1425     * position is a boundary of two views.
1426     * @param p1 the position to convert >= 0
1427     * @param b1 the bias toward the previous character or the
1428     * next character represented by p1, in case the
1429     * position is a boundary of two views.
1430     * @param a the allocated region to render into
1431     * @return the bounding box of the given position is returned
1432     * @exception BadLocationException if the given position does
1433     * not represent a valid location in the associated document
1434     * @exception IllegalArgumentException for an invalid bias argument
1435     * @see View#viewToModel
1436     */

1437    public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
1438        if (view != null) {
1439        return view.modelToView(p0, b0, p1, b1, a);
1440        }
1441        return null;
1442    }
1443
1444        /**
1445         * Provides a mapping from the view coordinate space to the logical
1446         * coordinate space of the model.
1447         *
1448         * @param x x coordinate of the view location to convert
1449         * @param y y coordinate of the view location to convert
1450         * @param a the allocated region to render into
1451         * @return the location within the model that best represents the
1452         * given point in the view
1453         */

1454        public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
1455            if (view != null) {
1456                int retValue = view.viewToModel(x, y, a, bias);
1457        return retValue;
1458            }
1459            return -1;
1460        }
1461
1462        /**
1463         * Provides a way to determine the next visually represented model
1464         * location that one might place a caret. Some views may not be visible,
1465         * they might not be in the same order found in the model, or they just
1466         * might not allow access to some of the locations in the model.
1467         *
1468         * @param pos the position to convert >= 0
1469         * @param a the allocated region to render into
1470         * @param direction the direction from the current position that can
1471         * be thought of as the arrow keys typically found on a keyboard.
1472         * This may be SwingConstants.WEST, SwingConstants.EAST,
1473         * SwingConstants.NORTH, or SwingConstants.SOUTH.
1474         * @return the location within the model that best represents the next
1475         * location visual position.
1476         * @exception BadLocationException
1477         * @exception IllegalArgumentException for an invalid direction
1478         */

1479        public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
1480                                             int direction,
1481                                             Position.Bias[] biasRet)
1482            throws BadLocationException {
1483            if( view != null ) {
1484                int nextPos = view.getNextVisualPositionFrom(pos, b, a,
1485                             direction, biasRet);
1486        if(nextPos != -1) {
1487            pos = nextPos;
1488        }
1489        else {
1490            biasRet[0] = b;
1491        }
1492            }
1493            return pos;
1494        }
1495
1496        /**
1497         * Gives notification that something was inserted into the document
1498         * in a location that this view is responsible for.
1499         *
1500         * @param e the change information from the associated document
1501         * @param a the current allocation of the view
1502         * @param f the factory to use to rebuild if the view has children
1503         */

1504        public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1505            if (view != null) {
1506                view.insertUpdate(e, a, f);
1507            }
1508        }
1509        
1510        /**
1511         * Gives notification that something was removed from the document
1512         * in a location that this view is responsible for.
1513         *
1514         * @param e the change information from the associated document
1515         * @param a the current allocation of the view
1516         * @param f the factory to use to rebuild if the view has children
1517         */

1518        public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1519            if (view != null) {
1520                view.removeUpdate(e, a, f);
1521            }
1522        }
1523
1524        /**
1525         * Gives notification from the document that attributes were changed
1526         * in a location that this view is responsible for.
1527         *
1528         * @param e the change information from the associated document
1529         * @param a the current allocation of the view
1530         * @param f the factory to use to rebuild if the view has children
1531         */

1532        public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1533            if (view != null) {
1534                view.changedUpdate(e, a, f);
1535            }
1536        }
1537
1538        /**
1539         * Returns the document model underlying the view.
1540         *
1541         * @return the model
1542         */

1543        public Document getDocument() {
1544            return editor.getDocument();
1545        }
1546        
1547        /**
1548         * Returns the starting offset into the model for this view.
1549         *
1550         * @return the starting offset
1551         */

1552        public int getStartOffset() {
1553            if (view != null) {
1554                return view.getStartOffset();
1555            }
1556            return getElement().getStartOffset();
1557        }
1558
1559        /**
1560         * Returns the ending offset into the model for this view.
1561         *
1562         * @return the ending offset
1563         */

1564        public int getEndOffset() {
1565            if (view != null) {
1566                return view.getEndOffset();
1567            }
1568            return getElement().getEndOffset();
1569        }
1570
1571        /**
1572         * Gets the element that this view is mapped to.
1573         *
1574         * @return the view
1575         */

1576        public Element getElement() {
1577            if (view != null) {
1578                return view.getElement();
1579            }
1580            return editor.getDocument().getDefaultRootElement();
1581        }
1582
1583        /**
1584         * Breaks this view on the given axis at the given length.
1585         *
1586         * @param axis may be either X_AXIS or Y_AXIS
1587         * @param len specifies where a break is desired in the span
1588         * @param the current allocation of the view
1589         * @return the fragment of the view that represents the given span
1590         * if the view can be broken, otherwise null
1591         */

1592        public View breakView(int axis, float len, Shape a) {
1593            throw new Error JavaDoc("Can't break root view");
1594        }
1595
1596        /**
1597         * Determines the resizability of the view along the
1598         * given axis. A value of 0 or less is not resizable.
1599         *
1600         * @param axis may be either X_AXIS or Y_AXIS
1601         * @return the weight
1602         */

1603        public int getResizeWeight(int axis) {
1604            if (view != null) {
1605                return view.getResizeWeight(axis);
1606            }
1607            return 0;
1608        }
1609
1610        /**
1611         * Sets the view size.
1612         *
1613         * @param width the width
1614         * @param height the height
1615         */

1616        public void setSize(float width, float height) {
1617            if (view != null) {
1618                view.setSize(width, height);
1619            }
1620        }
1621
1622        /**
1623         * Fetches the container hosting the view. This is useful for
1624         * things like scheduling a repaint, finding out the host
1625         * components font, etc. The default implementation
1626         * of this is to forward the query to the parent view.
1627         *
1628         * @return the container
1629         */

1630        public Container getContainer() {
1631            return editor;
1632        }
1633        
1634        /**
1635         * Fetches the factory to be used for building the
1636         * various view fragments that make up the view that
1637         * represents the model. This is what determines
1638         * how the model will be represented. This is implemented
1639         * to fetch the factory provided by the associated
1640         * EditorKit unless that is null, in which case this
1641         * simply returns the BasicTextUI itself which allows
1642         * subclasses to implement a simple factory directly without
1643         * creating extra objects.
1644         *
1645         * @return the factory
1646         */

1647        public ViewFactory getViewFactory() {
1648            EditorKit kit = getEditorKit(editor);
1649            ViewFactory f = kit.getViewFactory();
1650            if (f != null) {
1651                return f;
1652            }
1653            return BasicTextUI.this;
1654        }
1655
1656        private View view;
1657
1658    }
1659
1660    /**
1661     * Handles updates from various places. If the model is changed,
1662     * this class unregisters as a listener to the old model and
1663     * registers with the new model. If the document model changes,
1664     * the change is forwarded to the root view. If the focus
1665     * accelerator changes, a new keystroke is registered to request
1666     * focus.
1667     */

1668    class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, UIResource JavaDoc {
1669
1670        // --- PropertyChangeListener methods -----------------------
1671

1672        /**
1673         * This method gets called when a bound property is changed.
1674         * We are looking for document changes on the editor.
1675         */

1676        public final void propertyChange(PropertyChangeEvent evt) {
1677            Object JavaDoc oldValue = evt.getOldValue();
1678            Object JavaDoc newValue = evt.getNewValue();
1679        String JavaDoc propertyName = evt.getPropertyName();
1680            if ((oldValue instanceof Document) || (newValue instanceof Document)) {
1681                if (oldValue != null) {
1682                    ((Document)oldValue).removeDocumentListener(this);
1683            i18nView = false;
1684                }
1685                if (newValue != null) {
1686                    ((Document)newValue).addDocumentListener(this);
1687                    if ("document" == propertyName) {
1688                        setView(null);
1689                        BasicTextUI.this.propertyChange(evt);
1690                        modelChanged();
1691                        return;
1692                    }
1693                }
1694                modelChanged();
1695            }
1696        if ("focusAccelerator" == propertyName) {
1697        updateFocusAcceleratorBinding(true);
1698            } else if ("componentOrientation" == propertyName) {
1699                // Changes in ComponentOrientation require the views to be
1700
// rebuilt.
1701
modelChanged();
1702            } else if ("font" == propertyName) {
1703                modelChanged();
1704            } else if ("transferHandler" == propertyName) {
1705        DropTarget dropTarget = editor.getDropTarget();
1706        if (dropTarget instanceof UIResource JavaDoc) {
1707                    if (defaultDropTargetListener == null) {
1708                        defaultDropTargetListener = new TextDropTargetListener();
1709                    }
1710            try {
1711            dropTarget.addDropTargetListener(defaultDropTargetListener);
1712            } catch (TooManyListenersException tmle) {
1713            // should not happen... swing drop target is multicast
1714
}
1715        }
1716        } else if ("editable" == propertyName) {
1717                modelChanged();
1718        }
1719            BasicTextUI.this.propertyChange(evt);
1720        }
1721
1722        // --- DocumentListener methods -----------------------
1723

1724        /**
1725         * The insert notification. Gets sent to the root of the view structure
1726         * that represents the portion of the model being represented by the
1727         * editor. The factory is added as an argument to the update so that
1728         * the views can update themselves in a dynamic (not hardcoded) way.
1729         *
1730         * @param e The change notification from the currently associated
1731         * document.
1732         * @see DocumentListener#insertUpdate
1733         */

1734        public final void insertUpdate(DocumentEvent e) {
1735        Document doc = e.getDocument();
1736        Object JavaDoc o = doc.getProperty("i18n");
1737        if (o instanceof Boolean JavaDoc) {
1738        Boolean JavaDoc i18nFlag = (Boolean JavaDoc) o;
1739        if (i18nFlag.booleanValue() != i18nView) {
1740            // i18n flag changed, rebuild the view
1741
i18nView = i18nFlag.booleanValue();
1742            modelChanged();
1743            return;
1744        }
1745        }
1746
1747        // normal insert update
1748
Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1749            rootView.insertUpdate(e, alloc, rootView.getViewFactory());
1750        }
1751
1752        /**
1753         * The remove notification. Gets sent to the root of the view structure
1754         * that represents the portion of the model being represented by the
1755         * editor. The factory is added as an argument to the update so that
1756         * the views can update themselves in a dynamic (not hardcoded) way.
1757         *
1758         * @param e The change notification from the currently associated
1759         * document.
1760         * @see DocumentListener#removeUpdate
1761         */

1762        public final void removeUpdate(DocumentEvent e) {
1763            Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1764            rootView.removeUpdate(e, alloc, rootView.getViewFactory());
1765    }
1766
1767        /**
1768         * The change notification. Gets sent to the root of the view structure
1769         * that represents the portion of the model being represented by the
1770         * editor. The factory is added as an argument to the update so that
1771         * the views can update themselves in a dynamic (not hardcoded) way.
1772         *
1773         * @param e The change notification from the currently associated
1774         * document.
1775         * @see DocumentListener#changeUpdate
1776         */

1777        public final void changedUpdate(DocumentEvent e) {
1778            Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1779            rootView.changedUpdate(e, alloc, rootView.getViewFactory());
1780        }
1781
1782    // --- LayoutManager2 methods --------------------------------
1783

1784    /**
1785     * Adds the specified component with the specified name to
1786     * the layout.
1787     * @param name the component name
1788     * @param comp the component to be added
1789     */

1790    public void addLayoutComponent(String JavaDoc name, Component comp) {
1791        // not supported
1792
}
1793
1794    /**
1795     * Removes the specified component from the layout.
1796     * @param comp the component to be removed
1797     */

1798    public void removeLayoutComponent(Component comp) {
1799        if (constraints != null) {
1800        // remove the constraint record
1801
constraints.remove(comp);
1802        }
1803    }
1804
1805    /**
1806     * Calculates the preferred size dimensions for the specified
1807     * panel given the components in the specified parent container.
1808     * @param parent the component to be laid out
1809     *
1810     * @see #minimumLayoutSize
1811     */

1812    public Dimension preferredLayoutSize(Container parent) {
1813        // should not be called (JComponent uses UI instead)
1814
return null;
1815    }
1816
1817    /**
1818     * Calculates the minimum size dimensions for the specified
1819     * panel given the components in the specified parent container.
1820     * @param parent the component to be laid out
1821     * @see #preferredLayoutSize
1822     */

1823    public Dimension minimumLayoutSize(Container parent) {
1824        // should not be called (JComponent uses UI instead)
1825
return null;
1826    }
1827
1828    /**
1829     * Lays out the container in the specified panel. This is
1830     * implemented to position all components that were added
1831     * with a View object as a constraint. The current allocation
1832     * of the associated View is used as the location of the
1833     * component.
1834     * <p>
1835     * A read-lock is acquired on the document to prevent the
1836     * view tree from being modified while the layout process
1837     * is active.
1838     *
1839     * @param parent the component which needs to be laid out
1840     */

1841    public void layoutContainer(Container parent) {
1842        if ((constraints != null) && (! constraints.isEmpty())) {
1843        Rectangle alloc = getVisibleEditorRect();
1844        if (alloc != null) {
1845            Document doc = editor.getDocument();
1846            if (doc instanceof AbstractDocument) {
1847            ((AbstractDocument)doc).readLock();
1848            }
1849            try {
1850            rootView.setSize(alloc.width, alloc.height);
1851            Enumeration components = constraints.keys();
1852            while (components.hasMoreElements()) {
1853                Component comp = (Component) components.nextElement();
1854                View v = (View) constraints.get(comp);
1855                Shape ca = calculateViewPosition(alloc, v);
1856                if (ca != null) {
1857                Rectangle compAlloc = (ca instanceof Rectangle) ?
1858                    (Rectangle) ca : ca.getBounds();
1859                comp.setBounds(compAlloc);
1860                }
1861            }
1862            } finally {
1863            if (doc instanceof AbstractDocument) {
1864                ((AbstractDocument)doc).readUnlock();
1865            }
1866            }
1867        }
1868        }
1869    }
1870
1871    /**
1872     * Find the Shape representing the given view.
1873     */

1874    Shape calculateViewPosition(Shape alloc, View v) {
1875        int pos = v.getStartOffset();
1876        View child = null;
1877        for (View parent = rootView; (parent != null) && (parent != v); parent = child) {
1878        int index = parent.getViewIndex(pos, Position.Bias.Forward);
1879        alloc = parent.getChildAllocation(index, alloc);
1880        child = parent.getView(index);
1881        }
1882        return (child != null) ? alloc : null;
1883    }
1884
1885    /**
1886     * Adds the specified component to the layout, using the specified
1887     * constraint object. We only store those components that were added
1888     * with a constraint that is of type View.
1889     *
1890     * @param comp the component to be added
1891     * @param constraint where/how the component is added to the layout.
1892     */

1893    public void addLayoutComponent(Component comp, Object JavaDoc constraint) {
1894        if (constraint instanceof View) {
1895        if (constraints == null) {
1896            constraints = new Hashtable(7);
1897        }
1898        constraints.put(comp, constraint);
1899        }
1900    }
1901
1902    /**
1903     * Returns the maximum size of this component.
1904     * @see java.awt.Component#getMinimumSize()
1905     * @see java.awt.Component#getPreferredSize()
1906     * @see LayoutManager
1907     */

1908        public Dimension maximumLayoutSize(Container target) {
1909        // should not be called (JComponent uses UI instead)
1910
return null;
1911    }
1912
1913    /**
1914     * Returns the alignment along the x axis. This specifies how
1915     * the component would like to be aligned relative to other
1916     * components. The value should be a number between 0 and 1
1917     * where 0 represents alignment along the origin, 1 is aligned
1918     * the furthest away from the origin, 0.5 is centered, etc.
1919     */

1920        public float getLayoutAlignmentX(Container target) {
1921        return 0.5f;
1922    }
1923
1924    /**
1925     * Returns the alignment along the y axis. This specifies how
1926     * the component would like to be aligned relative to other
1927     * components. The value should be a number between 0 and 1
1928     * where 0 represents alignment along the origin, 1 is aligned
1929     * the furthest away from the origin, 0.5 is centered, etc.
1930     */

1931        public float getLayoutAlignmentY(Container target) {
1932        return 0.5f;
1933    }
1934
1935    /**
1936     * Invalidates the layout, indicating that if the layout manager
1937     * has cached information it should be discarded.
1938     */

1939        public void invalidateLayout(Container target) {
1940    }
1941
1942    /**
1943     * The "layout constraints" for the LayoutManager2 implementation.
1944     * These are View objects for those components that are represented
1945     * by a View in the View tree.
1946     */

1947    private Hashtable constraints;
1948
1949    private boolean i18nView = false;
1950    }
1951
1952    /**
1953     * Wrapper for text actions to return isEnabled false in case editor is non editable
1954     */

1955    class TextActionWrapper extends TextAction {
1956    public TextActionWrapper(TextAction action) {
1957        super((String JavaDoc)action.getValue(Action.NAME));
1958        this.action = action;
1959    }
1960    /**
1961         * The operation to perform when this action is triggered.
1962         *
1963         * @param e the action event
1964         */

1965        public void actionPerformed(ActionEvent e) {
1966        action.actionPerformed(e);
1967    }
1968    public boolean isEnabled() {
1969        return (editor == null || editor.isEditable()) ? action.isEnabled() : false;
1970    }
1971    TextAction action = null;
1972    }
1973
1974
1975    /**
1976     * Registered in the ActionMap.
1977     */

1978    class FocusAction extends AbstractAction {
1979
1980        public void actionPerformed(ActionEvent e) {
1981        editor.requestFocus();
1982        }
1983
1984        public boolean isEnabled() {
1985            return editor.isEditable();
1986        }
1987    }
1988
1989    private static DragListener getDragListener() {
1990        synchronized(DragListener.class) {
1991            DragListener listener =
1992                (DragListener)AppContext.getAppContext().
1993                    get(DragListener.class);
1994
1995            if (listener == null) {
1996                listener = new DragListener();
1997                AppContext.getAppContext().put(DragListener.class, listener);
1998            }
1999
2000            return listener;
2001        }
2002    }
2003
2004    /**
2005     * Listens for mouse events for the purposes of detecting drag gestures.
2006     * BasicTextUI will maintain one of these per AppContext.
2007     */

2008    static class DragListener extends MouseInputAdapter
2009                              implements BeforeDrag {
2010
2011        private boolean dragStarted;
2012
2013        public void dragStarting(MouseEvent me) {
2014            dragStarted = true;
2015        }
2016
2017        public void mousePressed(MouseEvent e) {
2018            JTextComponent c = (JTextComponent)e.getSource();
2019            if (c.getDragEnabled()) {
2020                dragStarted = false;
2021                if (isDragPossible(e) && DragRecognitionSupport.mousePressed(e)) {
2022                    e.consume();
2023                }
2024            }
2025        }
2026
2027        public void mouseReleased(MouseEvent e) {
2028            JTextComponent c = (JTextComponent)e.getSource();
2029            if (c.getDragEnabled()) {
2030                if (dragStarted) {
2031                    e.consume();
2032                }
2033
2034                DragRecognitionSupport.mouseReleased(e);
2035            }
2036        }
2037
2038        public void mouseDragged(MouseEvent e) {
2039            JTextComponent c = (JTextComponent)e.getSource();
2040            if (c.getDragEnabled()) {
2041                if (dragStarted || DragRecognitionSupport.mouseDragged(e, this)) {
2042                    e.consume();
2043                }
2044            }
2045        }
2046
2047        /**
2048         * Determines if the following are true:
2049         * <ul>
2050         * <li>the component is enabled
2051         * <li>the press event is located over a selection
2052         * </ul>
2053         */

2054        protected boolean isDragPossible(MouseEvent e) {
2055            JTextComponent c = (JTextComponent)e.getSource();
2056            if (c.isEnabled()) {
2057                Caret caret = c.getCaret();
2058                int dot = caret.getDot();
2059                int mark = caret.getMark();
2060                if (dot != mark) {
2061                    Point p = new Point(e.getX(), e.getY());
2062                    int pos = c.viewToModel(p);
2063
2064                    int p0 = Math.min(dot, mark);
2065                    int p1 = Math.max(dot, mark);
2066                    if ((pos >= p0) && (pos < p1)) {
2067                        return true;
2068                    }
2069                }
2070            }
2071            return false;
2072        }
2073    }
2074
2075    /**
2076     * A DropTargetListener to extend the default Swing handling of drop operations
2077     * by moving the caret to the nearest location to the mouse pointer.
2078     */

2079    static class TextDropTargetListener extends BasicDropTargetListener JavaDoc {
2080
2081    /**
2082     * called to save the state of a component in case it needs to
2083     * be restored because a drop is not performed.
2084     */

2085        protected void saveComponentState(JComponent comp) {
2086        JTextComponent c = (JTextComponent) comp;
2087        Caret caret = c.getCaret();
2088        dot = caret.getDot();
2089        mark = caret.getMark();
2090        visible = caret instanceof DefaultCaret ?
2091                          ((DefaultCaret)caret).isActive() :
2092                          caret.isVisible();
2093        caret.setVisible(true);
2094    }
2095
2096    /**
2097     * called to restore the state of a component
2098     * because a drop was not performed.
2099     */

2100        protected void restoreComponentState(JComponent comp) {
2101        JTextComponent c = (JTextComponent) comp;
2102        Caret caret = c.getCaret();
2103        caret.setDot(mark);
2104        caret.moveDot(dot);
2105        caret.setVisible(visible);
2106    }
2107
2108        /**
2109         * called to restore the state of a component
2110         * because a drop was performed.
2111         */

2112        protected void restoreComponentStateForDrop(JComponent comp) {
2113            JTextComponent c = (JTextComponent) comp;
2114            Caret caret = c.getCaret();
2115            caret.setVisible(visible);
2116        }
2117
2118    /**
2119     * called to set the insertion location to match the current
2120     * mouse pointer coordinates.
2121     */

2122        protected void updateInsertionLocation(JComponent comp, Point p) {
2123        JTextComponent c = (JTextComponent) comp;
2124        c.setCaretPosition(c.viewToModel(p));
2125    }
2126
2127    int dot;
2128    int mark;
2129    boolean visible;
2130    }
2131
2132    static class TextTransferHandler extends TransferHandler implements UIResource JavaDoc {
2133        
2134        private JTextComponent exportComp;
2135        private boolean shouldRemove;
2136        private int p0;
2137        private int p1;
2138        
2139        /**
2140         * Try to find a flavor that can be used to import a Transferable.
2141         * The set of usable flavors are tried in the following order:
2142         * <ol>
2143         * <li>First, an attempt is made to find a flavor matching the content type
2144         * of the EditorKit for the component.
2145         * <li>Second, an attempt to find a text/plain flavor is made.
2146         * <li>Third, an attempt to find a flavor representing a String reference
2147         * in the same VM is made.
2148         * <li>Lastly, DataFlavor.stringFlavor is searched for.
2149         * </ol>
2150         */

2151    protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
2152            DataFlavor plainFlavor = null;
2153            DataFlavor refFlavor = null;
2154            DataFlavor stringFlavor = null;
2155            
2156            if (c instanceof JEditorPane) {
2157                for (int i = 0; i < flavors.length; i++) {
2158                    String JavaDoc mime = flavors[i].getMimeType();
2159                    if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) {
2160                        return flavors[i];
2161                    } else if (plainFlavor == null && mime.startsWith("text/plain")) {
2162                        plainFlavor = flavors[i];
2163                    } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2164                                                 && flavors[i].getRepresentationClass() == java.lang.String JavaDoc.class) {
2165                        refFlavor = flavors[i];
2166                    } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2167                        stringFlavor = flavors[i];
2168                    }
2169                }
2170                if (plainFlavor != null) {
2171                    return plainFlavor;
2172                } else if (refFlavor != null) {
2173                    return refFlavor;
2174                } else if (stringFlavor != null) {
2175                    return stringFlavor;
2176                }
2177                return null;
2178            }
2179            
2180            
2181            for (int i = 0; i < flavors.length; i++) {
2182                String JavaDoc mime = flavors[i].getMimeType();
2183                if (mime.startsWith("text/plain")) {
2184                    return flavors[i];
2185                } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2186                                             && flavors[i].getRepresentationClass() == java.lang.String JavaDoc.class) {
2187                    refFlavor = flavors[i];
2188                } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2189                    stringFlavor = flavors[i];
2190                }
2191            }
2192            if (refFlavor != null) {
2193                return refFlavor;
2194            } else if (stringFlavor != null) {
2195                return stringFlavor;
2196            }
2197            return null;
2198    }
2199
2200    /**
2201     * Import the given stream data into the text component.
2202     */

2203        protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)
2204                                               throws BadLocationException, IOException {
2205            if (useRead) {
2206                int startPosition = c.getSelectionStart();
2207                int endPosition = c.getSelectionEnd();
2208                int length = endPosition - startPosition;
2209                EditorKit kit = c.getUI().getEditorKit(c);
2210                Document doc = c.getDocument();
2211                if (length > 0) {
2212                    doc.remove(startPosition, length);
2213                }
2214                kit.read(in, doc, startPosition);
2215            } else {
2216                char[] buff = new char[1024];
2217                int nch;
2218                boolean lastWasCR = false;
2219                int last;
2220                StringBuffer JavaDoc sbuff = null;
2221                
2222                // Read in a block at a time, mapping \r\n to \n, as well as single
2223
// \r to \n.
2224
while ((nch = in.read(buff, 0, buff.length)) != -1) {
2225                    if (sbuff == null) {
2226                        sbuff = new StringBuffer JavaDoc(nch);
2227                    }
2228                    last = 0;
2229                    for(int counter = 0; counter < nch; counter++) {
2230                        switch(buff[counter]) {
2231                        case '\r':
2232                            if (lastWasCR) {
2233                                if (counter == 0) {
2234                                    sbuff.append('\n');
2235                                } else {
2236                                    buff[counter - 1] = '\n';
2237                                }
2238                            } else {
2239                                lastWasCR = true;
2240                            }
2241                            break;
2242                        case '\n':
2243                            if (lastWasCR) {
2244                                if (counter > (last + 1)) {
2245                                    sbuff.append(buff, last, counter - last - 1);
2246                                }
2247                                // else nothing to do, can skip \r, next write will
2248
// write \n
2249
lastWasCR = false;
2250                                last = counter;
2251                            }
2252                            break;
2253                        default:
2254                            if (lastWasCR) {
2255                                if (counter == 0) {
2256                                    sbuff.append('\n');
2257                                } else {
2258                                    buff[counter - 1] = '\n';
2259                                }
2260                                lastWasCR = false;
2261                            }
2262                            break;
2263                        }
2264                    }
2265                    if (last < nch) {
2266                        if (lastWasCR) {
2267                            if (last < (nch - 1)) {
2268                                sbuff.append(buff, last, nch - last - 1);
2269                            }
2270                        } else {
2271                            sbuff.append(buff, last, nch - last);
2272                        }
2273                    }
2274                }
2275                if (lastWasCR) {
2276                    sbuff.append('\n');
2277                }
2278                c.replaceSelection(sbuff != null ? sbuff.toString() : "");
2279            }
2280    }
2281
2282    // --- TransferHandler methods ------------------------------------
2283

2284    /**
2285     * This is the type of transfer actions supported by the source. Some models are
2286     * not mutable, so a transfer operation of COPY only should
2287     * be advertised in that case.
2288     *
2289     * @param c The component holding the data to be transfered. This
2290     * argument is provided to enable sharing of TransferHandlers by
2291     * multiple components.
2292     * @return This is implemented to return NONE if the component is a JPasswordField
2293     * since exporting data via user gestures is not allowed. If the text component is
2294     * editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed.
2295     */

2296        public int getSourceActions(JComponent c) {
2297            if (c instanceof JPasswordField &&
2298                c.getClientProperty("JPasswordField.cutCopyAllowed") !=
2299                Boolean.TRUE) {
2300                return NONE;
2301            }
2302
2303            return ((JTextComponent)c).isEditable() ? COPY_OR_MOVE : COPY;
2304    }
2305
2306    /**
2307     * Create a Transferable to use as the source for a data transfer.
2308     *
2309     * @param comp The component holding the data to be transfered. This
2310     * argument is provided to enable sharing of TransferHandlers by
2311     * multiple components.
2312     * @return The representation of the data to be transfered.
2313     *
2314     */

2315        protected Transferable createTransferable(JComponent comp) {
2316            exportComp = (JTextComponent)comp;
2317            shouldRemove = true;
2318            p0 = exportComp.getSelectionStart();
2319            p1 = exportComp.getSelectionEnd();
2320            return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;
2321    }
2322
2323    /**
2324     * This method is called after data has been exported. This method should remove
2325     * the data that was transfered if the action was MOVE.
2326     *
2327     * @param source The component that was the source of the data.
2328     * @param data The data that was transferred or possibly null
2329         * if the action is <code>NONE</code>.
2330     * @param action The actual action that was performed.
2331     */

2332        protected void exportDone(JComponent source, Transferable data, int action) {
2333            // only remove the text if shouldRemove has not been set to
2334
// false by importData and only if the action is a move
2335
if (shouldRemove && action == MOVE) {
2336        TextTransferable t = (TextTransferable)data;
2337        t.removeText();
2338        }
2339            
2340            exportComp = null;
2341    }
2342
2343    /**
2344     * This method causes a transfer to a component from a clipboard or a
2345     * DND drop operation. The Transferable represents the data to be
2346     * imported into the component.
2347     *
2348     * @param comp The component to receive the transfer. This
2349     * argument is provided to enable sharing of TransferHandlers by
2350     * multiple components.
2351     * @param t The data to import
2352     * @return true if the data was inserted into the component, false otherwise.
2353     */

2354        public boolean importData(JComponent comp, Transferable t) {
2355            JTextComponent c = (JTextComponent)comp;
2356
2357        // if we are importing to the same component that we exported from
2358
// then don't actually do anything if the drop location is inside
2359
// the drag location and set shouldRemove to false so that exportDone
2360
// knows not to remove any data
2361
if (c == exportComp && c.getCaretPosition() >= p0 && c.getCaretPosition() <= p1) {
2362                shouldRemove = false;
2363                return true;
2364            }
2365
2366        boolean imported = false;
2367        DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
2368        if (importFlavor != null) {
2369        try {
2370                    boolean useRead = false;
2371                    if (comp instanceof JEditorPane) {
2372                        JEditorPane ep = (JEditorPane)comp;
2373                        if (!ep.getContentType().startsWith("text/plain") &&
2374                                importFlavor.getMimeType().startsWith(ep.getContentType())) {
2375                            useRead = true;
2376                        }
2377                    }
2378            InputContext JavaDoc ic = c.getInputContext();
2379            if (ic != null) {
2380            ic.endComposition();
2381            }
2382                    Reader r = importFlavor.getReaderForText(t);
2383                    handleReaderImport(r, c, useRead);
2384                    imported = true;
2385        } catch (UnsupportedFlavorException ufe) {
2386        } catch (BadLocationException ble) {
2387        } catch (IOException ioe) {
2388        }
2389        }
2390        return imported;
2391    }
2392
2393    /**
2394     * This method indicates if a component would accept an import of the given
2395     * set of data flavors prior to actually attempting to import it.
2396     *
2397     * @param comp The component to receive the transfer. This
2398     * argument is provided to enable sharing of TransferHandlers by
2399     * multiple components.
2400     * @param flavors The data formats available
2401     * @return true if the data can be inserted into the component, false otherwise.
2402     */

2403        public boolean canImport(JComponent comp, DataFlavor[] flavors) {
2404            JTextComponent c = (JTextComponent)comp;
2405            if (!(c.isEditable() && c.isEnabled())) {
2406                return false;
2407            }
2408            return (getImportFlavor(flavors, c) != null);
2409    }
2410
2411        /**
2412     * A possible implementation of the Transferable interface
2413     * for text components. For a JEditorPane with a rich set
2414     * of EditorKit implementations, conversions could be made
2415     * giving a wider set of formats. This is implemented to
2416     * offer up only the active content type and text/plain
2417     * (if that is not the active format) since that can be
2418     * extracted from other formats.
2419     */

2420    static class TextTransferable extends BasicTransferable JavaDoc {
2421
2422        TextTransferable(JTextComponent c, int start, int end) {
2423        super(null, null);
2424                
2425                this.c = c;
2426                
2427        Document doc = c.getDocument();
2428
2429        try {
2430            p0 = doc.createPosition(start);
2431            p1 = doc.createPosition(end);
2432
2433                    plainData = c.getSelectedText();
2434
2435                    if (c instanceof JEditorPane) {
2436                        JEditorPane ep = (JEditorPane)c;
2437                        
2438                        mimeType = ep.getContentType();
2439
2440                        if (mimeType.startsWith("text/plain")) {
2441                            return;
2442                        }
2443
2444                        StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());
2445                        ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());
2446                        
2447                        if (mimeType.startsWith("text/html")) {
2448                            htmlData = sw.toString();
2449                        } else {
2450                            richText = sw.toString();
2451                        }
2452                    }
2453        } catch (BadLocationException ble) {
2454        } catch (IOException ioe) {
2455                }
2456        }
2457
2458        void removeText() {
2459        if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
2460            try {
2461            Document doc = c.getDocument();
2462            doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
2463            } catch (BadLocationException e) {
2464            }
2465        }
2466        }
2467
2468        // ---- EditorKit other than plain or HTML text -----------------------
2469

2470        /**
2471         * If the EditorKit is not for text/plain or text/html, that format
2472         * is supported through the "richer flavors" part of BasicTransferable.
2473         */

2474            protected DataFlavor[] getRicherFlavors() {
2475        if (richText == null) {
2476            return null;
2477        }
2478
2479        try {
2480            DataFlavor[] flavors = new DataFlavor[3];
2481                    flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String");
2482                    flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader");
2483                    flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode");
2484            return flavors;
2485        } catch (ClassNotFoundException JavaDoc cle) {
2486            // fall through to unsupported (should not happen)
2487
}
2488
2489        return null;
2490        }
2491
2492        /**
2493         * The only richer format supported is the file list flavor
2494         */

2495            protected Object JavaDoc getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
2496        if (richText == null) {
2497            return null;
2498        }
2499
2500        if (String JavaDoc.class.equals(flavor.getRepresentationClass())) {
2501            return richText;
2502        } else if (Reader.class.equals(flavor.getRepresentationClass())) {
2503            return new StringReader(richText);
2504        } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
2505            return new StringBufferInputStream(richText);
2506        }
2507                throw new UnsupportedFlavorException(flavor);
2508        }
2509
2510        Position p0;
2511        Position p1;
2512            String JavaDoc mimeType;
2513            String JavaDoc richText;
2514            JTextComponent c;
2515    }
2516
2517    }
2518
2519}
2520
2521
Popular Tags