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