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