This fixes some bits in BasicTextUI and speeds up initialization of text components: - The focus handler for X-like-clipboard is made static and shared between components. And is only installed at all, as long as the (new) system property gnu.swing.text.no-xlike-clipboard is _not_ set.
- The document and property handlers are combined into one class.
- The (un)installUI and (un)installDefaults methods have been fixed up a little.
- The initialization of rootView and editorKit are now done more lazily.

2006-08-29  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/plaf/basic/BasicTextUI.java
        (FocusHandler): New class. This is moved from the anonymous
        inner focus listener class to a static member class, and
        is now shared between components.
        (DocumentHandler): This class is combined with the PropertyHandler
        into the Handler class.
        (PropertyChangeHandler): This class is combined with the
        DocumentHandler into the Handler class.
        (Handler): New class. This combines the Property and Document
        handler into one class.
        (RootView.changedUpdate): Only forward if real view != null.
        (RootView.insertUpdate): Only forward if real view != null.
        (RootView.removeUpdate): Only forward if real view != null.
        (documentHandler): Removed field and replaced by handler.
        (focuslistener): Made field static and renamed to focusListener.
        (handler): New field.
        (kit): Lazily initialize field.
        (rootView): Lazily initialize field.
        (updateHandler): Removed and replaced by handler.
        (getEditorKit): Lazily instantiate field.
        (installDefaults): Don't set margin twice. Install correct
        property for disabledTextColor. Moved caret and highlighter
        initialization to installFixedDefaults.
        (installFixedDefaults): New method. Installs defaults that
        can't be overridden by subclasses.
        (installListeners): Only install focus handler when new
        system property gnu.swing.text.no-xlike-clipboard is not set.
        Lazily initialize focus handler.
        (installUI): Lazily initialize rootView. Install handler
        both for property and document changes.
        (uninstallDefaults): Uninstall the UI defaults.
        (uninstallFixedDefaults): New method. Uninstalls the fixed
        defaults.
        (installListeners): Only uninstall focus handler when not null.
        (uninstallUI): Uninstall property and document listener here.


/Roman
Index: javax/swing/plaf/basic/BasicTextUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTextUI.java,v
retrieving revision 1.92
diff -u -1 -2 -r1.92 BasicTextUI.java
--- javax/swing/plaf/basic/BasicTextUI.java	24 Aug 2006 17:09:15 -0000	1.92
+++ javax/swing/plaf/basic/BasicTextUI.java	29 Aug 2006 10:56:45 -0000
@@ -29,24 +29,26 @@
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.plaf.basic;
 
+import gnu.classpath.SystemProperties;
+
 import java.awt.Color;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.HeadlessException;
 import java.awt.Insets;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Shape;
 import java.awt.Toolkit;
 import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.StringSelection;
@@ -112,24 +114,158 @@
   /**
    * A [EMAIL PROTECTED] DefaultHighlighter} that implements [EMAIL PROTECTED] UIResource}.
    */
   public static class BasicHighlighter extends DefaultHighlighter
     implements UIResource
   {
     public BasicHighlighter()
     {
       // Nothing to do here.
     }
   }
 
+  private static class FocusHandler
+    implements FocusListener
+  {
+    public void focusGained(FocusEvent e) 
+    {
+      // Nothing to do here.
+    }
+    public void focusLost(FocusEvent e)
+    {
+      JTextComponent textComponent = (JTextComponent) e.getComponent();
+      // Integrates Swing text components with the system clipboard:
+      // The idea is that if one wants to copy text around X11-style
+      // (select text and middle-click in the target component) the focus
+      // will move to the new component which gives the old focus owner the
+      // possibility to paste its selection into the clipboard.
+      if (!e.isTemporary()
+          && textComponent.getSelectionStart()
+             != textComponent.getSelectionEnd())
+        {
+          SecurityManager sm = System.getSecurityManager();
+          try
+            {
+              if (sm != null)
+                sm.checkSystemClipboardAccess();
+              
+              Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
+              if (cb != null)
+                {
+                  StringSelection selection = new StringSelection(
+                      textComponent.getSelectedText());
+                  cb.setContents(selection, selection);
+                }
+            }
+          catch (SecurityException se)
+            {
+              // Not allowed to access the clipboard: Ignore and
+              // do not access it.
+            }
+          catch (HeadlessException he)
+            {
+              // There is no AWT: Ignore and do not access the
+              // clipboard.
+            }
+          catch (IllegalStateException ise)
+          {
+              // Clipboard is currently unavaible.
+          }
+        }
+    }
+  }
+
+  /**
+   * This FocusListener triggers repaints on focus shift.
+   */
+  private static FocusListener focusListener;
+
+  /**
+   * Receives notifications when properties of the text component change.
+   */
+  private class Handler
+    implements PropertyChangeListener, DocumentListener
+  {
+    /**
+     * Notifies when a property of the text component changes.
+     *
+     * @param event the PropertyChangeEvent describing the change
+     */
+    public void propertyChange(PropertyChangeEvent event)
+    {
+      if (event.getPropertyName().equals("document"))
+        {
+          // Document changed.
+          Object oldValue = event.getOldValue();
+          if (oldValue != null)
+            {
+              Document oldDoc = (Document) oldValue;
+              oldDoc.removeDocumentListener(handler);
+            }
+          Object newValue = event.getNewValue();
+          if (newValue != null)
+            {
+              Document newDoc = (Document) newValue;
+              newDoc.addDocumentListener(handler);
+            }
+          modelChanged();
+        }
+
+      BasicTextUI.this.propertyChange(event);
+    }
+
+    /**
+     * Notification about a document change event.
+     *
+     * @param ev the DocumentEvent describing the change
+     */
+    public void changedUpdate(DocumentEvent ev)
+    {
+      // Updates are forwarded to the View even if 'getVisibleEditorRect'
+      // method returns null. This means the View classes have to be
+      // aware of that possibility.
+      rootView.changedUpdate(ev, getVisibleEditorRect(),
+                             rootView.getViewFactory());
+    }
+
+    /**
+     * Notification about a document insert event.
+     *
+     * @param ev the DocumentEvent describing the insertion
+     */
+    public void insertUpdate(DocumentEvent ev)
+    {
+      // Updates are forwarded to the View even if 'getVisibleEditorRect'
+      // method returns null. This means the View classes have to be
+      // aware of that possibility.
+      rootView.insertUpdate(ev, getVisibleEditorRect(),
+                            rootView.getViewFactory());
+    }
+
+    /**
+     * Notification about a document removal event.
+     *
+     * @param ev the DocumentEvent describing the removal
+     */
+    public void removeUpdate(DocumentEvent ev)
+    {
+      // Updates are forwarded to the View even if 'getVisibleEditorRect'
+      // method returns null. This means the View classes have to be
+      // aware of that possibility.
+      rootView.removeUpdate(ev, getVisibleEditorRect(),
+                            rootView.getViewFactory());
+    }
+
+  }
+
   /**
    * This view forms the root of the View hierarchy. However, it delegates
    * most calls to another View which is the real root of the hierarchy.
    * The purpose is to make sure that all Views in the hierarchy, including
    * the (real) root have a well-defined parent to which they can delegate
    * calls like [EMAIL PROTECTED] #preferenceChanged}, [EMAIL PROTECTED] #getViewFactory} and
    * [EMAIL PROTECTED] #getContainer}.
    */
   private class RootView extends View
   {
     /** The real root view. */
     private View view;
@@ -310,51 +446,54 @@
     }
 
     /**
      * Notification about text insertions. These are forwarded to the
      * real root view.
      *
      * @param ev the DocumentEvent describing the change
      * @param shape the current allocation of the view's display
      * @param vf the ViewFactory to use for creating new Views
      */
     public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
     {
-      view.insertUpdate(ev, shape, vf);
+      if (view != null)
+        view.insertUpdate(ev, shape, vf);
     }
 
     /**
      * Notification about text removals. These are forwarded to the
      * real root view.
      *
      * @param ev the DocumentEvent describing the change
      * @param shape the current allocation of the view's display
      * @param vf the ViewFactory to use for creating new Views
      */
     public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
     {
-      view.removeUpdate(ev, shape, vf);
+      if (view != null)
+        view.removeUpdate(ev, shape, vf);
     }
 
     /**
      * Notification about text changes. These are forwarded to the
      * real root view.
      *
      * @param ev the DocumentEvent describing the change
      * @param shape the current allocation of the view's display
      * @param vf the ViewFactory to use for creating new Views
      */
     public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
     {
-      view.changedUpdate(ev, shape, vf);
+      if (view != null)
+        view.changedUpdate(ev, shape, vf);
     }
 
     /**
      * Returns the document position that is (visually) nearest to the given
      * document position <code>pos</code> in the given direction <code>d</code>.
      *
      * @param pos the document position
      * @param b the bias for <code>pos</code>
      * @param a the allocation for the view
      * @param d the direction, must be either [EMAIL PROTECTED] SwingConstants#NORTH},
      *        [EMAIL PROTECTED] SwingConstants#SOUTH}, [EMAIL PROTECTED] SwingConstants#WEST} or
      *        [EMAIL PROTECTED] SwingConstants#EAST}
@@ -409,133 +548,48 @@
     }
 
     /**
      * Returns the attributes, which is null for the RootView.
      */
     public AttributeSet getAttributes()
     {
       return null;
     }
   }
 
   /**
-   * Receives notifications when properties of the text component change.
+   * The EditorKit used by this TextUI.
    */
-  private class PropertyChangeHandler implements PropertyChangeListener
-  {
-    /**
-     * Notifies when a property of the text component changes.
-     *
-     * @param event the PropertyChangeEvent describing the change
-     */
-    public void propertyChange(PropertyChangeEvent event)
-    {
-      if (event.getPropertyName().equals("document"))
-        {
-          // Document changed.
-          Object oldValue = event.getOldValue();
-          if (oldValue != null)
-            {
-              Document oldDoc = (Document) oldValue;
-              oldDoc.removeDocumentListener(documentHandler);
-            }
-          Object newValue = event.getNewValue();
-          if (newValue != null)
-            {
-              Document newDoc = (Document) newValue;
-              newDoc.addDocumentListener(documentHandler);
-            }
-          modelChanged();
-        }
-
-      BasicTextUI.this.propertyChange(event);
-    }
-  }
+  private static EditorKit kit;
 
   /**
-   * Listens for changes on the underlying model and forwards notifications
-   * to the View. This also updates the caret position of the text component.
+   * The combined event handler for text components.
    *
-   * TODO: Maybe this should somehow be handled through EditorKits
-   */
-  class DocumentHandler implements DocumentListener
-  {
-    /**
-     * Notification about a document change event.
-     *
-     * @param ev the DocumentEvent describing the change
-     */
-    public void changedUpdate(DocumentEvent ev)
-    {
-      // Updates are forwarded to the View even if 'getVisibleEditorRect'
-      // method returns null. This means the View classes have to be
-      // aware of that possibility.
-      rootView.changedUpdate(ev, getVisibleEditorRect(),
-                             rootView.getViewFactory());
-    }
-
-    /**
-     * Notification about a document insert event.
-     *
-     * @param ev the DocumentEvent describing the insertion
-     */
-    public void insertUpdate(DocumentEvent ev)
-    {
-      // Updates are forwarded to the View even if 'getVisibleEditorRect'
-      // method returns null. This means the View classes have to be
-      // aware of that possibility.
-      rootView.insertUpdate(ev, getVisibleEditorRect(),
-                            rootView.getViewFactory());
-    }
-
-    /**
-     * Notification about a document removal event.
-     *
-     * @param ev the DocumentEvent describing the removal
-     */
-    public void removeUpdate(DocumentEvent ev)
-    {
-      // Updates are forwarded to the View even if 'getVisibleEditorRect'
-      // method returns null. This means the View classes have to be
-      // aware of that possibility.
-      rootView.removeUpdate(ev, getVisibleEditorRect(),
-                            rootView.getViewFactory());
-    }
-  }
-
-  /**
-   * The EditorKit used by this TextUI.
+   * This is package private to avoid accessor methods.
    */
-  // FIXME: should probably be non-static.
-  static EditorKit kit = new DefaultEditorKit();
+  Handler handler;
 
   /**
    * The root view.
+   *
+   * This is package private to avoid accessor methods.
    */
-  RootView rootView = new RootView();
+  RootView rootView;
 
   /**
    * The text component that we handle.
    */
   JTextComponent textComponent;
 
   /**
-   * Receives notification when the model changes.
-   */
-  private PropertyChangeHandler updateHandler = new PropertyChangeHandler();
-
-  /** The DocumentEvent handler. */
-  DocumentHandler documentHandler = new DocumentHandler();
-
-  /**
    * Creates a new <code>BasicTextUI</code> instance.
    */
   public BasicTextUI()
   {
     // Nothing to do here.
   }
 
   /**
    * Creates a [EMAIL PROTECTED] Caret} that should be installed into the text component.
    *
    * @return a caret that should be installed into the text component
    */
@@ -564,162 +618,138 @@
   {
     return textComponent;
   }
 
   /**
    * Installs this UI on the text component.
    *
    * @param c the text component on which to install the UI
    */
   public void installUI(final JComponent c)
   {
     textComponent = (JTextComponent) c;
+
+    if (rootView == null)
+      rootView = new RootView();
+
     installDefaults();
-    textComponent.addPropertyChangeListener(updateHandler);
+    installFixedDefaults();
+
+    // These listeners must be installed outside of installListeners(),
+    // because overriding installListeners() doesn't prevent installing
+    // these in the RI, but overriding isntallUI() does.
+    if (handler == null)
+      handler = new Handler();
+    textComponent.addPropertyChangeListener(handler);
     Document doc = textComponent.getDocument();
     if (doc == null)
       {
+        // The Handler takes care of installing the necessary listeners
+        // on the document here.
         doc = getEditorKit(textComponent).createDefaultDocument();
         textComponent.setDocument(doc);
       }
     else
       {
-        doc.addDocumentListener(documentHandler);
+        // Must install the document listener.
+        doc.addDocumentListener(handler);
         modelChanged();
       }
 
     installListeners();
     installKeyboardActions();
   }
 
   /**
    * Installs UI defaults on the text components.
    */
   protected void installDefaults()
   {
     String prefix = getPropertyPrefix();
     // Install the standard properties.
     LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
                                      prefix + ".foreground", prefix + ".font");
     LookAndFeel.installBorder(textComponent, prefix + ".border");
-    textComponent.setMargin(UIManager.getInsets(prefix + ".margin"));
 
     // Some additional text component only properties.
     Color color = textComponent.getCaretColor();
     if (color == null || color instanceof UIResource)
       {
         color = UIManager.getColor(prefix + ".caretForeground");
         textComponent.setCaretColor(color);
       }
 
     // Fetch the colors for enabled/disabled text components.
     color = textComponent.getDisabledTextColor();
     if (color == null || color instanceof UIResource)
       {
-        color = UIManager.getColor(prefix + ".inactiveBackground");
+        color = UIManager.getColor(prefix + ".inactiveForeground");
         textComponent.setDisabledTextColor(color);
       }
     color = textComponent.getSelectedTextColor();
     if (color == null || color instanceof UIResource)
       {
         color = UIManager.getColor(prefix  + ".selectionForeground");
         textComponent.setSelectedTextColor(color);
       }
     color = textComponent.getSelectionColor();
     if (color == null || color instanceof UIResource)
       {
         color = UIManager.getColor(prefix  + ".selectionBackground");
         textComponent.setSelectionColor(color);    
       }
 
     Insets margin = textComponent.getMargin();
     if (margin == null || margin instanceof UIResource)
       {
         margin = UIManager.getInsets(prefix + ".margin");
         textComponent.setMargin(margin);
       }
 
+  }
+
+  /**
+   * Installs defaults that can't be overridden by overriding
+   * installDefaults().
+   */
+  private void installFixedDefaults()
+  {
+    String prefix = getPropertyPrefix();
     Caret caret = textComponent.getCaret();
     if (caret == null || caret instanceof UIResource)
       {
         caret = createCaret();
         textComponent.setCaret(caret);
         caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate"));
       }
 
     Highlighter highlighter = textComponent.getHighlighter();
     if (highlighter == null || highlighter instanceof UIResource)
       textComponent.setHighlighter(createHighlighter());
 
   }
 
   /**
-   * This FocusListener triggers repaints on focus shift.
-   */
-  private FocusListener focuslistener = new FocusListener() {
-      public void focusGained(FocusEvent e) 
-      {
-        textComponent.repaint();
-      }
-      public void focusLost(FocusEvent e)
-      {
-        textComponent.repaint();
-        
-        // Integrates Swing text components with the system clipboard:
-        // The idea is that if one wants to copy text around X11-style
-        // (select text and middle-click in the target component) the focus
-        // will move to the new component which gives the old focus owner the
-        // possibility to paste its selection into the clipboard.
-        if (!e.isTemporary()
-            && textComponent.getSelectionStart()
-               != textComponent.getSelectionEnd())
-          {
-            SecurityManager sm = System.getSecurityManager();
-            try
-              {
-                if (sm != null)
-                  sm.checkSystemClipboardAccess();
-                
-                Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
-                if (cb != null)
-                  {
-                    StringSelection selection = new StringSelection(
-                        textComponent.getSelectedText());
-                    cb.setContents(selection, selection);
-                  }
-              }
-            catch (SecurityException se)
-              {
-                // Not allowed to access the clipboard: Ignore and
-                // do not access it.
-              }
-            catch (HeadlessException he)
-              {
-                // There is no AWT: Ignore and do not access the
-                // clipboard.
-              }
-            catch (IllegalStateException ise)
-            {
-                // Clipboard is currently unavaible.
-            }
-          }
-      }
-    };
-
-  /**
    * Install all listeners on the text component.
    */
   protected void installListeners()
   {
-    textComponent.addFocusListener(focuslistener);
+    // 
+    if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
+        == null)
+      {
+        if (focusListener == null)
+          focusListener = new FocusHandler();
+        textComponent.addFocusListener(focusListener);
+      }
   }
 
   /**
    * Returns the name of the keymap for this type of TextUI.
    * 
    * This is implemented so that the classname of this TextUI
    * without the package prefix is returned. This way subclasses
    * don't have to override this method.
    * 
    * @return the name of the keymap for this TextUI
    */
   protected String getKeymapName()
@@ -840,51 +870,77 @@
     if (shared != null)
       im.setParent(shared);
     return im;
   }
 
   /**
    * Uninstalls this TextUI from the text component.
    *
    * @param component the text component to uninstall the UI from
    */
   public void uninstallUI(final JComponent component)
   {
-    super.uninstallUI(component);
+    textComponent.removePropertyChangeListener(handler);
+    textComponent.getDocument().removeDocumentListener(handler);
     rootView.setView(null);
 
     uninstallDefaults();
+    uninstallFixedDefaults();
     uninstallListeners();
     uninstallKeyboardActions();
 
     textComponent = null;
   }
 
   /**
    * Uninstalls all default properties that have previously been installed by
    * this UI.
    */
   protected void uninstallDefaults()
   {
-    // Do nothing here.
+    if (textComponent.getCaretColor() instanceof UIResource)
+      textComponent.setCaretColor(null);
+    if (textComponent.getSelectionColor() instanceof UIResource)
+      textComponent.setSelectionColor(null);
+    if (textComponent.getDisabledTextColor() instanceof UIResource)
+      textComponent.setDisabledTextColor(null);
+    if (textComponent.getSelectedTextColor() instanceof UIResource)
+      textComponent.setSelectedTextColor(null);
+    LookAndFeel.uninstallBorder(textComponent);
+    if (textComponent.getMargin() instanceof UIResource)
+      textComponent.setMargin(null);
+  }
+
+  /**
+   * Uninstalls additional fixed defaults that were installed
+   * by installFixedDefaults().
+   */
+  private void uninstallFixedDefaults()
+  {
+    if (textComponent.getCaret() instanceof UIResource)
+      textComponent.setCaret(null);
+    if (textComponent.getHighlighter() instanceof UIResource)
+      textComponent.setHighlighter(null);
   }
 
   /**
    * Uninstalls all listeners that have previously been installed by
    * this UI.
    */
   protected void uninstallListeners()
   {
-    textComponent.removeFocusListener(focuslistener);
-    textComponent.getDocument().removeDocumentListener(documentHandler);
+    // Don't nullify the focusListener field, as it is static and shared
+    // between components.
+    if (focusListener != null)
+      textComponent.removeFocusListener(focusListener);
   }
 
   /**
    * Uninstalls all keyboard actions that have previously been installed by
    * this UI.
    */
   protected void uninstallKeyboardActions()
   {
     SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED, 
                                      null);
     SwingUtilities.replaceUIActionMap(textComponent, null);
   }
@@ -1116,24 +1172,26 @@
 
   /**
    * Returns the [EMAIL PROTECTED] EditorKit} used for the text component that is managed
    * by this UI.
    *
    * @param t the text component
    *
    * @return the [EMAIL PROTECTED] EditorKit} used for the text component that is managed
    *         by this UI
    */
   public EditorKit getEditorKit(JTextComponent t)
   {
+    if (kit == null)
+      kit = new DefaultEditorKit();
     return kit;
   }
 
   /**
    * Gets the next position inside the document model that is visible on
    * screen, starting from <code>pos</code>.
    *
    * @param t the text component
    * @param pos the start positionn
    * @param b the bias for pos
    * @param direction the search direction
    * @param biasRet filled by the method to indicate the bias of the return

Reply via email to