Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]

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

Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]