In JDK5 Sun added a useful method to Swing. LookAndFeel.installProperty()
allows UI delegates to install primitive typed properties just like
UIResources, despite the fact that the primitive types cannot inherit
from UIResource.

I have investigated this and found the the supported properties depend on
the actual type of the component. I have summarized my findings in the
API docs to LookAndFeel.installProperty(). The implementation of that
method delegates to a package private method in JComponent (and its
subclasses), which keeps some internal state to determine if a property
has been set by a client program or by a UI delegate.

2006-04-05  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/LookAndFeel.java
        (installProperty): New method. Allows primitive typed properties
        to be handled like UIResources.
        * javax/swing/AbstractButton.java
        (clientBorderPaintedSet): New field.
        (clientRolloverEnabledSet): New field.
        (clientIconTextGapSet): New field.
        (clientContentAreaFilledSet): New field.
        (setRolloverEnabled): Set the client field to true.
        (setBorderPainted): Likewise.
        (setIconTextGap): Likewise.
        (setContentAreaFilled): Likewise.
        (setUIProperty): New helper method.
        * javax/swing/JComponent.java
        (clientOpaqueSet): New field.
        (clientAutoscrollsSet): New field.
        (setAutoscrolls): Set the client field to true.
        (setOpaque): Likewise.
        (setUIProperty): New helper method.
        * javax/swing/JDesktopPane.java
        (clientDragModeSet): New field.
        (setDragMode): Set the client field to true.
        (setUIProperty): New helper method.
        * javax/swing/JSplitPane.java
        (clientDividerSizeSet): New field.
        (clientOneTouchExpandableSet): New field.
        (setDividerSize): Set the client field to true.
        (setOneTouchExpandable): Likewise.
        (setUIProperty): New helper method.
        * javax/swing/JTable.java
        (clientRowHeightSet): New field.
        (setRowHeight): Set the client field to true.
        (setUIProperty): New helper method.
        * javax/swing/JTree.java
        (clientRowHeightSet): New field.
        (clientScrollsOnExpandSet): New field.
        (clientShowsRootHandlesSet): New field.
        (setRowHeight): Set the client field to true.
        (setShowsRootHandles): Likewise.
        (setScrollsOnExpand): Likewise.
        (setUIProperty): New helper method.

/Roman
Index: javax/swing/AbstractButton.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/AbstractButton.java,v
retrieving revision 1.52
diff -u -1 -0 -r1.52 AbstractButton.java
--- javax/swing/AbstractButton.java	23 Mar 2006 23:22:39 -0000	1.52
+++ javax/swing/AbstractButton.java	5 Apr 2006 14:51:57 -0000
@@ -270,20 +270,56 @@
   /**
    * Listener the button uses to receive PropertyChangeEvents from its
    * Action.
    */
   PropertyChangeListener actionPropertyChangeListener;
   
   /** ChangeEvent that is fired to button's ChangeEventListeners  */  
   protected ChangeEvent changeEvent = new ChangeEvent(this);
   
   /**
+   * Indicates if the borderPainted property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientBorderPaintedSet = false;
+
+  /**
+   * Indicates if the rolloverEnabled property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientRolloverEnabledSet = false;
+
+  /**
+   * Indicates if the iconTextGap property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientIconTextGapSet = false;
+
+  /**
+   * Indicates if the contentAreaFilled property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientContentAreaFilledSet = false;
+
+  /**
    * Fired in a PropertyChangeEvent when the "borderPainted" property changes.
    */
   public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
   
   /**
    * Fired in a PropertyChangeEvent when the "contentAreaFilled" property
    * changes.
    */
   public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
     "contentAreaFilled";
@@ -932,20 +968,21 @@
 
   /**
    * Set the "rolloverEnabled" property. When rollover is enabled, and the
    * look and feel supports it, the button will change its icon to
    * rolloverIcon, when the mouse passes over it.
    *
    * @param r Whether or not to enable rollover icon changes
    */
   public void setRolloverEnabled(boolean r)
   {
+    clientRolloverEnabledSet = true;
     if (rollOverEnabled != r)
       {
         rollOverEnabled = r;
         firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
         revalidate();
         repaint();
       }
   }
 
   /**
@@ -1164,20 +1201,21 @@
 
   /**
    * Set the value of the "borderPainted" property. If set to
    * <code>false</code>, the button's look and feel class should not paint
    * a border for the button. The default is <code>true</code>.
    *
    * @param b The new value of the property.
    */
   public void setBorderPainted(boolean b)
   {
+    clientBorderPaintedSet = true;
     if (borderPainted == b)
       return;
     boolean old = borderPainted;
     borderPainted = b;
     firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
     revalidate();
     repaint();
   }
 
   /**
@@ -1314,20 +1352,21 @@
 
   /**
    * Set the value of the [EMAIL PROTECTED] #iconTextGap} property.
    * 
    * @param i The new value of the property
    * 
    * @since 1.4
    */
   public void setIconTextGap(int i)
   {
+    clientIconTextGapSet = true;
     if (iconTextGap == i)
       return;
     
     int old = iconTextGap;
     iconTextGap = i;
     firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
     revalidate();
     repaint();
   }
 
@@ -1947,20 +1986,21 @@
    * Sets the value of the button's "contentAreaFilled" property. This
    * property indicates whether the area surrounding the text and icon of
    * the button should be filled by the look and feel class.  If this
    * property is <code>false</code>, the look and feel class should leave
    * the content area transparent.
    *
    * @param b The new value of the "contentAreaFilled" property
    */
   public void setContentAreaFilled(boolean b)
   {
+    clientContentAreaFilledSet = true;
     if (contentAreaFilled == b)
       return;
     
     // The JDK sets the opaque property to the value of the contentAreaFilled
     // property, so should we do.
     setOpaque(b);
     boolean old = contentAreaFilled;
     contentAreaFilled = b;
     firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
    }
@@ -2074,11 +2114,65 @@
    * 
    * @since 1.4
    */
   public void setMultiClickThreshhold(long threshhold)
   {
     if (threshhold < 0)
       throw new IllegalArgumentException();
 
     multiClickThreshhold = threshhold;
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("borderPainted"))
+      {
+        if (! clientBorderPaintedSet)
+          {
+            setBorderPainted(((Boolean) value).booleanValue());
+            clientBorderPaintedSet = false;
+          }
+      }
+    else if (propertyName.equals("rolloverEnabled"))
+      {
+        if (! clientRolloverEnabledSet)
+          {
+            setRolloverEnabled(((Boolean) value).booleanValue());
+            clientRolloverEnabledSet = false;
+          }
+      }
+    else if (propertyName.equals("iconTextGap"))
+      {
+        if (! clientIconTextGapSet)
+          {
+            setIconTextGap(((Integer) value).intValue());
+            clientIconTextGapSet = false;
+          }
+      }
+    else if (propertyName.equals("contentAreaFilled"))
+      {
+        if (! clientContentAreaFilledSet)
+          {
+            setContentAreaFilled(((Boolean) value).booleanValue());
+            clientContentAreaFilledSet = false;
+          }
+      }
+    else
+      {
+        super.setUIProperty(propertyName, value);
+      }
+  }
 }
Index: javax/swing/JComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JComponent.java,v
retrieving revision 1.110
diff -u -1 -0 -r1.110 JComponent.java
--- javax/swing/JComponent.java	31 Mar 2006 12:33:05 -0000	1.110
+++ javax/swing/JComponent.java	5 Apr 2006 14:52:00 -0000
@@ -604,20 +604,38 @@
   public static final int WHEN_IN_FOCUSED_WINDOW = 2;
 
   /**
    * Indicates if this component is completely dirty or not. This is used
    * by the RepaintManager's
    * [EMAIL PROTECTED] RepaintManager#isCompletelyDirty(JComponent)} method.
    */
   boolean isCompletelyDirty = false;
 
   /**
+   * Indicates if the opaque property has been set by a client program or by
+   * the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientOpaqueSet = false;
+
+  /**
+   * Indicates if the autoscrolls property has been set by a client program or
+   * by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientAutoscrollsSet = false;
+
+  /**
    * Creates a new <code>JComponent</code> instance.
    */
   public JComponent()
   {
     super();
     setDropTarget(new DropTarget());
     defaultLocale = Locale.getDefault();
     debugGraphicsOptions = DebugGraphics.NONE_OPTION;
     setRequestFocusEnabled(true);
   }
@@ -2537,20 +2555,21 @@
   }
 
   /**
    * Set the value of the [EMAIL PROTECTED] #autoscrolls} property.
    *
    * @param a The new value of the property
    */
   public void setAutoscrolls(boolean a)
   {
     autoscrolls = a;
+    clientAutoscrollsSet = true;
   }
 
   /**
    * Set the value of the [EMAIL PROTECTED] #debugGraphicsOptions} property.
    *
    * @param debugOptions The new value of the property
    */
   public void setDebugGraphicsOptions(int debugOptions)
   {
     debugGraphicsOptions = debugOptions;
@@ -2730,20 +2749,21 @@
    * Set the value of the [EMAIL PROTECTED] #opaque} property.
    *
    * @param isOpaque The new value of the property
    *
    * @see ComponentUI#update
    */
   public void setOpaque(boolean isOpaque)
   {
     boolean oldOpaque = opaque;
     opaque = isOpaque;
+    clientOpaqueSet = true;
     firePropertyChange("opaque", oldOpaque, opaque);
   }
 
   /**
    * Set the value of the visible property.
    *
    * If the value is changed, then the AncestorListeners of this component
    * and all its children (recursivly) are notified.
    *
    * @param v The new value of the property
@@ -3405,11 +3425,51 @@
     // Now we have to update the keyboard manager's hashtable
     KeyboardManager km = KeyboardManager.getManager();
     
     // This is a poor strategy, should be improved.  We currently 
     // delete all the old bindings for the component and then register
     // the current bindings.
     km.clearBindingsForComp(changed.getComponent());
     km.registerEntireMap((ComponentInputMap) 
                          getInputMap(WHEN_IN_FOCUSED_WINDOW));
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("opaque"))
+      {
+        if (! clientOpaqueSet)
+          {
+            setOpaque(((Boolean) value).booleanValue());
+            clientOpaqueSet = false;
+          }
+      }
+    else if (propertyName.equals("autoscrolls"))
+      {
+        if (! clientAutoscrollsSet)
+          {
+            setAutoscrolls(((Boolean) value).booleanValue());
+            clientAutoscrollsSet = false;
+          }
+      }
+    else
+      {
+        throw new IllegalArgumentException
+            ("Unsupported property for LookAndFeel.installProperty(): "
+             + propertyName);
+      }
+  }
 }
Index: javax/swing/JDesktopPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JDesktopPane.java,v
retrieving revision 1.13
diff -u -1 -0 -r1.13 JDesktopPane.java
--- javax/swing/JDesktopPane.java	13 Mar 2006 21:33:37 -0000	1.13
+++ javax/swing/JDesktopPane.java	5 Apr 2006 14:52:00 -0000
@@ -78,20 +78,29 @@
   /** The selected frame in the JDesktopPane. */
   private transient JInternalFrame selectedFrame;
 
   /** The JDesktopManager to use for acting on JInternalFrames. */
   transient DesktopManager desktopManager;
 
   /** The drag mode used by the JDesktopPane. */
   private transient int dragMode = LIVE_DRAG_MODE;
 
   /**
+   * Indicates if the dragMode property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientDragModeSet = false;
+
+  /**
    * AccessibleJDesktopPane
    */
   protected class AccessibleJDesktopPane extends AccessibleJComponent
   {
     /** DOCUMENT ME! */
     private static final long serialVersionUID = 6079388927946077570L;
 
     /**
      * Constructor AccessibleJDesktopPane
      */
@@ -146,20 +155,22 @@
    * @param mode The drag mode to use.
    *
    * @throws IllegalArgumentException If the drag mode given is not
    *         LIVE_DRAG_MODE or OUTLINE_DRAG_MODE.
    */
   public void setDragMode(int mode)
   {
     if ((mode != LIVE_DRAG_MODE) && (mode != OUTLINE_DRAG_MODE))
       throw new IllegalArgumentException("Drag mode not valid.");
 
+    clientDragModeSet = true;
+
     // FIXME: Unsupported mode.
     if (mode == OUTLINE_DRAG_MODE)
       // throw new IllegalArgumentException("Outline drag modes are
       // unsupported.");
       mode = LIVE_DRAG_MODE;
 
     dragMode = mode;
   }
 
   /**
@@ -323,11 +334,41 @@
    *
    * @return AccessibleContext
    */
   public AccessibleContext getAccessibleContext()
   {
     if (accessibleContext == null)
       accessibleContext = new AccessibleJDesktopPane();
 
     return accessibleContext;
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("dragMode"))
+      {
+        if (! clientDragModeSet)
+          {
+            setDragMode(((Integer) value).intValue());
+            clientDragModeSet = false;
+          }
+      }
+    else
+      {
+        super.setUIProperty(propertyName, value);
+      }
+  }
 }
Index: javax/swing/JSplitPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JSplitPane.java,v
retrieving revision 1.13
diff -u -1 -0 -r1.13 JSplitPane.java
--- javax/swing/JSplitPane.java	13 Mar 2006 21:33:37 -0000	1.13
+++ javax/swing/JSplitPane.java	5 Apr 2006 14:52:00 -0000
@@ -216,20 +216,38 @@
   /** The component on the top or left. */
   protected Component leftComponent;
 
   /** The component on the right or bottom. */
   protected Component rightComponent;
 
   /** Determines how extra space should be allocated. */
   private transient double resizeWeight;
 
   /**
+   * Indicates if the dividerSize property has been set by a client program or
+   * by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientDividerSizeSet = false;
+
+  /**
+   * Indicates if the oneTouchExpandable property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientOneTouchExpandableSet = false;
+
+  /**
    * Creates a new JSplitPane object with the given orientation, layout mode,
    * and left and right components.
    *
    * @param newOrientation The orientation to use.
    * @param newContinuousLayout The layout mode to use.
    * @param newLeftComponent The left component.
    * @param newRightComponent The right component.
    *
    * @throws IllegalArgumentException DOCUMENT ME!
    */
@@ -665,20 +683,21 @@
       }
   }
 
   /**
    * This method sets the size of the divider.
    *
    * @param newSize The size of the divider.
    */
   public void setDividerSize(int newSize)
   {
+    clientDividerSizeSet = true;
     if (newSize != dividerSize)
       {
         int oldSize = dividerSize;
         dividerSize = newSize;
         firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, dividerSize);
       }
   }
 
   // This doesn't appear to do anything when set from user side.
   // so it probably is only used from the UI side to change the
@@ -716,20 +735,21 @@
   /**
    * This method sets whether the divider has one touch expandable buttons.
    * The one touch expandable buttons can expand the size of either component
    * to the maximum allowed size.
    *
    * @param newValue Whether the divider will have one touch expandable
    *        buttons.
    */
   public void setOneTouchExpandable(boolean newValue)
   {
+    clientOneTouchExpandableSet = true;
     if (newValue != oneTouchExpandable)
       {
         boolean oldValue = oneTouchExpandable;
         oneTouchExpandable = newValue;
         firePropertyChange(ONE_TOUCH_EXPANDABLE_PROPERTY, oldValue,
                            oneTouchExpandable);
       }
   }
 
   /**
@@ -815,11 +835,49 @@
   /**
    * This method returns a string identifier to determine which UI class it
    * needs.
    *
    * @return A string that identifies it's UI class.
    */
   public String getUIClassID()
   {
     return "SplitPaneUI";
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("dividerSize"))
+      {
+        if (! clientDividerSizeSet)
+          {
+            setDividerSize(((Integer) value).intValue());
+            clientDividerSizeSet = false;
+          }
+      }
+    else if (propertyName.equals("oneTouchExpandable"))
+      {
+        if (! clientOneTouchExpandableSet)
+          {
+            setOneTouchExpandable(((Boolean) value).booleanValue());
+            clientOneTouchExpandableSet = false;
+          }
+      }
+    else
+      {
+        super.setUIProperty(propertyName, value);
+      }
+  }
 }
Index: javax/swing/JTable.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JTable.java,v
retrieving revision 1.94
diff -u -1 -0 -r1.94 JTable.java
--- javax/swing/JTable.java	31 Mar 2006 22:03:44 -0000	1.94
+++ javax/swing/JTable.java	5 Apr 2006 14:52:01 -0000
@@ -1568,20 +1568,29 @@
    * activated.
    */
   private boolean surrendersFocusOnKeystroke = false;
 
   /**
    * A Rectangle object to be reused in [EMAIL PROTECTED] #getCellRect}. 
    */
   private Rectangle rectCache = new Rectangle();
 
   /**
+   * Indicates if the rowHeight property has been set by a client program or by
+   * the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientRowHeightSet = false;
+
+  /**
    * Creates a new <code>JTable</code> instance.
    */
   public JTable ()
   {
     this(null, null, null);
   }
 
   /**
    * Creates a new <code>JTable</code> instance with the given number
    * of rows and columns.
@@ -2740,21 +2749,23 @@
 
   /**
    * Set the value of the [EMAIL PROTECTED] #rowHeight} property.
    *
    * @param r The new value of the rowHeight property
    */ 
   public void setRowHeight(int r)
   {
     if (r < 1)
       throw new IllegalArgumentException();
-    
+
+    clientRowHeightSet = true;
+
     rowHeight = r;
     revalidate();
     repaint();
   }
   
   /**
    * Sets the value of the rowHeight property for the specified
    * row.
    * 
    * @param rh is the new rowHeight
@@ -3783,11 +3794,41 @@
    * @return whether cell editors of this table should receive keyboard focus
    *         when the editor is activated by a keystroke
    *
    * @since 1.4
    */
   public boolean getSurrendersFocusOnKeystroke()
   {
     // TODO: Implement functionality of this property (in UI impl).
     return surrendersFocusOnKeystroke;
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("rowHeight"))
+      {
+        if (! clientRowHeightSet)
+          {
+            setRowHeight(((Integer) value).intValue());
+            clientRowHeightSet = false;
+          }
+      }
+    else
+      {
+        super.setUIProperty(propertyName, value);
+      }
+  }
 }
Index: javax/swing/JTree.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JTree.java,v
retrieving revision 1.59
diff -u -1 -0 -r1.59 JTree.java
--- javax/swing/JTree.java	20 Mar 2006 11:26:04 -0000	1.59
+++ javax/swing/JTree.java	5 Apr 2006 14:52:02 -0000
@@ -1436,20 +1436,47 @@
    */
   protected transient TreeModelListener treeModelListener;
 
   /**
    * Redirects TreeSelectionEvents so that the source is this JTree.
    */
   protected TreeSelectionRedirector selectionRedirector =
     new TreeSelectionRedirector();
 
   /**
+   * Indicates if the rowHeight property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientRowHeightSet = false;
+
+  /**
+   * Indicates if the scrollsOnExpand property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientScrollsOnExpandSet = false;
+
+  /**
+   * Indicates if the showsRootHandles property has been set by a client
+   * program or by the UI.
+   *
+   * @see #setUIProperty(String, Object)
+   * @see LookAndFeel#installProperty(JComponent, String, Object)
+   */
+  private boolean clientShowsRootHandlesSet = false;
+
+  /**
    * Creates a new <code>JTree</code> object.
    */
   public JTree()
   {
     this(createTreeModel(null));
   }
 
   /**
    * Creates a new <code>JTree</code> object.
    * 
@@ -1919,20 +1946,22 @@
     firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
   }
 
   public boolean getShowsRootHandles()
   {
     return showsRootHandles;
   }
 
   public void setShowsRootHandles(boolean flag)
   {
+    clientShowsRootHandlesSet = true;
+
     if (showsRootHandles == flag)
       return;
     
     boolean oldValue = showsRootHandles;
     showsRootHandles = flag;
     firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
   }
 
   public TreeCellEditor getCellEditor()
   {
@@ -2018,20 +2047,22 @@
     firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
   }
 
   public int getRowHeight()
   {
     return rowHeight;
   }
 
   public void setRowHeight(int height)
   {
+    clientRowHeightSet = true;
+
     if (rowHeight == height)
       return;
 
     int oldValue = rowHeight;
     rowHeight = height;
     firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
   }
 
   public boolean isFixedRowHeight()
   {
@@ -2107,20 +2138,21 @@
     scrollPathToVisible(getPathForRow(row));
   }
 
   public boolean getScrollsOnExpand()
   {
     return scrollsOnExpand;
   }
 
   public void setScrollsOnExpand(boolean scroll)
   {
+    clientScrollsOnExpandSet = true;
     if (scrollsOnExpand == scroll)
       return;
 
     boolean oldValue = scrollsOnExpand;
     scrollsOnExpand = scroll;
     firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
   }
 
   public void setSelectionPath(TreePath path)
   {
@@ -2937,11 +2969,57 @@
   /**
    * Sent when the tree has changed enough that we need to resize the bounds, 
    * but not enough that we need to remove the expanded node set (e.g nodes
    * were expanded or collapsed, or nodes were inserted into the tree). You 
    * should never have to invoke this, the UI will invoke this as it needs to. 
    */
   public void treeDidChange()
   {
     repaint();
   }
+
+  /**
+   * Helper method for
+   * [EMAIL PROTECTED] LookAndFeel#installProperty(JComponent, String, Object)}.
+   * 
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   */
+  void setUIProperty(String propertyName, Object value)
+  {
+    if (propertyName.equals("rowHeight"))
+      {
+        if (! clientRowHeightSet)
+          {
+            setRowHeight(((Integer) value).intValue());
+            clientRowHeightSet = false;
+          }
+      }
+    else if (propertyName.equals("scrollsOnExpand"))
+      {
+        if (! clientScrollsOnExpandSet)
+          {
+            setScrollsOnExpand(((Boolean) value).booleanValue());
+            clientScrollsOnExpandSet = false;
+          }
+      }
+    else if (propertyName.equals("showsRootHandles"))
+      {
+        if (! clientShowsRootHandlesSet)
+          {
+            setShowsRootHandles(((Boolean) value).booleanValue());
+            clientShowsRootHandlesSet = false;
+          }
+      }
+    else
+      {
+        super.setUIProperty(propertyName, value);
+      }
+  }
 }
Index: javax/swing/LookAndFeel.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/LookAndFeel.java,v
retrieving revision 1.17
diff -u -1 -0 -r1.17 LookAndFeel.java
--- javax/swing/LookAndFeel.java	14 Mar 2006 15:11:27 -0000	1.17
+++ javax/swing/LookAndFeel.java	5 Apr 2006 14:52:02 -0000
@@ -377,11 +377,57 @@
    * Convenience method for un-installing a component's default border on the
    * specified component if the border is currently an instance of UIResource.
    * 
    * @param c  the component (<code>null</code> not permitted).
    */
   public static void uninstallBorder(JComponent c)
   {
     if (c.getBorder() instanceof UIResource)
       c.setBorder(null);
   }
+
+  /**
+   * This methods installs a UI property if it hasn't already been set by an
+   * application. This method is used by UI delegates that install a default
+   * value for a property with a primitive type but do not want to override
+   * a value that has been set by an application.
+   *
+   * The supported properties depend on the actual type of the component and
+   * are listed in the table below. The supported properties are of course
+   * inherited to subclasses.
+   *
+   * <table>
+   * <tr><th>Type</th><th>Supported properties</th></tr>
+   * <tr><td><code>JComponent</code></td>
+   *     <td><code>opaque, autoscrolls</code></td></tr>
+   * <tr><td><code>AbstractButton</code></td>
+   *     <td><code>borderPainted, rolloverEnabled, iconTextGap,
+   *      contentAreaFilled</code></td></tr>
+   * <tr><td><code>JDesktopPane</code></td>
+   *     <td><code>dragMode</code></td></tr>
+   * <tr><td><code>JSplitPane</code></td>
+   *     <td><code>dividerSize, oneTouchExpandable</code></td></tr>
+   * <tr><td><code>JTable</code></td>
+   *     <td><code>rowHeight</code></td></tr>
+   * <tr><td><code>JTree</code></td>
+   *     <td><code>rowHeight, scrollsOnExpand, showsRootHandles</code></td></tr>
+   * </table>
+   *
+   * @param c the component to install the property to
+   * @param propertyName the name of the property
+   * @param value the value of the property
+   *
+   * @throws IllegalArgumentException if the specified property cannot be set
+   *         by this method
+   * @throws ClassCastException if the property value does not match the
+   *         property type
+   * @throws NullPointerException if <code>c</code> or
+   *         <code>propertyValue</code> is <code>null</code>
+   *
+   * @since 1.5
+   */
+  public static void installProperty(JComponent c, String propertyName,
+                                     Object value)
+  {
+    c.setUIProperty(propertyName, value);
+  }
 }

Reply via email to