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