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