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