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