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