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