1/*
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.plaf.basic;
26
27import java.util.*;
28import java.awt.*;
29import java.awt.event.*;
30import java.awt.datatransfer.*;
31import java.awt.geom.Point2D;
32import java.awt.geom.Rectangle2D;
33import java.awt.im.InputContext;
34import java.beans.*;
35import java.io.*;
36import javax.swing.*;
37import javax.swing.plaf.*;
38import javax.swing.text.*;
39import javax.swing.event.*;
40import javax.swing.border.Border;
41import javax.swing.plaf.UIResource;
42import javax.swing.plaf.synth.SynthUI;
43import sun.swing.DefaultLookup;
44import sun.awt.AppContext;
45import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
46
47/**
48 * <p>
49 * Basis of a text components look-and-feel.  This provides the
50 * basic editor view and controller services that may be useful
51 * when creating a look-and-feel for an extension of
52 * <code>JTextComponent</code>.
53 * <p>
54 * Most state is held in the associated <code>JTextComponent</code>
55 * as bound properties, and the UI installs default values for the
56 * various properties.  This default will install something for
57 * all of the properties.  Typically, a LAF implementation will
58 * do more however.  At a minimum, a LAF would generally install
59 * key bindings.
60 * <p>
61 * This class also provides some concurrency support if the
62 * <code>Document</code> associated with the JTextComponent is a subclass of
63 * <code>AbstractDocument</code>.  Access to the View (or View hierarchy) is
64 * serialized between any thread mutating the model and the Swing
65 * event thread (which is expected to render, do model/view coordinate
66 * translation, etc).  <em>Any access to the root view should first
67 * acquire a read-lock on the AbstractDocument and release that lock
68 * in a finally block.</em>
69 * <p>
70 * An important method to define is the {@link #getPropertyPrefix} method
71 * which is used as the basis of the keys used to fetch defaults
72 * from the UIManager.  The string should reflect the type of
73 * TextUI (eg. TextField, TextArea, etc) without the particular
74 * LAF part of the name (eg Metal, Motif, etc).
75 * <p>
76 * To build a view of the model, one of the following strategies
77 * can be employed.
78 * <ol>
79 * <li>
80 * One strategy is to simply redefine the
81 * ViewFactory interface in the UI.  By default, this UI itself acts
82 * as the factory for View implementations.  This is useful
83 * for simple factories.  To do this reimplement the
84 * {@link #create} method.
85 * <li>
86 * A common strategy for creating more complex types of documents
87 * is to have the EditorKit implementation return a factory.  Since
88 * the EditorKit ties all of the pieces necessary to maintain a type
89 * of document, the factory is typically an important part of that
90 * and should be produced by the EditorKit implementation.
91 * </ol>
92 * <p>
93 * <strong>Warning:</strong>
94 * Serialized objects of this class will not be compatible with
95 * future Swing releases. The current serialization support is
96 * appropriate for short term storage or RMI between applications running
97 * the same version of Swing.  As of 1.4, support for long term storage
98 * of all JavaBeans&trade;
99 * has been added to the <code>java.beans</code> package.
100 * Please see {@link java.beans.XMLEncoder}.
101 *
102 * @author Timothy Prinzing
103 * @author Shannon Hickey (drag and drop)
104 */
105@SuppressWarnings("serial") // Same-version serialization only
106public abstract class BasicTextUI extends TextUI implements ViewFactory {
107    private static final int DEFAULT_CARET_MARGIN = 1;
108
109    /**
110     * Creates a new UI.
111     */
112    public BasicTextUI() {
113        painted = false;
114    }
115
116    /**
117     * Creates the object to use for a caret.  By default an
118     * instance of BasicCaret is created.  This method
119     * can be redefined to provide something else that implements
120     * the InputPosition interface or a subclass of JCaret.
121     *
122     * @return the caret object
123     */
124    protected Caret createCaret() {
125        return new BasicCaret();
126    }
127
128    /**
129     * Creates the object to use for adding highlights.  By default
130     * an instance of BasicHighlighter is created.  This method
131     * can be redefined to provide something else that implements
132     * the Highlighter interface or a subclass of DefaultHighlighter.
133     *
134     * @return the highlighter
135     */
136    protected Highlighter createHighlighter() {
137        return new BasicHighlighter();
138    }
139
140    /**
141     * Fetches the name of the keymap that will be installed/used
142     * by default for this UI. This is implemented to create a
143     * name based upon the classname.  The name is the name
144     * of the class with the package prefix removed.
145     *
146     * @return the name
147     */
148    protected String getKeymapName() {
149        String nm = getClass().getName();
150        int index = nm.lastIndexOf('.');
151        if (index >= 0) {
152            nm = nm.substring(index+1, nm.length());
153        }
154        return nm;
155    }
156
157    /**
158     * Creates the keymap to use for the text component, and installs
159     * any necessary bindings into it.  By default, the keymap is
160     * shared between all instances of this type of TextUI. The
161     * keymap has the name defined by the getKeymapName method.  If the
162     * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
163     * <p>
164     * The set of bindings used to create the keymap is fetched
165     * from the UIManager using a key formed by combining the
166     * {@link #getPropertyPrefix} method
167     * and the string <code>.keyBindings</code>.  The type is expected
168     * to be <code>JTextComponent.KeyBinding[]</code>.
169     *
170     * @return the keymap
171     * @see #getKeymapName
172     * @see javax.swing.text.JTextComponent
173     */
174    protected Keymap createKeymap() {
175        String nm = getKeymapName();
176        Keymap map = JTextComponent.getKeymap(nm);
177        if (map == null) {
178            Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
179            map = JTextComponent.addKeymap(nm, parent);
180            String prefix = getPropertyPrefix();
181            Object o = DefaultLookup.get(editor, this,
182                prefix + ".keyBindings");
183            if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
184                JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
185                JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
186            }
187        }
188        return map;
189    }
190
191    /**
192     * This method gets called when a bound property is changed
193     * on the associated JTextComponent.  This is a hook
194     * which UI implementations may change to reflect how the
195     * UI displays bound properties of JTextComponent subclasses.
196     * This is implemented to do nothing (i.e. the response to
197     * properties in JTextComponent itself are handled prior
198     * to calling this method).
199     *
200     * This implementation updates the background of the text
201     * component if the editable and/or enabled state changes.
202     *
203     * @param evt the property change event
204     */
205    protected void propertyChange(PropertyChangeEvent evt) {
206        if (evt.getPropertyName().equals("editable") ||
207                evt.getPropertyName().equals("enabled")) {
208
209            updateBackground((JTextComponent)evt.getSource());
210        } else if (evt.getPropertyName().equals("caretWidth")) {
211            Object value = evt.getNewValue();
212            if (value instanceof Number) {
213                int width = ((Number) value).intValue();
214                if (width >= 0) caretMargin = width;
215            }
216        }
217    }
218
219    /**
220     * Updates the background of the text component based on whether the
221     * text component is editable and/or enabled.
222     *
223     * @param c the JTextComponent that needs its background color updated
224     */
225    private void updateBackground(JTextComponent c) {
226        // This is a temporary workaround.
227        // This code does not correctly deal with Synth (Synth doesn't use
228        // properties like this), nor does it deal with the situation where
229        // the developer grabs the color from a JLabel and sets it as
230        // the background for a JTextArea in all look and feels. The problem
231        // scenario results if the Color obtained for the Label and TextArea
232        // is ==, which is the case for the windows look and feel.
233        // Until an appropriate solution is found, the code is being
234        // reverted to what it was before the original fix.
235        if (this instanceof SynthUI || (c instanceof JTextArea)) {
236            return;
237        }
238        Color background = c.getBackground();
239        if (background instanceof UIResource) {
240            String prefix = getPropertyPrefix();
241
242            Color disabledBG =
243                DefaultLookup.getColor(c, this, prefix + ".disabledBackground", null);
244            Color inactiveBG =
245                DefaultLookup.getColor(c, this, prefix + ".inactiveBackground", null);
246            Color bg =
247                DefaultLookup.getColor(c, this, prefix + ".background", null);
248
249            /* In an ideal situation, the following check would not be necessary
250             * and we would replace the color any time the previous color was a
251             * UIResouce. However, it turns out that there is existing code that
252             * uses the following inadvisable pattern to turn a text area into
253             * what appears to be a multi-line label:
254             *
255             * JLabel label = new JLabel();
256             * JTextArea area = new JTextArea();
257             * area.setBackground(label.getBackground());
258             * area.setEditable(false);
259             *
260             * JLabel's default background is a UIResource. As such, just
261             * checking for UIResource would have us always changing the
262             * background away from what the developer wanted.
263             *
264             * Therefore, for JTextArea/JEditorPane, we'll additionally check
265             * that the color we're about to replace matches one that was
266             * installed by us from the UIDefaults.
267             */
268            if ((c instanceof JTextArea || c instanceof JEditorPane)
269                    && background != disabledBG
270                    && background != inactiveBG
271                    && background != bg) {
272
273                return;
274            }
275
276            Color newColor = null;
277            if (!c.isEnabled()) {
278                newColor = disabledBG;
279            }
280            if (newColor == null && !c.isEditable()) {
281                newColor = inactiveBG;
282            }
283            if (newColor == null) {
284                newColor = bg;
285            }
286            if (newColor != null && newColor != background) {
287                c.setBackground(newColor);
288            }
289        }
290    }
291
292    /**
293     * Gets the name used as a key to look up properties through the
294     * UIManager.  This is used as a prefix to all the standard
295     * text properties.
296     *
297     * @return the name
298     */
299    protected abstract String getPropertyPrefix();
300
301    /**
302     * Initializes component properties, such as font, foreground,
303     * background, caret color, selection color, selected text color,
304     * disabled text color, and border color.  The font, foreground, and
305     * background properties are only set if their current value is either null
306     * or a UIResource, other properties are set if the current
307     * value is null.
308     *
309     * @see #uninstallDefaults
310     * @see #installUI
311     */
312    protected void installDefaults()
313    {
314        String prefix = getPropertyPrefix();
315        Font f = editor.getFont();
316        if ((f == null) || (f instanceof UIResource)) {
317            editor.setFont(UIManager.getFont(prefix + ".font"));
318        }
319
320        Color bg = editor.getBackground();
321        if ((bg == null) || (bg instanceof UIResource)) {
322            editor.setBackground(UIManager.getColor(prefix + ".background"));
323        }
324
325        Color fg = editor.getForeground();
326        if ((fg == null) || (fg instanceof UIResource)) {
327            editor.setForeground(UIManager.getColor(prefix + ".foreground"));
328        }
329
330        Color color = editor.getCaretColor();
331        if ((color == null) || (color instanceof UIResource)) {
332            editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
333        }
334
335        Color s = editor.getSelectionColor();
336        if ((s == null) || (s instanceof UIResource)) {
337            editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
338        }
339
340        Color sfg = editor.getSelectedTextColor();
341        if ((sfg == null) || (sfg instanceof UIResource)) {
342            editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
343        }
344
345        Color dfg = editor.getDisabledTextColor();
346        if ((dfg == null) || (dfg instanceof UIResource)) {
347            editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
348        }
349
350        Border b = editor.getBorder();
351        if ((b == null) || (b instanceof UIResource)) {
352            editor.setBorder(UIManager.getBorder(prefix + ".border"));
353        }
354
355        Insets margin = editor.getMargin();
356        if (margin == null || margin instanceof UIResource) {
357            editor.setMargin(UIManager.getInsets(prefix + ".margin"));
358        }
359
360        updateCursor();
361    }
362
363    private void installDefaults2() {
364        editor.addMouseListener(dragListener);
365        editor.addMouseMotionListener(dragListener);
366
367        String prefix = getPropertyPrefix();
368
369        Caret caret = editor.getCaret();
370        if (caret == null || caret instanceof UIResource) {
371            caret = createCaret();
372            editor.setCaret(caret);
373
374            int rate = DefaultLookup.getInt(getComponent(), this, prefix + ".caretBlinkRate", 500);
375            caret.setBlinkRate(rate);
376        }
377
378        Highlighter highlighter = editor.getHighlighter();
379        if (highlighter == null || highlighter instanceof UIResource) {
380            editor.setHighlighter(createHighlighter());
381        }
382
383        TransferHandler th = editor.getTransferHandler();
384        if (th == null || th instanceof UIResource) {
385            editor.setTransferHandler(getTransferHandler());
386        }
387    }
388
389    /**
390     * Sets the component properties that have not been explicitly overridden
391     * to {@code null}.  A property is considered overridden if its current
392     * value is not a {@code UIResource}.
393     *
394     * @see #installDefaults
395     * @see #uninstallUI
396     */
397    protected void uninstallDefaults()
398    {
399        editor.removeMouseListener(dragListener);
400        editor.removeMouseMotionListener(dragListener);
401
402        if (editor.getCaretColor() instanceof UIResource) {
403            editor.setCaretColor(null);
404        }
405
406        if (editor.getSelectionColor() instanceof UIResource) {
407            editor.setSelectionColor(null);
408        }
409
410        if (editor.getDisabledTextColor() instanceof UIResource) {
411            editor.setDisabledTextColor(null);
412        }
413
414        if (editor.getSelectedTextColor() instanceof UIResource) {
415            editor.setSelectedTextColor(null);
416        }
417
418        if (editor.getBorder() instanceof UIResource) {
419            editor.setBorder(null);
420        }
421
422        if (editor.getMargin() instanceof UIResource) {
423            editor.setMargin(null);
424        }
425
426        if (editor.getCaret() instanceof UIResource) {
427            editor.setCaret(null);
428        }
429
430        if (editor.getHighlighter() instanceof UIResource) {
431            editor.setHighlighter(null);
432        }
433
434        if (editor.getTransferHandler() instanceof UIResource) {
435            editor.setTransferHandler(null);
436        }
437
438        if (editor.getCursor() instanceof UIResource) {
439            editor.setCursor(null);
440        }
441    }
442
443    /**
444     * Installs listeners for the UI.
445     */
446    protected void installListeners() {
447    }
448
449    /**
450     * Uninstalls listeners for the UI.
451     */
452    protected void uninstallListeners() {
453    }
454
455    /**
456     * Registers keyboard actions.
457     */
458    protected void installKeyboardActions() {
459        // backward compatibility support... keymaps for the UI
460        // are now installed in the more friendly input map.
461        editor.setKeymap(createKeymap());
462
463        InputMap km = getInputMap();
464        if (km != null) {
465            SwingUtilities.replaceUIInputMap(editor, JComponent.WHEN_FOCUSED,
466                                             km);
467        }
468
469        ActionMap map = getActionMap();
470        if (map != null) {
471            SwingUtilities.replaceUIActionMap(editor, map);
472        }
473
474        updateFocusAcceleratorBinding(false);
475    }
476
477    /**
478     * Get the InputMap to use for the UI.
479     */
480    InputMap getInputMap() {
481        InputMap map = new InputMapUIResource();
482
483        InputMap shared =
484            (InputMap)DefaultLookup.get(editor, this,
485            getPropertyPrefix() + ".focusInputMap");
486        if (shared != null) {
487            map.setParent(shared);
488        }
489        return map;
490    }
491
492    /**
493     * Invoked when the focus accelerator changes, this will update the
494     * key bindings as necessary.
495     */
496    void updateFocusAcceleratorBinding(boolean changed) {
497        char accelerator = editor.getFocusAccelerator();
498
499        if (changed || accelerator != '\0') {
500            InputMap km = SwingUtilities.getUIInputMap
501                        (editor, JComponent.WHEN_IN_FOCUSED_WINDOW);
502
503            if (km == null && accelerator != '\0') {
504                km = new ComponentInputMapUIResource(editor);
505                SwingUtilities.replaceUIInputMap(editor, JComponent.
506                                                 WHEN_IN_FOCUSED_WINDOW, km);
507                ActionMap am = getActionMap();
508                SwingUtilities.replaceUIActionMap(editor, am);
509            }
510            if (km != null) {
511                km.clear();
512                if (accelerator != '\0') {
513                    km.put(KeyStroke.getKeyStroke(accelerator, BasicLookAndFeel.getFocusAcceleratorKeyMask()), "requestFocus");
514                }
515            }
516        }
517    }
518
519
520    /**
521     * Invoked when editable property is changed.
522     *
523     * removing 'TAB' and 'SHIFT-TAB' from traversalKeysSet in case
524     * editor is editable
525     * adding 'TAB' and 'SHIFT-TAB' to traversalKeysSet in case
526     * editor is non editable
527     */
528    @SuppressWarnings("deprecation")
529    void updateFocusTraversalKeys() {
530        /*
531         * Fix for 4514331 Non-editable JTextArea and similar
532         * should allow Tab to keyboard - accessibility
533         */
534        EditorKit editorKit = getEditorKit(editor);
535        if ( editorKit != null
536             && editorKit instanceof DefaultEditorKit) {
537            Set<AWTKeyStroke> storedForwardTraversalKeys = editor.
538                getFocusTraversalKeys(KeyboardFocusManager.
539                                      FORWARD_TRAVERSAL_KEYS);
540            Set<AWTKeyStroke> storedBackwardTraversalKeys = editor.
541                getFocusTraversalKeys(KeyboardFocusManager.
542                                      BACKWARD_TRAVERSAL_KEYS);
543            Set<AWTKeyStroke> forwardTraversalKeys =
544                new HashSet<AWTKeyStroke>(storedForwardTraversalKeys);
545            Set<AWTKeyStroke> backwardTraversalKeys =
546                new HashSet<AWTKeyStroke>(storedBackwardTraversalKeys);
547            if (editor.isEditable()) {
548                forwardTraversalKeys.
549                    remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
550                backwardTraversalKeys.
551                    remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
552                                                  InputEvent.SHIFT_MASK));
553            } else {
554                forwardTraversalKeys.add(KeyStroke.
555                                         getKeyStroke(KeyEvent.VK_TAB, 0));
556                backwardTraversalKeys.
557                    add(KeyStroke.
558                        getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
559            }
560            LookAndFeel.installProperty(editor,
561                                        "focusTraversalKeysForward",
562                                         forwardTraversalKeys);
563            LookAndFeel.installProperty(editor,
564                                        "focusTraversalKeysBackward",
565                                         backwardTraversalKeys);
566        }
567
568    }
569
570    /**
571     * As needed updates cursor for the target editor.
572     */
573    private void updateCursor() {
574        if ((! editor.isCursorSet())
575               || editor.getCursor() instanceof UIResource) {
576            Cursor cursor = (editor.isEditable()) ? textCursor : null;
577            editor.setCursor(cursor);
578        }
579    }
580
581    /**
582     * Returns the <code>TransferHandler</code> that will be installed if
583     * their isn't one installed on the <code>JTextComponent</code>.
584     */
585    TransferHandler getTransferHandler() {
586        return defaultTransferHandler;
587    }
588
589    /**
590     * Fetch an action map to use.
591     */
592    ActionMap getActionMap() {
593        String mapName = getPropertyPrefix() + ".actionMap";
594        ActionMap map = (ActionMap)UIManager.get(mapName);
595
596        if (map == null) {
597            map = createActionMap();
598            if (map != null) {
599                UIManager.getLookAndFeelDefaults().put(mapName, map);
600            }
601        }
602        ActionMap componentMap = new ActionMapUIResource();
603        componentMap.put("requestFocus", new FocusAction());
604        /*
605         * fix for bug 4515750
606         * JTextField & non-editable JTextArea bind return key - default btn not accessible
607         *
608         * Wrap the return action so that it is only enabled when the
609         * component is editable. This allows the default button to be
610         * processed when the text component has focus and isn't editable.
611         *
612         */
613        if (getEditorKit(editor) instanceof DefaultEditorKit) {
614            if (map != null) {
615                Object obj = map.get(DefaultEditorKit.insertBreakAction);
616                if (obj != null
617                    && obj instanceof DefaultEditorKit.InsertBreakAction) {
618                    Action action =  new TextActionWrapper((TextAction)obj);
619                    componentMap.put(action.getValue(Action.NAME),action);
620                }
621            }
622        }
623        if (map != null) {
624            componentMap.setParent(map);
625        }
626        return componentMap;
627    }
628
629    /**
630     * Create a default action map.  This is basically the
631     * set of actions found exported by the component.
632     */
633    ActionMap createActionMap() {
634        ActionMap map = new ActionMapUIResource();
635        Action[] actions = editor.getActions();
636        //System.out.println("building map for UI: " + getPropertyPrefix());
637        int n = actions.length;
638        for (int i = 0; i < n; i++) {
639            Action a = actions[i];
640            map.put(a.getValue(Action.NAME), a);
641            //System.out.println("  " + a.getValue(Action.NAME));
642        }
643        map.put(TransferHandler.getCutAction().getValue(Action.NAME),
644                TransferHandler.getCutAction());
645        map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
646                TransferHandler.getCopyAction());
647        map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
648                TransferHandler.getPasteAction());
649        return map;
650    }
651
652    /**
653     * Unregisters keyboard actions.
654     */
655    protected void uninstallKeyboardActions() {
656        editor.setKeymap(null);
657        SwingUtilities.replaceUIInputMap(editor, JComponent.
658                                         WHEN_IN_FOCUSED_WINDOW, null);
659        SwingUtilities.replaceUIActionMap(editor, null);
660    }
661
662    /**
663     * Paints a background for the view.  This will only be
664     * called if isOpaque() on the associated component is
665     * true.  The default is to paint the background color
666     * of the component.
667     *
668     * @param g the graphics context
669     */
670    protected void paintBackground(Graphics g) {
671        g.setColor(editor.getBackground());
672        g.fillRect(0, 0, editor.getWidth(), editor.getHeight());
673    }
674
675    /**
676     * Fetches the text component associated with this
677     * UI implementation.  This will be null until
678     * the ui has been installed.
679     *
680     * @return the editor component
681     */
682    protected final JTextComponent getComponent() {
683        return editor;
684    }
685
686    /**
687     * Flags model changes.
688     * This is called whenever the model has changed.
689     * It is implemented to rebuild the view hierarchy
690     * to represent the default root element of the
691     * associated model.
692     */
693    protected void modelChanged() {
694        // create a view hierarchy
695        ViewFactory f = rootView.getViewFactory();
696        Document doc = editor.getDocument();
697        Element elem = doc.getDefaultRootElement();
698        setView(f.create(elem));
699    }
700
701    /**
702     * Sets the current root of the view hierarchy and calls invalidate().
703     * If there were any child components, they will be removed (i.e.
704     * there are assumed to have come from components embedded in views).
705     *
706     * @param v the root view
707     */
708    protected final void setView(View v) {
709        rootView.setView(v);
710        painted = false;
711        editor.revalidate();
712        editor.repaint();
713    }
714
715    /**
716     * Paints the interface safely with a guarantee that
717     * the model won't change from the view of this thread.
718     * This does the following things, rendering from
719     * back to front.
720     * <ol>
721     * <li>
722     * If the component is marked as opaque, the background
723     * is painted in the current background color of the
724     * component.
725     * <li>
726     * The highlights (if any) are painted.
727     * <li>
728     * The view hierarchy is painted.
729     * <li>
730     * The caret is painted.
731     * </ol>
732     *
733     * @param g the graphics context
734     */
735    protected void paintSafely(Graphics g) {
736        painted = true;
737        Highlighter highlighter = editor.getHighlighter();
738        Caret caret = editor.getCaret();
739
740        // paint the background
741        if (editor.isOpaque()) {
742            paintBackground(g);
743        }
744
745        // paint the highlights
746        if (highlighter != null) {
747            highlighter.paint(g);
748        }
749
750        // paint the view hierarchy
751        Rectangle alloc = getVisibleEditorRect();
752        if (alloc != null) {
753            rootView.paint(g, alloc);
754        }
755
756        // paint the caret
757        if (caret != null) {
758            caret.paint(g);
759        }
760
761        if (dropCaret != null) {
762            dropCaret.paint(g);
763        }
764    }
765
766    // --- ComponentUI methods --------------------------------------------
767
768    /**
769     * Installs the UI for a component.  This does the following
770     * things.
771     * <ol>
772     * <li>
773     * Sets the associated component to opaque if the opaque property
774     * has not already been set by the client program. This will cause the
775     * component's background color to be painted.
776     * <li>
777     * Installs the default caret and highlighter into the
778     * associated component. These properties are only set if their
779     * current value is either {@code null} or an instance of
780     * {@link UIResource}.
781     * <li>
782     * Attaches to the editor and model.  If there is no
783     * model, a default one is created.
784     * <li>
785     * Creates the view factory and the view hierarchy used
786     * to represent the model.
787     * </ol>
788     *
789     * @param c the editor component
790     * @see ComponentUI#installUI
791     */
792    public void installUI(JComponent c) {
793        if (c instanceof JTextComponent) {
794            editor = (JTextComponent) c;
795
796            // common case is background painted... this can
797            // easily be changed by subclasses or from outside
798            // of the component.
799            LookAndFeel.installProperty(editor, "opaque", Boolean.TRUE);
800            LookAndFeel.installProperty(editor, "autoscrolls", Boolean.TRUE);
801
802            // install defaults
803            installDefaults();
804            installDefaults2();
805
806            // margin required to show caret in the rightmost position
807            caretMargin = -1;
808            Object property = UIManager.get("Caret.width");
809            if (property instanceof Number) {
810                caretMargin = ((Number) property).intValue();
811            }
812            property = c.getClientProperty("caretWidth");
813            if (property instanceof Number) {
814                caretMargin = ((Number) property).intValue();
815            }
816            if (caretMargin < 0) {
817                caretMargin = DEFAULT_CARET_MARGIN;
818            }
819
820            // attach to the model and editor
821            editor.addPropertyChangeListener(updateHandler);
822            Document doc = editor.getDocument();
823            if (doc == null) {
824                // no model, create a default one.  This will
825                // fire a notification to the updateHandler
826                // which takes care of the rest.
827                editor.setDocument(getEditorKit(editor).createDefaultDocument());
828            } else {
829                doc.addDocumentListener(updateHandler);
830                modelChanged();
831            }
832
833            // install keymap
834            installListeners();
835            installKeyboardActions();
836
837            LayoutManager oldLayout = editor.getLayout();
838            if ((oldLayout == null) || (oldLayout instanceof UIResource)) {
839                // by default, use default LayoutManger implementation that
840                // will position the components associated with a View object.
841                editor.setLayout(updateHandler);
842            }
843
844            updateBackground(editor);
845        } else {
846            throw new Error("TextUI needs JTextComponent");
847        }
848    }
849
850    /**
851     * Deinstalls the UI for a component.  This removes the listeners,
852     * uninstalls the highlighter, removes views, and nulls out the keymap.
853     *
854     * @param c the editor component
855     * @see ComponentUI#uninstallUI
856     */
857    public void uninstallUI(JComponent c) {
858        // detach from the model
859        editor.removePropertyChangeListener(updateHandler);
860        editor.getDocument().removeDocumentListener(updateHandler);
861
862        // view part
863        painted = false;
864        uninstallDefaults();
865        rootView.setView(null);
866        c.removeAll();
867        LayoutManager lm = c.getLayout();
868        if (lm instanceof UIResource) {
869            c.setLayout(null);
870        }
871
872        // controller part
873        uninstallKeyboardActions();
874        uninstallListeners();
875
876        editor = null;
877    }
878
879    /**
880     * Superclass paints background in an uncontrollable way
881     * (i.e. one might want an image tiled into the background).
882     * To prevent this from happening twice, this method is
883     * reimplemented to simply paint.
884     * <p>
885     * <em>NOTE:</em> NOTE: Superclass is also not thread-safe in its
886     * rendering of the background, although that is not an issue with the
887     * default rendering.
888     */
889    public void update(Graphics g, JComponent c) {
890        paint(g, c);
891    }
892
893    /**
894     * Paints the interface.  This is routed to the
895     * paintSafely method under the guarantee that
896     * the model won't change from the view of this thread
897     * while it's rendering (if the associated model is
898     * derived from AbstractDocument).  This enables the
899     * model to potentially be updated asynchronously.
900     *
901     * @param g the graphics context
902     * @param c the editor component
903     */
904    public final void paint(Graphics g, JComponent c) {
905        if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
906            Document doc = editor.getDocument();
907            if (doc instanceof AbstractDocument) {
908                ((AbstractDocument)doc).readLock();
909            }
910            try {
911                paintSafely(g);
912            } finally {
913                if (doc instanceof AbstractDocument) {
914                    ((AbstractDocument)doc).readUnlock();
915                }
916            }
917        }
918    }
919
920    /**
921     * Gets the preferred size for the editor component.  If the component
922     * has been given a size prior to receiving this request, it will
923     * set the size of the view hierarchy to reflect the size of the component
924     * before requesting the preferred size of the view hierarchy.  This
925     * allows formatted views to format to the current component size before
926     * answering the request.  Other views don't care about currently formatted
927     * size and give the same answer either way.
928     *
929     * @param c the editor component
930     * @return the size
931     */
932    public Dimension getPreferredSize(JComponent c) {
933        Document doc = editor.getDocument();
934        Insets i = c.getInsets();
935        Dimension d = c.getSize();
936
937        if (doc instanceof AbstractDocument) {
938            ((AbstractDocument)doc).readLock();
939        }
940        try {
941            if ((d.width > (i.left + i.right + caretMargin)) && (d.height > (i.top + i.bottom))) {
942                rootView.setSize(d.width - i.left - i.right -
943                        caretMargin, d.height - i.top - i.bottom);
944            }
945            else if (!rootViewInitialized && (d.width <= 0 || d.height <= 0)) {
946                // Probably haven't been layed out yet, force some sort of
947                // initial sizing.
948                rootViewInitialized = true;
949                rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
950            }
951            d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
952                         (long) i.left + (long) i.right + caretMargin, Integer.MAX_VALUE);
953            d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
954                                      (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
955        } finally {
956            if (doc instanceof AbstractDocument) {
957                ((AbstractDocument)doc).readUnlock();
958            }
959        }
960        return d;
961    }
962
963    /**
964     * Gets the minimum size for the editor component.
965     *
966     * @param c the editor component
967     * @return the size
968     */
969    public Dimension getMinimumSize(JComponent c) {
970        Document doc = editor.getDocument();
971        Insets i = c.getInsets();
972        Dimension d = new Dimension();
973        if (doc instanceof AbstractDocument) {
974            ((AbstractDocument)doc).readLock();
975        }
976        try {
977            d.width = (int) rootView.getMinimumSpan(View.X_AXIS) + i.left + i.right + caretMargin;
978            d.height = (int)  rootView.getMinimumSpan(View.Y_AXIS) + i.top + i.bottom;
979        } finally {
980            if (doc instanceof AbstractDocument) {
981                ((AbstractDocument)doc).readUnlock();
982            }
983        }
984        return d;
985    }
986
987    /**
988     * Gets the maximum size for the editor component.
989     *
990     * @param c the editor component
991     * @return the size
992     */
993    public Dimension getMaximumSize(JComponent c) {
994        Document doc = editor.getDocument();
995        Insets i = c.getInsets();
996        Dimension d = new Dimension();
997        if (doc instanceof AbstractDocument) {
998            ((AbstractDocument)doc).readLock();
999        }
1000        try {
1001            d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) +
1002                                     (long) i.left + (long) i.right + caretMargin, Integer.MAX_VALUE);
1003            d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) +
1004                                      (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
1005        } finally {
1006            if (doc instanceof AbstractDocument) {
1007                ((AbstractDocument)doc).readUnlock();
1008            }
1009        }
1010        return d;
1011    }
1012
1013    // ---- TextUI methods -------------------------------------------
1014
1015
1016    /**
1017     * Gets the allocation to give the root View.  Due
1018     * to an unfortunate set of historical events this
1019     * method is inappropriately named.  The Rectangle
1020     * returned has nothing to do with visibility.
1021     * The component must have a non-zero positive size for
1022     * this translation to be computed.
1023     *
1024     * @return the bounding box for the root view
1025     */
1026    protected Rectangle getVisibleEditorRect() {
1027        Rectangle alloc = editor.getBounds();
1028        if ((alloc.width > 0) && (alloc.height > 0)) {
1029            alloc.x = alloc.y = 0;
1030            Insets insets = editor.getInsets();
1031            alloc.x += insets.left;
1032            alloc.y += insets.top;
1033            alloc.width -= insets.left + insets.right + caretMargin;
1034            alloc.height -= insets.top + insets.bottom;
1035            return alloc;
1036        }
1037        return null;
1038    }
1039
1040    /**
1041     * Converts the given location in the model to a place in
1042     * the view coordinate system.
1043     * The component must have a non-zero positive size for
1044     * this translation to be computed.
1045     *
1046     * @param tc the text component for which this UI is installed
1047     * @param pos the local location in the model to translate &gt;= 0
1048     * @return the coordinates as a rectangle, null if the model is not painted
1049     * @exception BadLocationException  if the given position does not
1050     *   represent a valid location in the associated document
1051     * @see TextUI#modelToView
1052     *
1053     * @deprecated replaced by
1054     *     {@link #modelToView2D(JTextComponent, int, Position.Bias)}
1055     */
1056    @Deprecated(since = "9")
1057    @Override
1058    public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
1059        return modelToView(tc, pos, Position.Bias.Forward);
1060    }
1061
1062    /**
1063     * Converts the given location in the model to a place in
1064     * the view coordinate system.
1065     * The component must have a non-zero positive size for
1066     * this translation to be computed.
1067     *
1068     * @param tc the text component for which this UI is installed
1069     * @param pos the local location in the model to translate &gt;= 0
1070     * @return the coordinates as a rectangle, null if the model is not painted
1071     * @exception BadLocationException  if the given position does not
1072     *   represent a valid location in the associated document
1073     * @see TextUI#modelToView
1074     *
1075     * @deprecated replaced by
1076     *     {@link #modelToView2D(JTextComponent, int, Position.Bias)}
1077     */
1078    @Deprecated(since = "9")
1079    @Override
1080    public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias)
1081            throws BadLocationException
1082    {
1083        return (Rectangle) modelToView(tc, pos, bias, false);
1084    }
1085
1086    @Override
1087    public Rectangle2D modelToView2D(JTextComponent tc, int pos,
1088                                     Position.Bias bias)
1089            throws BadLocationException
1090    {
1091        return modelToView(tc, pos, bias, true);
1092    }
1093
1094    private Rectangle2D modelToView(JTextComponent tc, int pos,
1095                                    Position.Bias bias, boolean useFPAPI)
1096            throws BadLocationException
1097    {
1098        Document doc = editor.getDocument();
1099        if (doc instanceof AbstractDocument) {
1100            ((AbstractDocument)doc).readLock();
1101        }
1102        try {
1103            Rectangle alloc = getVisibleEditorRect();
1104            if (alloc != null) {
1105                rootView.setSize(alloc.width, alloc.height);
1106                Shape s = rootView.modelToView(pos, alloc, bias);
1107                if (s != null) {
1108                    return useFPAPI ? s.getBounds2D() : s.getBounds();
1109                }
1110            }
1111        } finally {
1112            if (doc instanceof AbstractDocument) {
1113                ((AbstractDocument)doc).readUnlock();
1114            }
1115        }
1116        return null;
1117    }
1118
1119    /**
1120     * Converts the given place in the view coordinate system
1121     * to the nearest representative location in the model.
1122     * The component must have a non-zero positive size for
1123     * this translation to be computed.
1124     *
1125     * @param tc the text component for which this UI is installed
1126     * @param pt the location in the view to translate.  This
1127     *  should be in the same coordinate system as the mouse events.
1128     * @return the offset from the start of the document &gt;= 0,
1129     *   -1 if not painted
1130     * @see TextUI#viewToModel
1131     *
1132     * @deprecated replaced by
1133     *     {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
1134     */
1135    @Deprecated(since = "9")
1136    @Override
1137    public int viewToModel(JTextComponent tc, Point pt) {
1138        return viewToModel(tc, pt, discardBias);
1139    }
1140
1141    /**
1142     * Converts the given place in the view coordinate system
1143     * to the nearest representative location in the model.
1144     * The component must have a non-zero positive size for
1145     * this translation to be computed.
1146     *
1147     * @param tc the text component for which this UI is installed
1148     * @param pt the location in the view to translate.  This
1149     *  should be in the same coordinate system as the mouse events.
1150     * @return the offset from the start of the document &gt;= 0,
1151     *   -1 if the component doesn't yet have a positive size.
1152     * @see TextUI#viewToModel
1153     *
1154     * @deprecated replaced by
1155     *     {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
1156     */
1157    @Deprecated(since = "9")
1158    @Override
1159    public int viewToModel(JTextComponent tc, Point pt,
1160                           Position.Bias[] biasReturn) {
1161        return viewToModel(tc, pt.x, pt.y, biasReturn);
1162    }
1163
1164    @Override
1165    public int viewToModel2D(JTextComponent tc, Point2D pt,
1166                             Position.Bias[] biasReturn) {
1167        return viewToModel(tc, (float) pt.getX(), (float) pt.getY(), biasReturn);
1168    }
1169
1170    private int viewToModel(JTextComponent tc, float x, float y,
1171                            Position.Bias[] biasReturn) {
1172        int offs = -1;
1173        Document doc = editor.getDocument();
1174        if (doc instanceof AbstractDocument) {
1175            ((AbstractDocument)doc).readLock();
1176        }
1177        try {
1178            Rectangle alloc = getVisibleEditorRect();
1179            if (alloc != null) {
1180                rootView.setSize(alloc.width, alloc.height);
1181                offs = rootView.viewToModel(x, y, alloc, biasReturn);
1182            }
1183        } finally {
1184            if (doc instanceof AbstractDocument) {
1185                ((AbstractDocument)doc).readUnlock();
1186            }
1187        }
1188        return offs;
1189    }
1190
1191    /**
1192     * {@inheritDoc}
1193     */
1194    public int getNextVisualPositionFrom(JTextComponent t, int pos,
1195                    Position.Bias b, int direction, Position.Bias[] biasRet)
1196                    throws BadLocationException{
1197        Document doc = editor.getDocument();
1198
1199        if (pos < -1 || pos > doc.getLength()) {
1200            throw new BadLocationException("Invalid position", pos);
1201        }
1202
1203        if (doc instanceof AbstractDocument) {
1204            ((AbstractDocument)doc).readLock();
1205        }
1206        try {
1207            if (painted) {
1208                Rectangle alloc = getVisibleEditorRect();
1209                if (alloc != null) {
1210                    rootView.setSize(alloc.width, alloc.height);
1211                }
1212                return rootView.getNextVisualPositionFrom(pos, b, alloc, direction,
1213                                                          biasRet);
1214            }
1215        } finally {
1216            if (doc instanceof AbstractDocument) {
1217                ((AbstractDocument)doc).readUnlock();
1218            }
1219        }
1220        return -1;
1221    }
1222
1223    /**
1224     * Causes the portion of the view responsible for the
1225     * given part of the model to be repainted.  Does nothing if
1226     * the view is not currently painted.
1227     *
1228     * @param tc the text component for which this UI is installed
1229     * @param p0 the beginning of the range &gt;= 0
1230     * @param p1 the end of the range &gt;= p0
1231     * @see TextUI#damageRange
1232     */
1233    public void damageRange(JTextComponent tc, int p0, int p1) {
1234        damageRange(tc, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
1235    }
1236
1237    /**
1238     * Causes the portion of the view responsible for the
1239     * given part of the model to be repainted.
1240     *
1241     * @param p0 the beginning of the range &gt;= 0
1242     * @param p1 the end of the range &gt;= p0
1243     */
1244    public void damageRange(JTextComponent t, int p0, int p1,
1245                            Position.Bias p0Bias, Position.Bias p1Bias) {
1246        if (painted) {
1247            Rectangle alloc = getVisibleEditorRect();
1248            if (alloc != null) {
1249                Document doc = t.getDocument();
1250                if (doc instanceof AbstractDocument) {
1251                    ((AbstractDocument)doc).readLock();
1252                }
1253                try {
1254                    rootView.setSize(alloc.width, alloc.height);
1255                    Shape toDamage = rootView.modelToView(p0, p0Bias,
1256                            p1, p1Bias, alloc);
1257                    Rectangle rect = (toDamage instanceof Rectangle) ?
1258                            (Rectangle)toDamage : toDamage.getBounds();
1259                    editor.repaint(rect.x, rect.y, rect.width, rect.height);
1260                } catch (BadLocationException e) {
1261                } finally {
1262                    if (doc instanceof AbstractDocument) {
1263                        ((AbstractDocument)doc).readUnlock();
1264                    }
1265                }
1266            }
1267        }
1268    }
1269
1270    /**
1271     * Fetches the EditorKit for the UI.
1272     *
1273     * @param tc the text component for which this UI is installed
1274     * @return the editor capabilities
1275     * @see TextUI#getEditorKit
1276     */
1277    public EditorKit getEditorKit(JTextComponent tc) {
1278        return defaultKit;
1279    }
1280
1281    /**
1282     * Fetches a View with the allocation of the associated
1283     * text component (i.e. the root of the hierarchy) that
1284     * can be traversed to determine how the model is being
1285     * represented spatially.
1286     * <p style="color:red;">
1287     * <b>NOTE:</b>The View hierarchy can
1288     * be traversed from the root view, and other things
1289     * can be done as well.  Things done in this way cannot
1290     * be protected like simple method calls through the TextUI.
1291     * Therefore, proper operation in the presence of concurrency
1292     * must be arranged by any logic that calls this method!
1293     *
1294     * @param tc the text component for which this UI is installed
1295     * @return the view
1296     * @see TextUI#getRootView
1297     */
1298    public View getRootView(JTextComponent tc) {
1299        return rootView;
1300    }
1301
1302
1303    /**
1304     * Returns the string to be used as the tooltip at the passed in location.
1305     * This forwards the method onto the root View.
1306     *
1307     * @see javax.swing.text.JTextComponent#getToolTipText
1308     * @see javax.swing.text.View#getToolTipText
1309     * @since 1.4
1310     */
1311    @SuppressWarnings("deprecation")
1312    public String getToolTipText(JTextComponent t, Point pt) {
1313        if (!painted) {
1314            return null;
1315        }
1316        Document doc = editor.getDocument();
1317        String tt = null;
1318        Rectangle alloc = getVisibleEditorRect();
1319
1320        if (alloc != null) {
1321            if (doc instanceof AbstractDocument) {
1322                ((AbstractDocument)doc).readLock();
1323            }
1324            try {
1325                tt = rootView.getToolTipText(pt.x, pt.y, alloc);
1326            } finally {
1327                if (doc instanceof AbstractDocument) {
1328                    ((AbstractDocument)doc).readUnlock();
1329                }
1330            }
1331        }
1332        return tt;
1333    }
1334
1335    // --- ViewFactory methods ------------------------------
1336
1337    /**
1338     * Creates a view for an element.
1339     * If a subclass wishes to directly implement the factory
1340     * producing the view(s), it should reimplement this
1341     * method.  By default it simply returns null indicating
1342     * it is unable to represent the element.
1343     *
1344     * @param elem the element
1345     * @return the view
1346     */
1347    public View create(Element elem) {
1348        return null;
1349    }
1350
1351    /**
1352     * Creates a view for an element.
1353     * If a subclass wishes to directly implement the factory
1354     * producing the view(s), it should reimplement this
1355     * method.  By default it simply returns null indicating
1356     * it is unable to represent the part of the element.
1357     *
1358     * @param elem the element
1359     * @param p0 the starting offset &gt;= 0
1360     * @param p1 the ending offset &gt;= p0
1361     * @return the view
1362     */
1363    public View create(Element elem, int p0, int p1) {
1364        return null;
1365    }
1366
1367    /**
1368     * Default implementation of the interface {@code Caret}.
1369     */
1370    public static class BasicCaret extends DefaultCaret implements UIResource {}
1371
1372    /**
1373     * Default implementation of the interface {@code Highlighter}.
1374     */
1375    public static class BasicHighlighter extends DefaultHighlighter implements UIResource {}
1376
1377    static class BasicCursor extends Cursor implements UIResource {
1378        BasicCursor(int type) {
1379            super(type);
1380        }
1381
1382        BasicCursor(String name) {
1383            super(name);
1384        }
1385    }
1386
1387    private static BasicCursor textCursor = new BasicCursor(Cursor.TEXT_CURSOR);
1388    // ----- member variables ---------------------------------------
1389
1390    private static final EditorKit defaultKit = new DefaultEditorKit();
1391    transient JTextComponent editor;
1392    transient boolean painted;
1393    transient RootView rootView = new RootView();
1394    transient UpdateHandler updateHandler = new UpdateHandler();
1395    private static final TransferHandler defaultTransferHandler = new TextTransferHandler();
1396    private final DragListener dragListener = getDragListener();
1397    private static final Position.Bias[] discardBias = new Position.Bias[1];
1398    private DefaultCaret dropCaret;
1399    private int caretMargin;
1400    private boolean rootViewInitialized;
1401
1402    /**
1403     * Root view that acts as a gateway between the component
1404     * and the View hierarchy.
1405     */
1406    class RootView extends View {
1407
1408        RootView() {
1409            super(null);
1410        }
1411
1412        void setView(View v) {
1413            View oldView = view;
1414            view = null;
1415            if (oldView != null) {
1416                // get rid of back reference so that the old
1417                // hierarchy can be garbage collected.
1418                oldView.setParent(null);
1419            }
1420            if (v != null) {
1421                v.setParent(this);
1422            }
1423            view = v;
1424        }
1425
1426        /**
1427         * Fetches the attributes to use when rendering.  At the root
1428         * level there are no attributes.  If an attribute is resolved
1429         * up the view hierarchy this is the end of the line.
1430         */
1431        public AttributeSet getAttributes() {
1432            return null;
1433        }
1434
1435        /**
1436         * Determines the preferred span for this view along an axis.
1437         *
1438         * @param axis may be either X_AXIS or Y_AXIS
1439         * @return the span the view would like to be rendered into.
1440         *         Typically the view is told to render into the span
1441         *         that is returned, although there is no guarantee.
1442         *         The parent may choose to resize or break the view.
1443         */
1444        public float getPreferredSpan(int axis) {
1445            if (view != null) {
1446                return view.getPreferredSpan(axis);
1447            }
1448            return 10;
1449        }
1450
1451        /**
1452         * Determines the minimum span for this view along an axis.
1453         *
1454         * @param axis may be either X_AXIS or Y_AXIS
1455         * @return the span the view would like to be rendered into.
1456         *         Typically the view is told to render into the span
1457         *         that is returned, although there is no guarantee.
1458         *         The parent may choose to resize or break the view.
1459         */
1460        public float getMinimumSpan(int axis) {
1461            if (view != null) {
1462                return view.getMinimumSpan(axis);
1463            }
1464            return 10;
1465        }
1466
1467        /**
1468         * Determines the maximum span for this view along an axis.
1469         *
1470         * @param axis may be either X_AXIS or Y_AXIS
1471         * @return the span the view would like to be rendered into.
1472         *         Typically the view is told to render into the span
1473         *         that is returned, although there is no guarantee.
1474         *         The parent may choose to resize or break the view.
1475         */
1476        public float getMaximumSpan(int axis) {
1477            return Integer.MAX_VALUE;
1478        }
1479
1480        /**
1481         * Specifies that a preference has changed.
1482         * Child views can call this on the parent to indicate that
1483         * the preference has changed.  The root view routes this to
1484         * invalidate on the hosting component.
1485         * <p>
1486         * This can be called on a different thread from the
1487         * event dispatching thread and is basically unsafe to
1488         * propagate into the component.  To make this safe,
1489         * the operation is transferred over to the event dispatching
1490         * thread for completion.  It is a design goal that all view
1491         * methods be safe to call without concern for concurrency,
1492         * and this behavior helps make that true.
1493         *
1494         * @param child the child view
1495         * @param width true if the width preference has changed
1496         * @param height true if the height preference has changed
1497         */
1498        public void preferenceChanged(View child, boolean width, boolean height) {
1499            editor.revalidate();
1500        }
1501
1502        /**
1503         * Determines the desired alignment for this view along an axis.
1504         *
1505         * @param axis may be either X_AXIS or Y_AXIS
1506         * @return the desired alignment, where 0.0 indicates the origin
1507         *     and 1.0 the full span away from the origin
1508         */
1509        public float getAlignment(int axis) {
1510            if (view != null) {
1511                return view.getAlignment(axis);
1512            }
1513            return 0;
1514        }
1515
1516        /**
1517         * Renders the view.
1518         *
1519         * @param g the graphics context
1520         * @param allocation the region to render into
1521         */
1522        public void paint(Graphics g, Shape allocation) {
1523            if (view != null) {
1524                Rectangle alloc = (allocation instanceof Rectangle) ?
1525                          (Rectangle)allocation : allocation.getBounds();
1526                setSize(alloc.width, alloc.height);
1527                view.paint(g, allocation);
1528            }
1529        }
1530
1531        /**
1532         * Sets the view parent.
1533         *
1534         * @param parent the parent view
1535         */
1536        public void setParent(View parent) {
1537            throw new Error("Can't set parent on root view");
1538        }
1539
1540        /**
1541         * Returns the number of views in this view.  Since
1542         * this view simply wraps the root of the view hierarchy
1543         * it has exactly one child.
1544         *
1545         * @return the number of views
1546         * @see #getView
1547         */
1548        public int getViewCount() {
1549            return 1;
1550        }
1551
1552        /**
1553         * Gets the n-th view in this container.
1554         *
1555         * @param n the number of the view to get
1556         * @return the view
1557         */
1558        public View getView(int n) {
1559            return view;
1560        }
1561
1562        /**
1563         * Returns the child view index representing the given position in
1564         * the model.  This is implemented to return the index of the only
1565         * child.
1566         *
1567         * @param pos the position &gt;= 0
1568         * @return  index of the view representing the given position, or
1569         *   -1 if no view represents that position
1570         * @since 1.3
1571         */
1572        public int getViewIndex(int pos, Position.Bias b) {
1573            return 0;
1574        }
1575
1576        /**
1577         * Fetches the allocation for the given child view.
1578         * This enables finding out where various views
1579         * are located, without assuming the views store
1580         * their location.  This returns the given allocation
1581         * since this view simply acts as a gateway between
1582         * the view hierarchy and the associated component.
1583         *
1584         * @param index the index of the child
1585         * @param a  the allocation to this view.
1586         * @return the allocation to the child
1587         */
1588        public Shape getChildAllocation(int index, Shape a) {
1589            return a;
1590        }
1591
1592        /**
1593         * Provides a mapping from the document model coordinate space
1594         * to the coordinate space of the view mapped to it.
1595         *
1596         * @param pos the position to convert
1597         * @param a the allocated region to render into
1598         * @return the bounding box of the given position
1599         */
1600        public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
1601            if (view != null) {
1602                return view.modelToView(pos, a, b);
1603            }
1604            return null;
1605        }
1606
1607        /**
1608         * Provides a mapping from the document model coordinate space
1609         * to the coordinate space of the view mapped to it.
1610         *
1611         * @param p0 the position to convert &gt;= 0
1612         * @param b0 the bias toward the previous character or the
1613         *  next character represented by p0, in case the
1614         *  position is a boundary of two views.
1615         * @param p1 the position to convert &gt;= 0
1616         * @param b1 the bias toward the previous character or the
1617         *  next character represented by p1, in case the
1618         *  position is a boundary of two views.
1619         * @param a the allocated region to render into
1620         * @return the bounding box of the given position is returned
1621         * @exception BadLocationException  if the given position does
1622         *   not represent a valid location in the associated document
1623         * @exception IllegalArgumentException for an invalid bias argument
1624         * @see View#viewToModel
1625         */
1626        public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
1627            if (view != null) {
1628                return view.modelToView(p0, b0, p1, b1, a);
1629            }
1630            return null;
1631        }
1632
1633        /**
1634         * Provides a mapping from the view coordinate space to the logical
1635         * coordinate space of the model.
1636         *
1637         * @param x x coordinate of the view location to convert
1638         * @param y y coordinate of the view location to convert
1639         * @param a the allocated region to render into
1640         * @return the location within the model that best represents the
1641         *    given point in the view
1642         */
1643        public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
1644            if (view != null) {
1645                int retValue = view.viewToModel(x, y, a, bias);
1646                return retValue;
1647            }
1648            return -1;
1649        }
1650
1651        /**
1652         * Provides a way to determine the next visually represented model
1653         * location that one might place a caret.  Some views may not be visible,
1654         * they might not be in the same order found in the model, or they just
1655         * might not allow access to some of the locations in the model.
1656         * This method enables specifying a position to convert
1657         * within the range of &gt;=0.  If the value is -1, a position
1658         * will be calculated automatically.  If the value &lt; -1,
1659         * the {@code BadLocationException} will be thrown.
1660         *
1661         * @param pos the position to convert &gt;= 0
1662         * @param a the allocated region to render into
1663         * @param direction the direction from the current position that can
1664         *  be thought of as the arrow keys typically found on a keyboard.
1665         *  This may be SwingConstants.WEST, SwingConstants.EAST,
1666         *  SwingConstants.NORTH, or SwingConstants.SOUTH.
1667         * @return the location within the model that best represents the next
1668         *  location visual position.
1669         * @exception BadLocationException the given position is not a valid
1670         *                                 position within the document
1671         * @exception IllegalArgumentException for an invalid direction
1672         */
1673        public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
1674                                             int direction,
1675                                             Position.Bias[] biasRet)
1676            throws BadLocationException {
1677            if (pos < -1 || pos > getDocument().getLength()) {
1678                throw new BadLocationException("invalid position", pos);
1679            }
1680            if( view != null ) {
1681                int nextPos = view.getNextVisualPositionFrom(pos, b, a,
1682                                                     direction, biasRet);
1683                if(nextPos != -1) {
1684                    pos = nextPos;
1685                }
1686                else {
1687                    biasRet[0] = b;
1688                }
1689            }
1690            return pos;
1691        }
1692
1693        /**
1694         * Gives notification that something was inserted into the document
1695         * in a location that this view is responsible for.
1696         *
1697         * @param e the change information from the associated document
1698         * @param a the current allocation of the view
1699         * @param f the factory to use to rebuild if the view has children
1700         */
1701        public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1702            if (view != null) {
1703                view.insertUpdate(e, a, f);
1704            }
1705        }
1706
1707        /**
1708         * Gives notification that something was removed from the document
1709         * in a location that this view is responsible for.
1710         *
1711         * @param e the change information from the associated document
1712         * @param a the current allocation of the view
1713         * @param f the factory to use to rebuild if the view has children
1714         */
1715        public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1716            if (view != null) {
1717                view.removeUpdate(e, a, f);
1718            }
1719        }
1720
1721        /**
1722         * Gives notification from the document that attributes were changed
1723         * in a location that this view is responsible for.
1724         *
1725         * @param e the change information from the associated document
1726         * @param a the current allocation of the view
1727         * @param f the factory to use to rebuild if the view has children
1728         */
1729        public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1730            if (view != null) {
1731                view.changedUpdate(e, a, f);
1732            }
1733        }
1734
1735        /**
1736         * Returns the document model underlying the view.
1737         *
1738         * @return the model
1739         */
1740        public Document getDocument() {
1741            return editor.getDocument();
1742        }
1743
1744        /**
1745         * Returns the starting offset into the model for this view.
1746         *
1747         * @return the starting offset
1748         */
1749        public int getStartOffset() {
1750            if (view != null) {
1751                return view.getStartOffset();
1752            }
1753            return getElement().getStartOffset();
1754        }
1755
1756        /**
1757         * Returns the ending offset into the model for this view.
1758         *
1759         * @return the ending offset
1760         */
1761        public int getEndOffset() {
1762            if (view != null) {
1763                return view.getEndOffset();
1764            }
1765            return getElement().getEndOffset();
1766        }
1767
1768        /**
1769         * Gets the element that this view is mapped to.
1770         *
1771         * @return the view
1772         */
1773        public Element getElement() {
1774            if (view != null) {
1775                return view.getElement();
1776            }
1777            return editor.getDocument().getDefaultRootElement();
1778        }
1779
1780        /**
1781         * Breaks this view on the given axis at the given length.
1782         *
1783         * @param axis may be either X_AXIS or Y_AXIS
1784         * @param len specifies where a break is desired in the span
1785         * @param a the current allocation of the view
1786         * @return the fragment of the view that represents the given span
1787         *   if the view can be broken, otherwise null
1788         */
1789        public View breakView(int axis, float len, Shape a) {
1790            throw new Error("Can't break root view");
1791        }
1792
1793        /**
1794         * Determines the resizability of the view along the
1795         * given axis.  A value of 0 or less is not resizable.
1796         *
1797         * @param axis may be either X_AXIS or Y_AXIS
1798         * @return the weight
1799         */
1800        public int getResizeWeight(int axis) {
1801            if (view != null) {
1802                return view.getResizeWeight(axis);
1803            }
1804            return 0;
1805        }
1806
1807        /**
1808         * Sets the view size.
1809         *
1810         * @param width the width
1811         * @param height the height
1812         */
1813        public void setSize(float width, float height) {
1814            if (view != null) {
1815                view.setSize(width, height);
1816            }
1817        }
1818
1819        /**
1820         * Fetches the container hosting the view.  This is useful for
1821         * things like scheduling a repaint, finding out the host
1822         * components font, etc.  The default implementation
1823         * of this is to forward the query to the parent view.
1824         *
1825         * @return the container
1826         */
1827        public Container getContainer() {
1828            return editor;
1829        }
1830
1831        /**
1832         * Fetches the factory to be used for building the
1833         * various view fragments that make up the view that
1834         * represents the model.  This is what determines
1835         * how the model will be represented.  This is implemented
1836         * to fetch the factory provided by the associated
1837         * EditorKit unless that is null, in which case this
1838         * simply returns the BasicTextUI itself which allows
1839         * subclasses to implement a simple factory directly without
1840         * creating extra objects.
1841         *
1842         * @return the factory
1843         */
1844        public ViewFactory getViewFactory() {
1845            EditorKit kit = getEditorKit(editor);
1846            ViewFactory f = kit.getViewFactory();
1847            if (f != null) {
1848                return f;
1849            }
1850            return BasicTextUI.this;
1851        }
1852
1853        private View view;
1854
1855    }
1856
1857    /**
1858     * Handles updates from various places.  If the model is changed,
1859     * this class unregisters as a listener to the old model and
1860     * registers with the new model.  If the document model changes,
1861     * the change is forwarded to the root view.  If the focus
1862     * accelerator changes, a new keystroke is registered to request
1863     * focus.
1864     */
1865    class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, UIResource {
1866
1867        // --- PropertyChangeListener methods -----------------------
1868
1869        /**
1870         * This method gets called when a bound property is changed.
1871         * We are looking for document changes on the editor.
1872         */
1873        public final void propertyChange(PropertyChangeEvent evt) {
1874            Object oldValue = evt.getOldValue();
1875            Object newValue = evt.getNewValue();
1876            String propertyName = evt.getPropertyName();
1877            if ((oldValue instanceof Document) || (newValue instanceof Document)) {
1878                if (oldValue != null) {
1879                    ((Document)oldValue).removeDocumentListener(this);
1880                    i18nView = false;
1881                }
1882                if (newValue != null) {
1883                    ((Document)newValue).addDocumentListener(this);
1884                    if ("document" == propertyName) {
1885                        setView(null);
1886                        BasicTextUI.this.propertyChange(evt);
1887                        modelChanged();
1888                        return;
1889                    }
1890                }
1891                modelChanged();
1892            }
1893            if ("focusAccelerator" == propertyName) {
1894                updateFocusAcceleratorBinding(true);
1895            } else if ("componentOrientation" == propertyName) {
1896                // Changes in ComponentOrientation require the views to be
1897                // rebuilt.
1898                modelChanged();
1899            } else if ("font" == propertyName) {
1900                modelChanged();
1901            } else if ("dropLocation" == propertyName) {
1902                dropIndexChanged();
1903            } else if ("editable" == propertyName) {
1904                updateCursor();
1905                modelChanged();
1906            }
1907            BasicTextUI.this.propertyChange(evt);
1908        }
1909
1910        private void dropIndexChanged() {
1911            if (editor.getDropMode() == DropMode.USE_SELECTION) {
1912                return;
1913            }
1914
1915            JTextComponent.DropLocation dropLocation = editor.getDropLocation();
1916
1917            if (dropLocation == null) {
1918                if (dropCaret != null) {
1919                    dropCaret.deinstall(editor);
1920                    editor.repaint(dropCaret);
1921                    dropCaret = null;
1922                }
1923            } else {
1924                if (dropCaret == null) {
1925                    dropCaret = new BasicCaret();
1926                    dropCaret.install(editor);
1927                    dropCaret.setVisible(true);
1928                }
1929
1930                dropCaret.setDot(dropLocation.getIndex(),
1931                                 dropLocation.getBias());
1932            }
1933        }
1934
1935        // --- DocumentListener methods -----------------------
1936
1937        /**
1938         * The insert notification.  Gets sent to the root of the view structure
1939         * that represents the portion of the model being represented by the
1940         * editor.  The factory is added as an argument to the update so that
1941         * the views can update themselves in a dynamic (not hardcoded) way.
1942         *
1943         * @param e  The change notification from the currently associated
1944         *  document.
1945         * @see DocumentListener#insertUpdate
1946         */
1947        public final void insertUpdate(DocumentEvent e) {
1948            Document doc = e.getDocument();
1949            Object o = doc.getProperty("i18n");
1950            if (o instanceof Boolean) {
1951                Boolean i18nFlag = (Boolean) o;
1952                if (i18nFlag.booleanValue() != i18nView) {
1953                    // i18n flag changed, rebuild the view
1954                    i18nView = i18nFlag.booleanValue();
1955                    modelChanged();
1956                    return;
1957                }
1958            }
1959
1960            // normal insert update
1961            Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1962            rootView.insertUpdate(e, alloc, rootView.getViewFactory());
1963        }
1964
1965        /**
1966         * The remove notification.  Gets sent to the root of the view structure
1967         * that represents the portion of the model being represented by the
1968         * editor.  The factory is added as an argument to the update so that
1969         * the views can update themselves in a dynamic (not hardcoded) way.
1970         *
1971         * @param e  The change notification from the currently associated
1972         *  document.
1973         * @see DocumentListener#removeUpdate
1974         */
1975        public final void removeUpdate(DocumentEvent e) {
1976            Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1977            rootView.removeUpdate(e, alloc, rootView.getViewFactory());
1978        }
1979
1980        /**
1981         * The change notification.  Gets sent to the root of the view structure
1982         * that represents the portion of the model being represented by the
1983         * editor.  The factory is added as an argument to the update so that
1984         * the views can update themselves in a dynamic (not hardcoded) way.
1985         *
1986         * @param e  The change notification from the currently associated
1987         *  document.
1988         * @see DocumentListener#changedUpdate(DocumentEvent)
1989         */
1990        public final void changedUpdate(DocumentEvent e) {
1991            Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1992            rootView.changedUpdate(e, alloc, rootView.getViewFactory());
1993        }
1994
1995        // --- LayoutManager2 methods --------------------------------
1996
1997        /**
1998         * Adds the specified component with the specified name to
1999         * the layout.
2000         * @param name the component name
2001         * @param comp the component to be added
2002         */
2003        public void addLayoutComponent(String name, Component comp) {
2004            // not supported
2005        }
2006
2007        /**
2008         * Removes the specified component from the layout.
2009         * @param comp the component to be removed
2010         */
2011        public void removeLayoutComponent(Component comp) {
2012            if (constraints != null) {
2013                // remove the constraint record
2014                constraints.remove(comp);
2015            }
2016        }
2017
2018        /**
2019         * Calculates the preferred size dimensions for the specified
2020         * panel given the components in the specified parent container.
2021         * @param parent the component to be laid out
2022         *
2023         * @see #minimumLayoutSize
2024         */
2025        public Dimension preferredLayoutSize(Container parent) {
2026            // should not be called (JComponent uses UI instead)
2027            return null;
2028        }
2029
2030        /**
2031         * Calculates the minimum size dimensions for the specified
2032         * panel given the components in the specified parent container.
2033         * @param parent the component to be laid out
2034         * @see #preferredLayoutSize
2035         */
2036        public Dimension minimumLayoutSize(Container parent) {
2037            // should not be called (JComponent uses UI instead)
2038            return null;
2039        }
2040
2041        /**
2042         * Lays out the container in the specified panel.  This is
2043         * implemented to position all components that were added
2044         * with a View object as a constraint.  The current allocation
2045         * of the associated View is used as the location of the
2046         * component.
2047         * <p>
2048         * A read-lock is acquired on the document to prevent the
2049         * view tree from being modified while the layout process
2050         * is active.
2051         *
2052         * @param parent the component which needs to be laid out
2053         */
2054        public void layoutContainer(Container parent) {
2055            if ((constraints != null) && (! constraints.isEmpty())) {
2056                Rectangle alloc = getVisibleEditorRect();
2057                if (alloc != null) {
2058                    Document doc = editor.getDocument();
2059                    if (doc instanceof AbstractDocument) {
2060                        ((AbstractDocument)doc).readLock();
2061                    }
2062                    try {
2063                        rootView.setSize(alloc.width, alloc.height);
2064                        Enumeration<Component> components = constraints.keys();
2065                        while (components.hasMoreElements()) {
2066                            Component comp = components.nextElement();
2067                            View v = (View) constraints.get(comp);
2068                            Shape ca = calculateViewPosition(alloc, v);
2069                            if (ca != null) {
2070                                Rectangle compAlloc = (ca instanceof Rectangle) ?
2071                                    (Rectangle) ca : ca.getBounds();
2072                                comp.setBounds(compAlloc);
2073                            }
2074                        }
2075                    } finally {
2076                        if (doc instanceof AbstractDocument) {
2077                            ((AbstractDocument)doc).readUnlock();
2078                        }
2079                    }
2080                }
2081            }
2082        }
2083
2084        /**
2085         * Find the Shape representing the given view.
2086         */
2087        Shape calculateViewPosition(Shape alloc, View v) {
2088            int pos = v.getStartOffset();
2089            View child = null;
2090            for (View parent = rootView; (parent != null) && (parent != v); parent = child) {
2091                int index = parent.getViewIndex(pos, Position.Bias.Forward);
2092                alloc = parent.getChildAllocation(index, alloc);
2093                child = parent.getView(index);
2094            }
2095            return (child != null) ? alloc : null;
2096        }
2097
2098        /**
2099         * Adds the specified component to the layout, using the specified
2100         * constraint object.  We only store those components that were added
2101         * with a constraint that is of type View.
2102         *
2103         * @param comp the component to be added
2104         * @param constraint  where/how the component is added to the layout.
2105         */
2106        public void addLayoutComponent(Component comp, Object constraint) {
2107            if (constraint instanceof View) {
2108                if (constraints == null) {
2109                    constraints = new Hashtable<Component, Object>(7);
2110                }
2111                constraints.put(comp, constraint);
2112            }
2113        }
2114
2115        /**
2116         * Returns the maximum size of this component.
2117         * @see java.awt.Component#getMinimumSize()
2118         * @see java.awt.Component#getPreferredSize()
2119         * @see LayoutManager
2120         */
2121        public Dimension maximumLayoutSize(Container target) {
2122            // should not be called (JComponent uses UI instead)
2123            return null;
2124        }
2125
2126        /**
2127         * Returns the alignment along the x axis.  This specifies how
2128         * the component would like to be aligned relative to other
2129         * components.  The value should be a number between 0 and 1
2130         * where 0 represents alignment along the origin, 1 is aligned
2131         * the furthest away from the origin, 0.5 is centered, etc.
2132         */
2133        public float getLayoutAlignmentX(Container target) {
2134            return 0.5f;
2135        }
2136
2137        /**
2138         * Returns the alignment along the y axis.  This specifies how
2139         * the component would like to be aligned relative to other
2140         * components.  The value should be a number between 0 and 1
2141         * where 0 represents alignment along the origin, 1 is aligned
2142         * the furthest away from the origin, 0.5 is centered, etc.
2143         */
2144        public float getLayoutAlignmentY(Container target) {
2145            return 0.5f;
2146        }
2147
2148        /**
2149         * Invalidates the layout, indicating that if the layout manager
2150         * has cached information it should be discarded.
2151         */
2152        public void invalidateLayout(Container target) {
2153        }
2154
2155        /**
2156         * The "layout constraints" for the LayoutManager2 implementation.
2157         * These are View objects for those components that are represented
2158         * by a View in the View tree.
2159         */
2160        private Hashtable<Component, Object> constraints;
2161
2162        private boolean i18nView = false;
2163    }
2164
2165    /**
2166     * Wrapper for text actions to return isEnabled false in case editor is non editable
2167     */
2168    class TextActionWrapper extends TextAction {
2169        public TextActionWrapper(TextAction action) {
2170            super((String)action.getValue(Action.NAME));
2171            this.action = action;
2172        }
2173        /**
2174         * The operation to perform when this action is triggered.
2175         *
2176         * @param e the action event
2177         */
2178        public void actionPerformed(ActionEvent e) {
2179            action.actionPerformed(e);
2180        }
2181        public boolean isEnabled() {
2182            return (editor == null || editor.isEditable()) ? action.isEnabled() : false;
2183        }
2184        TextAction action = null;
2185    }
2186
2187
2188    /**
2189     * Registered in the ActionMap.
2190     */
2191    class FocusAction extends AbstractAction {
2192
2193        public void actionPerformed(ActionEvent e) {
2194            editor.requestFocus();
2195        }
2196
2197        public boolean isEnabled() {
2198            return editor.isEditable();
2199        }
2200    }
2201
2202    private static DragListener getDragListener() {
2203        synchronized(DragListener.class) {
2204            DragListener listener =
2205                (DragListener)AppContext.getAppContext().
2206                    get(DragListener.class);
2207
2208            if (listener == null) {
2209                listener = new DragListener();
2210                AppContext.getAppContext().put(DragListener.class, listener);
2211            }
2212
2213            return listener;
2214        }
2215    }
2216
2217    /**
2218     * Listens for mouse events for the purposes of detecting drag gestures.
2219     * BasicTextUI will maintain one of these per AppContext.
2220     */
2221    static class DragListener extends MouseInputAdapter
2222                              implements BeforeDrag {
2223
2224        private boolean dragStarted;
2225
2226        public void dragStarting(MouseEvent me) {
2227            dragStarted = true;
2228        }
2229
2230        public void mousePressed(MouseEvent e) {
2231            JTextComponent c = (JTextComponent)e.getSource();
2232            if (c.getDragEnabled()) {
2233                dragStarted = false;
2234                if (isDragPossible(e) && DragRecognitionSupport.mousePressed(e)) {
2235                    e.consume();
2236                }
2237            }
2238        }
2239
2240        public void mouseReleased(MouseEvent e) {
2241            JTextComponent c = (JTextComponent)e.getSource();
2242            if (c.getDragEnabled()) {
2243                if (dragStarted) {
2244                    e.consume();
2245                }
2246
2247                DragRecognitionSupport.mouseReleased(e);
2248            }
2249        }
2250
2251        public void mouseDragged(MouseEvent e) {
2252            JTextComponent c = (JTextComponent)e.getSource();
2253            if (c.getDragEnabled()) {
2254                if (dragStarted || DragRecognitionSupport.mouseDragged(e, this)) {
2255                    e.consume();
2256                }
2257            }
2258        }
2259
2260        /**
2261         * Determines if the following are true:
2262         * <ul>
2263         * <li>the component is enabled
2264         * <li>the press event is located over a selection
2265         * </ul>
2266         */
2267        @SuppressWarnings("deprecation")
2268        protected boolean isDragPossible(MouseEvent e) {
2269            JTextComponent c = (JTextComponent)e.getSource();
2270            if (c.isEnabled()) {
2271                Caret caret = c.getCaret();
2272                int dot = caret.getDot();
2273                int mark = caret.getMark();
2274                if (dot != mark) {
2275                    Point p = new Point(e.getX(), e.getY());
2276                    int pos = c.viewToModel(p);
2277
2278                    int p0 = Math.min(dot, mark);
2279                    int p1 = Math.max(dot, mark);
2280                    if ((pos >= p0) && (pos < p1)) {
2281                        return true;
2282                    }
2283                }
2284            }
2285            return false;
2286        }
2287    }
2288
2289    static class TextTransferHandler extends TransferHandler implements UIResource {
2290
2291        private JTextComponent exportComp;
2292        private boolean shouldRemove;
2293        private int p0;
2294        private int p1;
2295
2296        /**
2297         * Whether or not this is a drop using
2298         * <code>DropMode.INSERT</code>.
2299         */
2300        private boolean modeBetween = false;
2301
2302        /**
2303         * Whether or not this is a drop.
2304         */
2305        private boolean isDrop = false;
2306
2307        /**
2308         * The drop action.
2309         */
2310        private int dropAction = MOVE;
2311
2312        /**
2313         * The drop bias.
2314         */
2315        private Position.Bias dropBias;
2316
2317        /**
2318         * Try to find a flavor that can be used to import a Transferable.
2319         * The set of usable flavors are tried in the following order:
2320         * <ol>
2321         *     <li>First, an attempt is made to find a flavor matching the content type
2322         *         of the EditorKit for the component.
2323         *     <li>Second, an attempt to find a text/plain flavor is made.
2324         *     <li>Third, an attempt to find a flavor representing a String reference
2325         *         in the same VM is made.
2326         *     <li>Lastly, DataFlavor.stringFlavor is searched for.
2327         * </ol>
2328         */
2329        protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
2330            DataFlavor plainFlavor = null;
2331            DataFlavor refFlavor = null;
2332            DataFlavor stringFlavor = null;
2333
2334            if (c instanceof JEditorPane) {
2335                for (int i = 0; i < flavors.length; i++) {
2336                    String mime = flavors[i].getMimeType();
2337                    if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) {
2338                        return flavors[i];
2339                    } else if (plainFlavor == null && mime.startsWith("text/plain")) {
2340                        plainFlavor = flavors[i];
2341                    } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2342                                                 && flavors[i].getRepresentationClass() == java.lang.String.class) {
2343                        refFlavor = flavors[i];
2344                    } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2345                        stringFlavor = flavors[i];
2346                    }
2347                }
2348                if (plainFlavor != null) {
2349                    return plainFlavor;
2350                } else if (refFlavor != null) {
2351                    return refFlavor;
2352                } else if (stringFlavor != null) {
2353                    return stringFlavor;
2354                }
2355                return null;
2356            }
2357
2358
2359            for (int i = 0; i < flavors.length; i++) {
2360                String mime = flavors[i].getMimeType();
2361                if (mime.startsWith("text/plain")) {
2362                    return flavors[i];
2363                } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2364                                             && flavors[i].getRepresentationClass() == java.lang.String.class) {
2365                    refFlavor = flavors[i];
2366                } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2367                    stringFlavor = flavors[i];
2368                }
2369            }
2370            if (refFlavor != null) {
2371                return refFlavor;
2372            } else if (stringFlavor != null) {
2373                return stringFlavor;
2374            }
2375            return null;
2376        }
2377
2378        /**
2379         * Import the given stream data into the text component.
2380         */
2381        protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)
2382                                               throws BadLocationException, IOException {
2383            if (useRead) {
2384                int startPosition = c.getSelectionStart();
2385                int endPosition = c.getSelectionEnd();
2386                int length = endPosition - startPosition;
2387                EditorKit kit = c.getUI().getEditorKit(c);
2388                Document doc = c.getDocument();
2389                if (length > 0) {
2390                    doc.remove(startPosition, length);
2391                }
2392                kit.read(in, doc, startPosition);
2393            } else {
2394                char[] buff = new char[1024];
2395                int nch;
2396                boolean lastWasCR = false;
2397                int last;
2398                StringBuffer sbuff = null;
2399
2400                // Read in a block at a time, mapping \r\n to \n, as well as single
2401                // \r to \n.
2402                while ((nch = in.read(buff, 0, buff.length)) != -1) {
2403                    if (sbuff == null) {
2404                        sbuff = new StringBuffer(nch);
2405                    }
2406                    last = 0;
2407                    for(int counter = 0; counter < nch; counter++) {
2408                        switch(buff[counter]) {
2409                        case '\r':
2410                            if (lastWasCR) {
2411                                if (counter == 0) {
2412                                    sbuff.append('\n');
2413                                } else {
2414                                    buff[counter - 1] = '\n';
2415                                }
2416                            } else {
2417                                lastWasCR = true;
2418                            }
2419                            break;
2420                        case '\n':
2421                            if (lastWasCR) {
2422                                if (counter > (last + 1)) {
2423                                    sbuff.append(buff, last, counter - last - 1);
2424                                }
2425                                // else nothing to do, can skip \r, next write will
2426                                // write \n
2427                                lastWasCR = false;
2428                                last = counter;
2429                            }
2430                            break;
2431                        default:
2432                            if (lastWasCR) {
2433                                if (counter == 0) {
2434                                    sbuff.append('\n');
2435                                } else {
2436                                    buff[counter - 1] = '\n';
2437                                }
2438                                lastWasCR = false;
2439                            }
2440                            break;
2441                        }
2442                    }
2443                    if (last < nch) {
2444                        if (lastWasCR) {
2445                            if (last < (nch - 1)) {
2446                                sbuff.append(buff, last, nch - last - 1);
2447                            }
2448                        } else {
2449                            sbuff.append(buff, last, nch - last);
2450                        }
2451                    }
2452                }
2453                if (lastWasCR) {
2454                    sbuff.append('\n');
2455                }
2456                c.replaceSelection(sbuff != null ? sbuff.toString() : "");
2457            }
2458        }
2459
2460        // --- TransferHandler methods ------------------------------------
2461
2462        /**
2463         * This is the type of transfer actions supported by the source.  Some models are
2464         * not mutable, so a transfer operation of COPY only should
2465         * be advertised in that case.
2466         *
2467         * @param c  The component holding the data to be transfered.  This
2468         *  argument is provided to enable sharing of TransferHandlers by
2469         *  multiple components.
2470         * @return  This is implemented to return NONE if the component is a JPasswordField
2471         *  since exporting data via user gestures is not allowed.  If the text component is
2472         *  editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed.
2473         */
2474        public int getSourceActions(JComponent c) {
2475            if (c instanceof JPasswordField &&
2476                c.getClientProperty("JPasswordField.cutCopyAllowed") !=
2477                Boolean.TRUE) {
2478                return NONE;
2479            }
2480
2481            return ((JTextComponent)c).isEditable() ? COPY_OR_MOVE : COPY;
2482        }
2483
2484        /**
2485         * Create a Transferable to use as the source for a data transfer.
2486         *
2487         * @param comp  The component holding the data to be transfered.  This
2488         *  argument is provided to enable sharing of TransferHandlers by
2489         *  multiple components.
2490         * @return  The representation of the data to be transfered.
2491         *
2492         */
2493        protected Transferable createTransferable(JComponent comp) {
2494            exportComp = (JTextComponent)comp;
2495            shouldRemove = true;
2496            p0 = exportComp.getSelectionStart();
2497            p1 = exportComp.getSelectionEnd();
2498            return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;
2499        }
2500
2501        /**
2502         * This method is called after data has been exported.  This method should remove
2503         * the data that was transfered if the action was MOVE.
2504         *
2505         * @param source The component that was the source of the data.
2506         * @param data   The data that was transferred or possibly null
2507         *               if the action is <code>NONE</code>.
2508         * @param action The actual action that was performed.
2509         */
2510        protected void exportDone(JComponent source, Transferable data, int action) {
2511            // only remove the text if shouldRemove has not been set to
2512            // false by importData and only if the action is a move
2513            if (shouldRemove && action == MOVE) {
2514                TextTransferable t = (TextTransferable)data;
2515                t.removeText();
2516            }
2517
2518            exportComp = null;
2519        }
2520
2521        public boolean importData(TransferSupport support) {
2522            isDrop = support.isDrop();
2523
2524            if (isDrop) {
2525                modeBetween =
2526                    ((JTextComponent)support.getComponent()).getDropMode() == DropMode.INSERT;
2527
2528                dropBias = ((JTextComponent.DropLocation)support.getDropLocation()).getBias();
2529
2530                dropAction = support.getDropAction();
2531            }
2532
2533            try {
2534                return super.importData(support);
2535            } finally {
2536                isDrop = false;
2537                modeBetween = false;
2538                dropBias = null;
2539                dropAction = MOVE;
2540            }
2541        }
2542
2543        /**
2544         * This method causes a transfer to a component from a clipboard or a
2545         * DND drop operation.  The Transferable represents the data to be
2546         * imported into the component.
2547         *
2548         * @param comp  The component to receive the transfer.  This
2549         *  argument is provided to enable sharing of TransferHandlers by
2550         *  multiple components.
2551         * @param t     The data to import
2552         * @return  true if the data was inserted into the component, false otherwise.
2553         */
2554        public boolean importData(JComponent comp, Transferable t) {
2555            JTextComponent c = (JTextComponent)comp;
2556
2557            int pos = modeBetween
2558                      ? c.getDropLocation().getIndex() : c.getCaretPosition();
2559
2560            // if we are importing to the same component that we exported from
2561            // then don't actually do anything if the drop location is inside
2562            // the drag location and set shouldRemove to false so that exportDone
2563            // knows not to remove any data
2564            if (dropAction == MOVE && c == exportComp && pos >= p0 && pos <= p1) {
2565                shouldRemove = false;
2566                return true;
2567            }
2568
2569            boolean imported = false;
2570            DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
2571            if (importFlavor != null) {
2572                try {
2573                    boolean useRead = false;
2574                    if (comp instanceof JEditorPane) {
2575                        JEditorPane ep = (JEditorPane)comp;
2576                        if (!ep.getContentType().startsWith("text/plain") &&
2577                                importFlavor.getMimeType().startsWith(ep.getContentType())) {
2578                            useRead = true;
2579                        }
2580                    }
2581                    InputContext ic = c.getInputContext();
2582                    if (ic != null) {
2583                        ic.endComposition();
2584                    }
2585                    Reader r = importFlavor.getReaderForText(t);
2586
2587                    if (modeBetween) {
2588                        Caret caret = c.getCaret();
2589                        if (caret instanceof DefaultCaret) {
2590                            ((DefaultCaret)caret).setDot(pos, dropBias);
2591                        } else {
2592                            c.setCaretPosition(pos);
2593                        }
2594                    }
2595
2596                    handleReaderImport(r, c, useRead);
2597
2598                    if (isDrop) {
2599                        c.requestFocus();
2600                        Caret caret = c.getCaret();
2601                        if (caret instanceof DefaultCaret) {
2602                            int newPos = caret.getDot();
2603                            Position.Bias newBias = ((DefaultCaret)caret).getDotBias();
2604
2605                            ((DefaultCaret)caret).setDot(pos, dropBias);
2606                            ((DefaultCaret)caret).moveDot(newPos, newBias);
2607                        } else {
2608                            c.select(pos, c.getCaretPosition());
2609                        }
2610                    }
2611
2612                    imported = true;
2613                } catch (UnsupportedFlavorException ufe) {
2614                } catch (BadLocationException ble) {
2615                } catch (IOException ioe) {
2616                }
2617            }
2618            return imported;
2619        }
2620
2621        /**
2622         * This method indicates if a component would accept an import of the given
2623         * set of data flavors prior to actually attempting to import it.
2624         *
2625         * @param comp  The component to receive the transfer.  This
2626         *  argument is provided to enable sharing of TransferHandlers by
2627         *  multiple components.
2628         * @param flavors  The data formats available
2629         * @return  true if the data can be inserted into the component, false otherwise.
2630         */
2631        public boolean canImport(JComponent comp, DataFlavor[] flavors) {
2632            JTextComponent c = (JTextComponent)comp;
2633            if (!(c.isEditable() && c.isEnabled())) {
2634                return false;
2635            }
2636            return (getImportFlavor(flavors, c) != null);
2637        }
2638
2639        /**
2640         * A possible implementation of the Transferable interface
2641         * for text components.  For a JEditorPane with a rich set
2642         * of EditorKit implementations, conversions could be made
2643         * giving a wider set of formats.  This is implemented to
2644         * offer up only the active content type and text/plain
2645         * (if that is not the active format) since that can be
2646         * extracted from other formats.
2647         */
2648        static class TextTransferable extends BasicTransferable {
2649
2650            TextTransferable(JTextComponent c, int start, int end) {
2651                super(null, null);
2652
2653                this.c = c;
2654
2655                Document doc = c.getDocument();
2656
2657                try {
2658                    p0 = doc.createPosition(start);
2659                    p1 = doc.createPosition(end);
2660
2661                    plainData = c.getSelectedText();
2662
2663                    if (c instanceof JEditorPane) {
2664                        JEditorPane ep = (JEditorPane)c;
2665
2666                        mimeType = ep.getContentType();
2667
2668                        if (mimeType.startsWith("text/plain")) {
2669                            return;
2670                        }
2671
2672                        StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());
2673                        ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());
2674
2675                        if (mimeType.startsWith("text/html")) {
2676                            htmlData = sw.toString();
2677                        } else {
2678                            richText = sw.toString();
2679                        }
2680                    }
2681                } catch (BadLocationException ble) {
2682                } catch (IOException ioe) {
2683                }
2684            }
2685
2686            void removeText() {
2687                if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
2688                    try {
2689                        Document doc = c.getDocument();
2690                        doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
2691                    } catch (BadLocationException e) {
2692                    }
2693                }
2694            }
2695
2696            // ---- EditorKit other than plain or HTML text -----------------------
2697
2698            /**
2699             * If the EditorKit is not for text/plain or text/html, that format
2700             * is supported through the "richer flavors" part of BasicTransferable.
2701             */
2702            protected DataFlavor[] getRicherFlavors() {
2703                if (richText == null) {
2704                    return null;
2705                }
2706
2707                try {
2708                    DataFlavor[] flavors = new DataFlavor[3];
2709                    flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String");
2710                    flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader");
2711                    flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode");
2712                    return flavors;
2713                } catch (ClassNotFoundException cle) {
2714                    // fall through to unsupported (should not happen)
2715                }
2716
2717                return null;
2718            }
2719
2720            /**
2721             * The only richer format supported is the file list flavor
2722             */
2723            @SuppressWarnings("deprecation")
2724            protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
2725                if (richText == null) {
2726                    return null;
2727                }
2728
2729                if (String.class.equals(flavor.getRepresentationClass())) {
2730                    return richText;
2731                } else if (Reader.class.equals(flavor.getRepresentationClass())) {
2732                    return new StringReader(richText);
2733                } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
2734                    return new StringBufferInputStream(richText);
2735                }
2736                throw new UnsupportedFlavorException(flavor);
2737            }
2738
2739            Position p0;
2740            Position p1;
2741            String mimeType;
2742            String richText;
2743            JTextComponent c;
2744        }
2745
2746    }
2747
2748}
2749