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