1 /*
   2  * Copyright (c) 1997, 2016, 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> NOTE: 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      * <font style="color: red;"><b>NOTE:</b>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      * </font>
1294      *
1295      * @param tc the text component for which this UI is installed
1296      * @return the view
1297      * @see TextUI#getRootView
1298      */
1299     public View getRootView(JTextComponent tc) {
1300         return rootView;
1301     }
1302 
1303 
1304     /**
1305      * Returns the string to be used as the tooltip at the passed in location.
1306      * This forwards the method onto the root View.
1307      *
1308      * @see javax.swing.text.JTextComponent#getToolTipText
1309      * @see javax.swing.text.View#getToolTipText
1310      * @since 1.4
1311      */
1312     @SuppressWarnings("deprecation")
1313     public String getToolTipText(JTextComponent t, Point pt) {
1314         if (!painted) {
1315             return null;
1316         }
1317         Document doc = editor.getDocument();
1318         String tt = null;
1319         Rectangle alloc = getVisibleEditorRect();
1320 
1321         if (alloc != null) {
1322             if (doc instanceof AbstractDocument) {
1323                 ((AbstractDocument)doc).readLock();
1324             }
1325             try {
1326                 tt = rootView.getToolTipText(pt.x, pt.y, alloc);
1327             } finally {
1328                 if (doc instanceof AbstractDocument) {
1329                     ((AbstractDocument)doc).readUnlock();
1330                 }
1331             }
1332         }
1333         return tt;
1334     }
1335 
1336     // --- ViewFactory methods ------------------------------
1337 
1338     /**
1339      * Creates a view for an element.
1340      * If a subclass wishes to directly implement the factory
1341      * producing the view(s), it should reimplement this
1342      * method.  By default it simply returns null indicating
1343      * it is unable to represent the element.
1344      *
1345      * @param elem the element
1346      * @return the view
1347      */
1348     public View create(Element elem) {
1349         return null;
1350     }
1351 
1352     /**
1353      * Creates a view for an element.
1354      * If a subclass wishes to directly implement the factory
1355      * producing the view(s), it should reimplement this
1356      * method.  By default it simply returns null indicating
1357      * it is unable to represent the part of the element.
1358      *
1359      * @param elem the element
1360      * @param p0 the starting offset &gt;= 0
1361      * @param p1 the ending offset &gt;= p0
1362      * @return the view
1363      */
1364     public View create(Element elem, int p0, int p1) {
1365         return null;
1366     }
1367 
1368     /**
1369      * Default implementation of the interface {@code Caret}.
1370      */
1371     public static class BasicCaret extends DefaultCaret implements UIResource {}
1372 
1373     /**
1374      * Default implementation of the interface {@code Highlighter}.
1375      */
1376     public static class BasicHighlighter extends DefaultHighlighter implements UIResource {}
1377 
1378     static class BasicCursor extends Cursor implements UIResource {
1379         BasicCursor(int type) {
1380             super(type);
1381         }
1382 
1383         BasicCursor(String name) {
1384             super(name);
1385         }
1386     }
1387 
1388     private static BasicCursor textCursor = new BasicCursor(Cursor.TEXT_CURSOR);
1389     // ----- member variables ---------------------------------------
1390 
1391     private static final EditorKit defaultKit = new DefaultEditorKit();
1392     transient JTextComponent editor;
1393     transient boolean painted;
1394     transient RootView rootView = new RootView();
1395     transient UpdateHandler updateHandler = new UpdateHandler();
1396     private static final TransferHandler defaultTransferHandler = new TextTransferHandler();
1397     private final DragListener dragListener = getDragListener();
1398     private static final Position.Bias[] discardBias = new Position.Bias[1];
1399     private DefaultCaret dropCaret;
1400     private int caretMargin;
1401     private boolean rootViewInitialized;
1402 
1403     /**
1404      * Root view that acts as a gateway between the component
1405      * and the View hierarchy.
1406      */
1407     class RootView extends View {
1408 
1409         RootView() {
1410             super(null);
1411         }
1412 
1413         void setView(View v) {
1414             View oldView = view;
1415             view = null;
1416             if (oldView != null) {
1417                 // get rid of back reference so that the old
1418                 // hierarchy can be garbage collected.
1419                 oldView.setParent(null);
1420             }
1421             if (v != null) {
1422                 v.setParent(this);
1423             }
1424             view = v;
1425         }
1426 
1427         /**
1428          * Fetches the attributes to use when rendering.  At the root
1429          * level there are no attributes.  If an attribute is resolved
1430          * up the view hierarchy this is the end of the line.
1431          */
1432         public AttributeSet getAttributes() {
1433             return null;
1434         }
1435 
1436         /**
1437          * Determines the preferred span for this view along an axis.
1438          *
1439          * @param axis may be either X_AXIS or Y_AXIS
1440          * @return the span the view would like to be rendered into.
1441          *         Typically the view is told to render into the span
1442          *         that is returned, although there is no guarantee.
1443          *         The parent may choose to resize or break the view.
1444          */
1445         public float getPreferredSpan(int axis) {
1446             if (view != null) {
1447                 return view.getPreferredSpan(axis);
1448             }
1449             return 10;
1450         }
1451 
1452         /**
1453          * Determines the minimum span for this view along an axis.
1454          *
1455          * @param axis may be either X_AXIS or Y_AXIS
1456          * @return the span the view would like to be rendered into.
1457          *         Typically the view is told to render into the span
1458          *         that is returned, although there is no guarantee.
1459          *         The parent may choose to resize or break the view.
1460          */
1461         public float getMinimumSpan(int axis) {
1462             if (view != null) {
1463                 return view.getMinimumSpan(axis);
1464             }
1465             return 10;
1466         }
1467 
1468         /**
1469          * Determines the maximum span for this view along an axis.
1470          *
1471          * @param axis may be either X_AXIS or Y_AXIS
1472          * @return the span the view would like to be rendered into.
1473          *         Typically the view is told to render into the span
1474          *         that is returned, although there is no guarantee.
1475          *         The parent may choose to resize or break the view.
1476          */
1477         public float getMaximumSpan(int axis) {
1478             return Integer.MAX_VALUE;
1479         }
1480 
1481         /**
1482          * Specifies that a preference has changed.
1483          * Child views can call this on the parent to indicate that
1484          * the preference has changed.  The root view routes this to
1485          * invalidate on the hosting component.
1486          * <p>
1487          * This can be called on a different thread from the
1488          * event dispatching thread and is basically unsafe to
1489          * propagate into the component.  To make this safe,
1490          * the operation is transferred over to the event dispatching
1491          * thread for completion.  It is a design goal that all view
1492          * methods be safe to call without concern for concurrency,
1493          * and this behavior helps make that true.
1494          *
1495          * @param child the child view
1496          * @param width true if the width preference has changed
1497          * @param height true if the height preference has changed
1498          */
1499         public void preferenceChanged(View child, boolean width, boolean height) {
1500             editor.revalidate();
1501         }
1502 
1503         /**
1504          * Determines the desired alignment for this view along an axis.
1505          *
1506          * @param axis may be either X_AXIS or Y_AXIS
1507          * @return the desired alignment, where 0.0 indicates the origin
1508          *     and 1.0 the full span away from the origin
1509          */
1510         public float getAlignment(int axis) {
1511             if (view != null) {
1512                 return view.getAlignment(axis);
1513             }
1514             return 0;
1515         }
1516 
1517         /**
1518          * Renders the view.
1519          *
1520          * @param g the graphics context
1521          * @param allocation the region to render into
1522          */
1523         public void paint(Graphics g, Shape allocation) {
1524             if (view != null) {
1525                 Rectangle alloc = (allocation instanceof Rectangle) ?
1526                           (Rectangle)allocation : allocation.getBounds();
1527                 setSize(alloc.width, alloc.height);
1528                 view.paint(g, allocation);
1529             }
1530         }
1531 
1532         /**
1533          * Sets the view parent.
1534          *
1535          * @param parent the parent view
1536          */
1537         public void setParent(View parent) {
1538             throw new Error("Can't set parent on root view");
1539         }
1540 
1541         /**
1542          * Returns the number of views in this view.  Since
1543          * this view simply wraps the root of the view hierarchy
1544          * it has exactly one child.
1545          *
1546          * @return the number of views
1547          * @see #getView
1548          */
1549         public int getViewCount() {
1550             return 1;
1551         }
1552 
1553         /**
1554          * Gets the n-th view in this container.
1555          *
1556          * @param n the number of the view to get
1557          * @return the view
1558          */
1559         public View getView(int n) {
1560             return view;
1561         }
1562 
1563         /**
1564          * Returns the child view index representing the given position in
1565          * the model.  This is implemented to return the index of the only
1566          * child.
1567          *
1568          * @param pos the position &gt;= 0
1569          * @return  index of the view representing the given position, or
1570          *   -1 if no view represents that position
1571          * @since 1.3
1572          */
1573         public int getViewIndex(int pos, Position.Bias b) {
1574             return 0;
1575         }
1576 
1577         /**
1578          * Fetches the allocation for the given child view.
1579          * This enables finding out where various views
1580          * are located, without assuming the views store
1581          * their location.  This returns the given allocation
1582          * since this view simply acts as a gateway between
1583          * the view hierarchy and the associated component.
1584          *
1585          * @param index the index of the child
1586          * @param a  the allocation to this view.
1587          * @return the allocation to the child
1588          */
1589         public Shape getChildAllocation(int index, Shape a) {
1590             return a;
1591         }
1592 
1593         /**
1594          * Provides a mapping from the document model coordinate space
1595          * to the coordinate space of the view mapped to it.
1596          *
1597          * @param pos the position to convert
1598          * @param a the allocated region to render into
1599          * @return the bounding box of the given position
1600          */
1601         public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
1602             if (view != null) {
1603                 return view.modelToView(pos, a, b);
1604             }
1605             return null;
1606         }
1607 
1608         /**
1609          * Provides a mapping from the document model coordinate space
1610          * to the coordinate space of the view mapped to it.
1611          *
1612          * @param p0 the position to convert &gt;= 0
1613          * @param b0 the bias toward the previous character or the
1614          *  next character represented by p0, in case the
1615          *  position is a boundary of two views.
1616          * @param p1 the position to convert &gt;= 0
1617          * @param b1 the bias toward the previous character or the
1618          *  next character represented by p1, in case the
1619          *  position is a boundary of two views.
1620          * @param a the allocated region to render into
1621          * @return the bounding box of the given position is returned
1622          * @exception BadLocationException  if the given position does
1623          *   not represent a valid location in the associated document
1624          * @exception IllegalArgumentException for an invalid bias argument
1625          * @see View#viewToModel
1626          */
1627         public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
1628             if (view != null) {
1629                 return view.modelToView(p0, b0, p1, b1, a);
1630             }
1631             return null;
1632         }
1633 
1634         /**
1635          * Provides a mapping from the view coordinate space to the logical
1636          * coordinate space of the model.
1637          *
1638          * @param x x coordinate of the view location to convert
1639          * @param y y coordinate of the view location to convert
1640          * @param a the allocated region to render into
1641          * @return the location within the model that best represents the
1642          *    given point in the view
1643          */
1644         public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
1645             if (view != null) {
1646                 int retValue = view.viewToModel(x, y, a, bias);
1647                 return retValue;
1648             }
1649             return -1;
1650         }
1651 
1652         /**
1653          * Provides a way to determine the next visually represented model
1654          * location that one might place a caret.  Some views may not be visible,
1655          * they might not be in the same order found in the model, or they just
1656          * might not allow access to some of the locations in the model.
1657          * This method enables specifying a position to convert
1658          * within the range of &gt;=0.  If the value is -1, a position
1659          * will be calculated automatically.  If the value &lt; -1,
1660          * the {@code BadLocationException} will be thrown.
1661          *
1662          * @param pos the position to convert &gt;= 0
1663          * @param a the allocated region to render into
1664          * @param direction the direction from the current position that can
1665          *  be thought of as the arrow keys typically found on a keyboard.
1666          *  This may be SwingConstants.WEST, SwingConstants.EAST,
1667          *  SwingConstants.NORTH, or SwingConstants.SOUTH.
1668          * @return the location within the model that best represents the next
1669          *  location visual position.
1670          * @exception BadLocationException the given position is not a valid
1671          *                                 position within the document
1672          * @exception IllegalArgumentException for an invalid direction
1673          */
1674         public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
1675                                              int direction,
1676                                              Position.Bias[] biasRet)
1677             throws BadLocationException {
1678             if (pos < -1 || pos > getDocument().getLength()) {
1679                 throw new BadLocationException("invalid position", pos);
1680             }
1681             if( view != null ) {
1682                 int nextPos = view.getNextVisualPositionFrom(pos, b, a,
1683                                                      direction, biasRet);
1684                 if(nextPos != -1) {
1685                     pos = nextPos;
1686                 }
1687                 else {
1688                     biasRet[0] = b;
1689                 }
1690             }
1691             return pos;
1692         }
1693 
1694         /**
1695          * Gives notification that something was inserted into the document
1696          * in a location that this view is responsible for.
1697          *
1698          * @param e the change information from the associated document
1699          * @param a the current allocation of the view
1700          * @param f the factory to use to rebuild if the view has children
1701          */
1702         public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1703             if (view != null) {
1704                 view.insertUpdate(e, a, f);
1705             }
1706         }
1707 
1708         /**
1709          * Gives notification that something was removed from the document
1710          * in a location that this view is responsible for.
1711          *
1712          * @param e the change information from the associated document
1713          * @param a the current allocation of the view
1714          * @param f the factory to use to rebuild if the view has children
1715          */
1716         public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1717             if (view != null) {
1718                 view.removeUpdate(e, a, f);
1719             }
1720         }
1721 
1722         /**
1723          * Gives notification from the document that attributes were changed
1724          * in a location that this view is responsible for.
1725          *
1726          * @param e the change information from the associated document
1727          * @param a the current allocation of the view
1728          * @param f the factory to use to rebuild if the view has children
1729          */
1730         public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1731             if (view != null) {
1732                 view.changedUpdate(e, a, f);
1733             }
1734         }
1735 
1736         /**
1737          * Returns the document model underlying the view.
1738          *
1739          * @return the model
1740          */
1741         public Document getDocument() {
1742             return editor.getDocument();
1743         }
1744 
1745         /**
1746          * Returns the starting offset into the model for this view.
1747          *
1748          * @return the starting offset
1749          */
1750         public int getStartOffset() {
1751             if (view != null) {
1752                 return view.getStartOffset();
1753             }
1754             return getElement().getStartOffset();
1755         }
1756 
1757         /**
1758          * Returns the ending offset into the model for this view.
1759          *
1760          * @return the ending offset
1761          */
1762         public int getEndOffset() {
1763             if (view != null) {
1764                 return view.getEndOffset();
1765             }
1766             return getElement().getEndOffset();
1767         }
1768 
1769         /**
1770          * Gets the element that this view is mapped to.
1771          *
1772          * @return the view
1773          */
1774         public Element getElement() {
1775             if (view != null) {
1776                 return view.getElement();
1777             }
1778             return editor.getDocument().getDefaultRootElement();
1779         }
1780 
1781         /**
1782          * Breaks this view on the given axis at the given length.
1783          *
1784          * @param axis may be either X_AXIS or Y_AXIS
1785          * @param len specifies where a break is desired in the span
1786          * @param the current allocation of the view
1787          * @return the fragment of the view that represents the given span
1788          *   if the view can be broken, otherwise null
1789          */
1790         public View breakView(int axis, float len, Shape a) {
1791             throw new Error("Can't break root view");
1792         }
1793 
1794         /**
1795          * Determines the resizability of the view along the
1796          * given axis.  A value of 0 or less is not resizable.
1797          *
1798          * @param axis may be either X_AXIS or Y_AXIS
1799          * @return the weight
1800          */
1801         public int getResizeWeight(int axis) {
1802             if (view != null) {
1803                 return view.getResizeWeight(axis);
1804             }
1805             return 0;
1806         }
1807 
1808         /**
1809          * Sets the view size.
1810          *
1811          * @param width the width
1812          * @param height the height
1813          */
1814         public void setSize(float width, float height) {
1815             if (view != null) {
1816                 view.setSize(width, height);
1817             }
1818         }
1819 
1820         /**
1821          * Fetches the container hosting the view.  This is useful for
1822          * things like scheduling a repaint, finding out the host
1823          * components font, etc.  The default implementation
1824          * of this is to forward the query to the parent view.
1825          *
1826          * @return the container
1827          */
1828         public Container getContainer() {
1829             return editor;
1830         }
1831 
1832         /**
1833          * Fetches the factory to be used for building the
1834          * various view fragments that make up the view that
1835          * represents the model.  This is what determines
1836          * how the model will be represented.  This is implemented
1837          * to fetch the factory provided by the associated
1838          * EditorKit unless that is null, in which case this
1839          * simply returns the BasicTextUI itself which allows
1840          * subclasses to implement a simple factory directly without
1841          * creating extra objects.
1842          *
1843          * @return the factory
1844          */
1845         public ViewFactory getViewFactory() {
1846             EditorKit kit = getEditorKit(editor);
1847             ViewFactory f = kit.getViewFactory();
1848             if (f != null) {
1849                 return f;
1850             }
1851             return BasicTextUI.this;
1852         }
1853 
1854         private View view;
1855 
1856     }
1857 
1858     /**
1859      * Handles updates from various places.  If the model is changed,
1860      * this class unregisters as a listener to the old model and
1861      * registers with the new model.  If the document model changes,
1862      * the change is forwarded to the root view.  If the focus
1863      * accelerator changes, a new keystroke is registered to request
1864      * focus.
1865      */
1866     class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, UIResource {
1867 
1868         // --- PropertyChangeListener methods -----------------------
1869 
1870         /**
1871          * This method gets called when a bound property is changed.
1872          * We are looking for document changes on the editor.
1873          */
1874         public final void propertyChange(PropertyChangeEvent evt) {
1875             Object oldValue = evt.getOldValue();
1876             Object newValue = evt.getNewValue();
1877             String propertyName = evt.getPropertyName();
1878             if ((oldValue instanceof Document) || (newValue instanceof Document)) {
1879                 if (oldValue != null) {
1880                     ((Document)oldValue).removeDocumentListener(this);
1881                     i18nView = false;
1882                 }
1883                 if (newValue != null) {
1884                     ((Document)newValue).addDocumentListener(this);
1885                     if ("document" == propertyName) {
1886                         setView(null);
1887                         BasicTextUI.this.propertyChange(evt);
1888                         modelChanged();
1889                         return;
1890                     }
1891                 }
1892                 modelChanged();
1893             }
1894             if ("focusAccelerator" == propertyName) {
1895                 updateFocusAcceleratorBinding(true);
1896             } else if ("componentOrientation" == propertyName) {
1897                 // Changes in ComponentOrientation require the views to be
1898                 // rebuilt.
1899                 modelChanged();
1900             } else if ("font" == propertyName) {
1901                 modelChanged();
1902             } else if ("dropLocation" == propertyName) {
1903                 dropIndexChanged();
1904             } else if ("editable" == propertyName) {
1905                 updateCursor();
1906                 modelChanged();
1907             }
1908             BasicTextUI.this.propertyChange(evt);
1909         }
1910 
1911         private void dropIndexChanged() {
1912             if (editor.getDropMode() == DropMode.USE_SELECTION) {
1913                 return;
1914             }
1915 
1916             JTextComponent.DropLocation dropLocation = editor.getDropLocation();
1917 
1918             if (dropLocation == null) {
1919                 if (dropCaret != null) {
1920                     dropCaret.deinstall(editor);
1921                     editor.repaint(dropCaret);
1922                     dropCaret = null;
1923                 }
1924             } else {
1925                 if (dropCaret == null) {
1926                     dropCaret = new BasicCaret();
1927                     dropCaret.install(editor);
1928                     dropCaret.setVisible(true);
1929                 }
1930 
1931                 dropCaret.setDot(dropLocation.getIndex(),
1932                                  dropLocation.getBias());
1933             }
1934         }
1935 
1936         // --- DocumentListener methods -----------------------
1937 
1938         /**
1939          * The insert notification.  Gets sent to the root of the view structure
1940          * that represents the portion of the model being represented by the
1941          * editor.  The factory is added as an argument to the update so that
1942          * the views can update themselves in a dynamic (not hardcoded) way.
1943          *
1944          * @param e  The change notification from the currently associated
1945          *  document.
1946          * @see DocumentListener#insertUpdate
1947          */
1948         public final void insertUpdate(DocumentEvent e) {
1949             Document doc = e.getDocument();
1950             Object o = doc.getProperty("i18n");
1951             if (o instanceof Boolean) {
1952                 Boolean i18nFlag = (Boolean) o;
1953                 if (i18nFlag.booleanValue() != i18nView) {
1954                     // i18n flag changed, rebuild the view
1955                     i18nView = i18nFlag.booleanValue();
1956                     modelChanged();
1957                     return;
1958                 }
1959             }
1960 
1961             // normal insert update
1962             Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1963             rootView.insertUpdate(e, alloc, rootView.getViewFactory());
1964         }
1965 
1966         /**
1967          * The remove notification.  Gets sent to the root of the view structure
1968          * that represents the portion of the model being represented by the
1969          * editor.  The factory is added as an argument to the update so that
1970          * the views can update themselves in a dynamic (not hardcoded) way.
1971          *
1972          * @param e  The change notification from the currently associated
1973          *  document.
1974          * @see DocumentListener#removeUpdate
1975          */
1976         public final void removeUpdate(DocumentEvent e) {
1977             Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1978             rootView.removeUpdate(e, alloc, rootView.getViewFactory());
1979         }
1980 
1981         /**
1982          * The change notification.  Gets sent to the root of the view structure
1983          * that represents the portion of the model being represented by the
1984          * editor.  The factory is added as an argument to the update so that
1985          * the views can update themselves in a dynamic (not hardcoded) way.
1986          *
1987          * @param e  The change notification from the currently associated
1988          *  document.
1989          * @see DocumentListener#changedUpdate(DocumentEvent)
1990          */
1991         public final void changedUpdate(DocumentEvent e) {
1992             Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
1993             rootView.changedUpdate(e, alloc, rootView.getViewFactory());
1994         }
1995 
1996         // --- LayoutManager2 methods --------------------------------
1997 
1998         /**
1999          * Adds the specified component with the specified name to
2000          * the layout.
2001          * @param name the component name
2002          * @param comp the component to be added
2003          */
2004         public void addLayoutComponent(String name, Component comp) {
2005             // not supported
2006         }
2007 
2008         /**
2009          * Removes the specified component from the layout.
2010          * @param comp the component to be removed
2011          */
2012         public void removeLayoutComponent(Component comp) {
2013             if (constraints != null) {
2014                 // remove the constraint record
2015                 constraints.remove(comp);
2016             }
2017         }
2018 
2019         /**
2020          * Calculates the preferred size dimensions for the specified
2021          * panel given the components in the specified parent container.
2022          * @param parent the component to be laid out
2023          *
2024          * @see #minimumLayoutSize
2025          */
2026         public Dimension preferredLayoutSize(Container parent) {
2027             // should not be called (JComponent uses UI instead)
2028             return null;
2029         }
2030 
2031         /**
2032          * Calculates the minimum size dimensions for the specified
2033          * panel given the components in the specified parent container.
2034          * @param parent the component to be laid out
2035          * @see #preferredLayoutSize
2036          */
2037         public Dimension minimumLayoutSize(Container parent) {
2038             // should not be called (JComponent uses UI instead)
2039             return null;
2040         }
2041 
2042         /**
2043          * Lays out the container in the specified panel.  This is
2044          * implemented to position all components that were added
2045          * with a View object as a constraint.  The current allocation
2046          * of the associated View is used as the location of the
2047          * component.
2048          * <p>
2049          * A read-lock is acquired on the document to prevent the
2050          * view tree from being modified while the layout process
2051          * is active.
2052          *
2053          * @param parent the component which needs to be laid out
2054          */
2055         public void layoutContainer(Container parent) {
2056             if ((constraints != null) && (! constraints.isEmpty())) {
2057                 Rectangle alloc = getVisibleEditorRect();
2058                 if (alloc != null) {
2059                     Document doc = editor.getDocument();
2060                     if (doc instanceof AbstractDocument) {
2061                         ((AbstractDocument)doc).readLock();
2062                     }
2063                     try {
2064                         rootView.setSize(alloc.width, alloc.height);
2065                         Enumeration<Component> components = constraints.keys();
2066                         while (components.hasMoreElements()) {
2067                             Component comp = components.nextElement();
2068                             View v = (View) constraints.get(comp);
2069                             Shape ca = calculateViewPosition(alloc, v);
2070                             if (ca != null) {
2071                                 Rectangle compAlloc = (ca instanceof Rectangle) ?
2072                                     (Rectangle) ca : ca.getBounds();
2073                                 comp.setBounds(compAlloc);
2074                             }
2075                         }
2076                     } finally {
2077                         if (doc instanceof AbstractDocument) {
2078                             ((AbstractDocument)doc).readUnlock();
2079                         }
2080                     }
2081                 }
2082             }
2083         }
2084 
2085         /**
2086          * Find the Shape representing the given view.
2087          */
2088         Shape calculateViewPosition(Shape alloc, View v) {
2089             int pos = v.getStartOffset();
2090             View child = null;
2091             for (View parent = rootView; (parent != null) && (parent != v); parent = child) {
2092                 int index = parent.getViewIndex(pos, Position.Bias.Forward);
2093                 alloc = parent.getChildAllocation(index, alloc);
2094                 child = parent.getView(index);
2095             }
2096             return (child != null) ? alloc : null;
2097         }
2098 
2099         /**
2100          * Adds the specified component to the layout, using the specified
2101          * constraint object.  We only store those components that were added
2102          * with a constraint that is of type View.
2103          *
2104          * @param comp the component to be added
2105          * @param constraint  where/how the component is added to the layout.
2106          */
2107         public void addLayoutComponent(Component comp, Object constraint) {
2108             if (constraint instanceof View) {
2109                 if (constraints == null) {
2110                     constraints = new Hashtable<Component, Object>(7);
2111                 }
2112                 constraints.put(comp, constraint);
2113             }
2114         }
2115 
2116         /**
2117          * Returns the maximum size of this component.
2118          * @see java.awt.Component#getMinimumSize()
2119          * @see java.awt.Component#getPreferredSize()
2120          * @see LayoutManager
2121          */
2122         public Dimension maximumLayoutSize(Container target) {
2123             // should not be called (JComponent uses UI instead)
2124             return null;
2125         }
2126 
2127         /**
2128          * Returns the alignment along the x axis.  This specifies how
2129          * the component would like to be aligned relative to other
2130          * components.  The value should be a number between 0 and 1
2131          * where 0 represents alignment along the origin, 1 is aligned
2132          * the furthest away from the origin, 0.5 is centered, etc.
2133          */
2134         public float getLayoutAlignmentX(Container target) {
2135             return 0.5f;
2136         }
2137 
2138         /**
2139          * Returns the alignment along the y axis.  This specifies how
2140          * the component would like to be aligned relative to other
2141          * components.  The value should be a number between 0 and 1
2142          * where 0 represents alignment along the origin, 1 is aligned
2143          * the furthest away from the origin, 0.5 is centered, etc.
2144          */
2145         public float getLayoutAlignmentY(Container target) {
2146             return 0.5f;
2147         }
2148 
2149         /**
2150          * Invalidates the layout, indicating that if the layout manager
2151          * has cached information it should be discarded.
2152          */
2153         public void invalidateLayout(Container target) {
2154         }
2155 
2156         /**
2157          * The "layout constraints" for the LayoutManager2 implementation.
2158          * These are View objects for those components that are represented
2159          * by a View in the View tree.
2160          */
2161         private Hashtable<Component, Object> constraints;
2162 
2163         private boolean i18nView = false;
2164     }
2165 
2166     /**
2167      * Wrapper for text actions to return isEnabled false in case editor is non editable
2168      */
2169     class TextActionWrapper extends TextAction {
2170         public TextActionWrapper(TextAction action) {
2171             super((String)action.getValue(Action.NAME));
2172             this.action = action;
2173         }
2174         /**
2175          * The operation to perform when this action is triggered.
2176          *
2177          * @param e the action event
2178          */
2179         public void actionPerformed(ActionEvent e) {
2180             action.actionPerformed(e);
2181         }
2182         public boolean isEnabled() {
2183             return (editor == null || editor.isEditable()) ? action.isEnabled() : false;
2184         }
2185         TextAction action = null;
2186     }
2187 
2188 
2189     /**
2190      * Registered in the ActionMap.
2191      */
2192     class FocusAction extends AbstractAction {
2193 
2194         public void actionPerformed(ActionEvent e) {
2195             editor.requestFocus();
2196         }
2197 
2198         public boolean isEnabled() {
2199             return editor.isEditable();
2200         }
2201     }
2202 
2203     private static DragListener getDragListener() {
2204         synchronized(DragListener.class) {
2205             DragListener listener =
2206                 (DragListener)AppContext.getAppContext().
2207                     get(DragListener.class);
2208 
2209             if (listener == null) {
2210                 listener = new DragListener();
2211                 AppContext.getAppContext().put(DragListener.class, listener);
2212             }
2213 
2214             return listener;
2215         }
2216     }
2217 
2218     /**
2219      * Listens for mouse events for the purposes of detecting drag gestures.
2220      * BasicTextUI will maintain one of these per AppContext.
2221      */
2222     static class DragListener extends MouseInputAdapter
2223                               implements BeforeDrag {
2224 
2225         private boolean dragStarted;
2226 
2227         public void dragStarting(MouseEvent me) {
2228             dragStarted = true;
2229         }
2230 
2231         public void mousePressed(MouseEvent e) {
2232             JTextComponent c = (JTextComponent)e.getSource();
2233             if (c.getDragEnabled()) {
2234                 dragStarted = false;
2235                 if (isDragPossible(e) && DragRecognitionSupport.mousePressed(e)) {
2236                     e.consume();
2237                 }
2238             }
2239         }
2240 
2241         public void mouseReleased(MouseEvent e) {
2242             JTextComponent c = (JTextComponent)e.getSource();
2243             if (c.getDragEnabled()) {
2244                 if (dragStarted) {
2245                     e.consume();
2246                 }
2247 
2248                 DragRecognitionSupport.mouseReleased(e);
2249             }
2250         }
2251 
2252         public void mouseDragged(MouseEvent e) {
2253             JTextComponent c = (JTextComponent)e.getSource();
2254             if (c.getDragEnabled()) {
2255                 if (dragStarted || DragRecognitionSupport.mouseDragged(e, this)) {
2256                     e.consume();
2257                 }
2258             }
2259         }
2260 
2261         /**
2262          * Determines if the following are true:
2263          * <ul>
2264          * <li>the component is enabled
2265          * <li>the press event is located over a selection
2266          * </ul>
2267          */
2268         @SuppressWarnings("deprecation")
2269         protected boolean isDragPossible(MouseEvent e) {
2270             JTextComponent c = (JTextComponent)e.getSource();
2271             if (c.isEnabled()) {
2272                 Caret caret = c.getCaret();
2273                 int dot = caret.getDot();
2274                 int mark = caret.getMark();
2275                 if (dot != mark) {
2276                     Point p = new Point(e.getX(), e.getY());
2277                     int pos = c.viewToModel(p);
2278 
2279                     int p0 = Math.min(dot, mark);
2280                     int p1 = Math.max(dot, mark);
2281                     if ((pos >= p0) && (pos < p1)) {
2282                         return true;
2283                     }
2284                 }
2285             }
2286             return false;
2287         }
2288     }
2289 
2290     static class TextTransferHandler extends TransferHandler implements UIResource {
2291 
2292         private JTextComponent exportComp;
2293         private boolean shouldRemove;
2294         private int p0;
2295         private int p1;
2296 
2297         /**
2298          * Whether or not this is a drop using
2299          * <code>DropMode.INSERT</code>.
2300          */
2301         private boolean modeBetween = false;
2302 
2303         /**
2304          * Whether or not this is a drop.
2305          */
2306         private boolean isDrop = false;
2307 
2308         /**
2309          * The drop action.
2310          */
2311         private int dropAction = MOVE;
2312 
2313         /**
2314          * The drop bias.
2315          */
2316         private Position.Bias dropBias;
2317 
2318         /**
2319          * Try to find a flavor that can be used to import a Transferable.
2320          * The set of usable flavors are tried in the following order:
2321          * <ol>
2322          *     <li>First, an attempt is made to find a flavor matching the content type
2323          *         of the EditorKit for the component.
2324          *     <li>Second, an attempt to find a text/plain flavor is made.
2325          *     <li>Third, an attempt to find a flavor representing a String reference
2326          *         in the same VM is made.
2327          *     <li>Lastly, DataFlavor.stringFlavor is searched for.
2328          * </ol>
2329          */
2330         protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
2331             DataFlavor plainFlavor = null;
2332             DataFlavor refFlavor = null;
2333             DataFlavor stringFlavor = null;
2334 
2335             if (c instanceof JEditorPane) {
2336                 for (int i = 0; i < flavors.length; i++) {
2337                     String mime = flavors[i].getMimeType();
2338                     if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) {
2339                         return flavors[i];
2340                     } else if (plainFlavor == null && mime.startsWith("text/plain")) {
2341                         plainFlavor = flavors[i];
2342                     } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2343                                                  && flavors[i].getRepresentationClass() == java.lang.String.class) {
2344                         refFlavor = flavors[i];
2345                     } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2346                         stringFlavor = flavors[i];
2347                     }
2348                 }
2349                 if (plainFlavor != null) {
2350                     return plainFlavor;
2351                 } else if (refFlavor != null) {
2352                     return refFlavor;
2353                 } else if (stringFlavor != null) {
2354                     return stringFlavor;
2355                 }
2356                 return null;
2357             }
2358 
2359 
2360             for (int i = 0; i < flavors.length; i++) {
2361                 String mime = flavors[i].getMimeType();
2362                 if (mime.startsWith("text/plain")) {
2363                     return flavors[i];
2364                 } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
2365                                              && flavors[i].getRepresentationClass() == java.lang.String.class) {
2366                     refFlavor = flavors[i];
2367                 } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
2368                     stringFlavor = flavors[i];
2369                 }
2370             }
2371             if (refFlavor != null) {
2372                 return refFlavor;
2373             } else if (stringFlavor != null) {
2374                 return stringFlavor;
2375             }
2376             return null;
2377         }
2378 
2379         /**
2380          * Import the given stream data into the text component.
2381          */
2382         protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)
2383                                                throws BadLocationException, IOException {
2384             if (useRead) {
2385                 int startPosition = c.getSelectionStart();
2386                 int endPosition = c.getSelectionEnd();
2387                 int length = endPosition - startPosition;
2388                 EditorKit kit = c.getUI().getEditorKit(c);
2389                 Document doc = c.getDocument();
2390                 if (length > 0) {
2391                     doc.remove(startPosition, length);
2392                 }
2393                 kit.read(in, doc, startPosition);
2394             } else {
2395                 char[] buff = new char[1024];
2396                 int nch;
2397                 boolean lastWasCR = false;
2398                 int last;
2399                 StringBuffer sbuff = null;
2400 
2401                 // Read in a block at a time, mapping \r\n to \n, as well as single
2402                 // \r to \n.
2403                 while ((nch = in.read(buff, 0, buff.length)) != -1) {
2404                     if (sbuff == null) {
2405                         sbuff = new StringBuffer(nch);
2406                     }
2407                     last = 0;
2408                     for(int counter = 0; counter < nch; counter++) {
2409                         switch(buff[counter]) {
2410                         case '\r':
2411                             if (lastWasCR) {
2412                                 if (counter == 0) {
2413                                     sbuff.append('\n');
2414                                 } else {
2415                                     buff[counter - 1] = '\n';
2416                                 }
2417                             } else {
2418                                 lastWasCR = true;
2419                             }
2420                             break;
2421                         case '\n':
2422                             if (lastWasCR) {
2423                                 if (counter > (last + 1)) {
2424                                     sbuff.append(buff, last, counter - last - 1);
2425                                 }
2426                                 // else nothing to do, can skip \r, next write will
2427                                 // write \n
2428                                 lastWasCR = false;
2429                                 last = counter;
2430                             }
2431                             break;
2432                         default:
2433                             if (lastWasCR) {
2434                                 if (counter == 0) {
2435                                     sbuff.append('\n');
2436                                 } else {
2437                                     buff[counter - 1] = '\n';
2438                                 }
2439                                 lastWasCR = false;
2440                             }
2441                             break;
2442                         }
2443                     }
2444                     if (last < nch) {
2445                         if (lastWasCR) {
2446                             if (last < (nch - 1)) {
2447                                 sbuff.append(buff, last, nch - last - 1);
2448                             }
2449                         } else {
2450                             sbuff.append(buff, last, nch - last);
2451                         }
2452                     }
2453                 }
2454                 if (lastWasCR) {
2455                     sbuff.append('\n');
2456                 }
2457                 c.replaceSelection(sbuff != null ? sbuff.toString() : "");
2458             }
2459         }
2460 
2461         // --- TransferHandler methods ------------------------------------
2462 
2463         /**
2464          * This is the type of transfer actions supported by the source.  Some models are
2465          * not mutable, so a transfer operation of COPY only should
2466          * be advertised in that case.
2467          *
2468          * @param c  The component holding the data to be transfered.  This
2469          *  argument is provided to enable sharing of TransferHandlers by
2470          *  multiple components.
2471          * @return  This is implemented to return NONE if the component is a JPasswordField
2472          *  since exporting data via user gestures is not allowed.  If the text component is
2473          *  editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed.
2474          */
2475         public int getSourceActions(JComponent c) {
2476             if (c instanceof JPasswordField &&
2477                 c.getClientProperty("JPasswordField.cutCopyAllowed") !=
2478                 Boolean.TRUE) {
2479                 return NONE;
2480             }
2481 
2482             return ((JTextComponent)c).isEditable() ? COPY_OR_MOVE : COPY;
2483         }
2484 
2485         /**
2486          * Create a Transferable to use as the source for a data transfer.
2487          *
2488          * @param comp  The component holding the data to be transfered.  This
2489          *  argument is provided to enable sharing of TransferHandlers by
2490          *  multiple components.
2491          * @return  The representation of the data to be transfered.
2492          *
2493          */
2494         protected Transferable createTransferable(JComponent comp) {
2495             exportComp = (JTextComponent)comp;
2496             shouldRemove = true;
2497             p0 = exportComp.getSelectionStart();
2498             p1 = exportComp.getSelectionEnd();
2499             return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;
2500         }
2501 
2502         /**
2503          * This method is called after data has been exported.  This method should remove
2504          * the data that was transfered if the action was MOVE.
2505          *
2506          * @param source The component that was the source of the data.
2507          * @param data   The data that was transferred or possibly null
2508          *               if the action is <code>NONE</code>.
2509          * @param action The actual action that was performed.
2510          */
2511         protected void exportDone(JComponent source, Transferable data, int action) {
2512             // only remove the text if shouldRemove has not been set to
2513             // false by importData and only if the action is a move
2514             if (shouldRemove && action == MOVE) {
2515                 TextTransferable t = (TextTransferable)data;
2516                 t.removeText();
2517             }
2518 
2519             exportComp = null;
2520         }
2521 
2522         public boolean importData(TransferSupport support) {
2523             isDrop = support.isDrop();
2524 
2525             if (isDrop) {
2526                 modeBetween =
2527                     ((JTextComponent)support.getComponent()).getDropMode() == DropMode.INSERT;
2528 
2529                 dropBias = ((JTextComponent.DropLocation)support.getDropLocation()).getBias();
2530 
2531                 dropAction = support.getDropAction();
2532             }
2533 
2534             try {
2535                 return super.importData(support);
2536             } finally {
2537                 isDrop = false;
2538                 modeBetween = false;
2539                 dropBias = null;
2540                 dropAction = MOVE;
2541             }
2542         }
2543 
2544         /**
2545          * This method causes a transfer to a component from a clipboard or a
2546          * DND drop operation.  The Transferable represents the data to be
2547          * imported into the component.
2548          *
2549          * @param comp  The component to receive the transfer.  This
2550          *  argument is provided to enable sharing of TransferHandlers by
2551          *  multiple components.
2552          * @param t     The data to import
2553          * @return  true if the data was inserted into the component, false otherwise.
2554          */
2555         public boolean importData(JComponent comp, Transferable t) {
2556             JTextComponent c = (JTextComponent)comp;
2557 
2558             int pos = modeBetween
2559                       ? c.getDropLocation().getIndex() : c.getCaretPosition();
2560 
2561             // if we are importing to the same component that we exported from
2562             // then don't actually do anything if the drop location is inside
2563             // the drag location and set shouldRemove to false so that exportDone
2564             // knows not to remove any data
2565             if (dropAction == MOVE && c == exportComp && pos >= p0 && pos <= p1) {
2566                 shouldRemove = false;
2567                 return true;
2568             }
2569 
2570             boolean imported = false;
2571             DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
2572             if (importFlavor != null) {
2573                 try {
2574                     boolean useRead = false;
2575                     if (comp instanceof JEditorPane) {
2576                         JEditorPane ep = (JEditorPane)comp;
2577                         if (!ep.getContentType().startsWith("text/plain") &&
2578                                 importFlavor.getMimeType().startsWith(ep.getContentType())) {
2579                             useRead = true;
2580                         }
2581                     }
2582                     InputContext ic = c.getInputContext();
2583                     if (ic != null) {
2584                         ic.endComposition();
2585                     }
2586                     Reader r = importFlavor.getReaderForText(t);
2587 
2588                     if (modeBetween) {
2589                         Caret caret = c.getCaret();
2590                         if (caret instanceof DefaultCaret) {
2591                             ((DefaultCaret)caret).setDot(pos, dropBias);
2592                         } else {
2593                             c.setCaretPosition(pos);
2594                         }
2595                     }
2596 
2597                     handleReaderImport(r, c, useRead);
2598 
2599                     if (isDrop) {
2600                         c.requestFocus();
2601                         Caret caret = c.getCaret();
2602                         if (caret instanceof DefaultCaret) {
2603                             int newPos = caret.getDot();
2604                             Position.Bias newBias = ((DefaultCaret)caret).getDotBias();
2605 
2606                             ((DefaultCaret)caret).setDot(pos, dropBias);
2607                             ((DefaultCaret)caret).moveDot(newPos, newBias);
2608                         } else {
2609                             c.select(pos, c.getCaretPosition());
2610                         }
2611                     }
2612 
2613                     imported = true;
2614                 } catch (UnsupportedFlavorException ufe) {
2615                 } catch (BadLocationException ble) {
2616                 } catch (IOException ioe) {
2617                 }
2618             }
2619             return imported;
2620         }
2621 
2622         /**
2623          * This method indicates if a component would accept an import of the given
2624          * set of data flavors prior to actually attempting to import it.
2625          *
2626          * @param comp  The component to receive the transfer.  This
2627          *  argument is provided to enable sharing of TransferHandlers by
2628          *  multiple components.
2629          * @param flavors  The data formats available
2630          * @return  true if the data can be inserted into the component, false otherwise.
2631          */
2632         public boolean canImport(JComponent comp, DataFlavor[] flavors) {
2633             JTextComponent c = (JTextComponent)comp;
2634             if (!(c.isEditable() && c.isEnabled())) {
2635                 return false;
2636             }
2637             return (getImportFlavor(flavors, c) != null);
2638         }
2639 
2640         /**
2641          * A possible implementation of the Transferable interface
2642          * for text components.  For a JEditorPane with a rich set
2643          * of EditorKit implementations, conversions could be made
2644          * giving a wider set of formats.  This is implemented to
2645          * offer up only the active content type and text/plain
2646          * (if that is not the active format) since that can be
2647          * extracted from other formats.
2648          */
2649         static class TextTransferable extends BasicTransferable {
2650 
2651             TextTransferable(JTextComponent c, int start, int end) {
2652                 super(null, null);
2653 
2654                 this.c = c;
2655 
2656                 Document doc = c.getDocument();
2657 
2658                 try {
2659                     p0 = doc.createPosition(start);
2660                     p1 = doc.createPosition(end);
2661 
2662                     plainData = c.getSelectedText();
2663 
2664                     if (c instanceof JEditorPane) {
2665                         JEditorPane ep = (JEditorPane)c;
2666 
2667                         mimeType = ep.getContentType();
2668 
2669                         if (mimeType.startsWith("text/plain")) {
2670                             return;
2671                         }
2672 
2673                         StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());
2674                         ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());
2675 
2676                         if (mimeType.startsWith("text/html")) {
2677                             htmlData = sw.toString();
2678                         } else {
2679                             richText = sw.toString();
2680                         }
2681                     }
2682                 } catch (BadLocationException ble) {
2683                 } catch (IOException ioe) {
2684                 }
2685             }
2686 
2687             void removeText() {
2688                 if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
2689                     try {
2690                         Document doc = c.getDocument();
2691                         doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
2692                     } catch (BadLocationException e) {
2693                     }
2694                 }
2695             }
2696 
2697             // ---- EditorKit other than plain or HTML text -----------------------
2698 
2699             /**
2700              * If the EditorKit is not for text/plain or text/html, that format
2701              * is supported through the "richer flavors" part of BasicTransferable.
2702              */
2703             protected DataFlavor[] getRicherFlavors() {
2704                 if (richText == null) {
2705                     return null;
2706                 }
2707 
2708                 try {
2709                     DataFlavor[] flavors = new DataFlavor[3];
2710                     flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String");
2711                     flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader");
2712                     flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode");
2713                     return flavors;
2714                 } catch (ClassNotFoundException cle) {
2715                     // fall through to unsupported (should not happen)
2716                 }
2717 
2718                 return null;
2719             }
2720 
2721             /**
2722              * The only richer format supported is the file list flavor
2723              */
2724             @SuppressWarnings("deprecation")
2725             protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
2726                 if (richText == null) {
2727                     return null;
2728                 }
2729 
2730                 if (String.class.equals(flavor.getRepresentationClass())) {
2731                     return richText;
2732                 } else if (Reader.class.equals(flavor.getRepresentationClass())) {
2733                     return new StringReader(richText);
2734                 } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
2735                     return new StringBufferInputStream(richText);
2736                 }
2737                 throw new UnsupportedFlavorException(flavor);
2738             }
2739 
2740             Position p0;
2741             Position p1;
2742             String mimeType;
2743             String richText;
2744             JTextComponent c;
2745         }
2746 
2747     }
2748 
2749 }