Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6612752

Incorrect getOpenIcon() instanceof in the DefaultTreeCellRenderer

XMLWordPrintable

    • b12
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      java version "1.6.0_02"
      Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
      Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

      ADDITIONAL OS VERSION INFORMATION :
      All OSes

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Not applicable

      A DESCRIPTION OF THE PROBLEM :
      When explicitly setting a DefaultTreeCellRenderer (subclassed or not) on a JTree, the renderer fails to react on Look and Feel change.

      The cause is misplacement of the setting of UI values. This is done in the constructor, NOT in the updateUI method.

        Fixing is simple: move the setting of UI variables from the constructor to the updateUI method.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Please compile and run attached source code.

      When changing Look and Feel, the bug is visible.

      Also, the proposed fix is made visible.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      All trees should be displayed in the selected Look and Feel
      ACTUAL -
      The tree with an explicitly set DefaultTreeCellRenderer is not displayed in the selected Look and Feel.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Not applicable.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.BorderLayout;
      import java.awt.GridLayout;
      import java.awt.Window;
      import java.awt.event.ActionEvent;
      import java.util.Enumeration;

      import javax.swing.AbstractAction;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JScrollPane;
      import javax.swing.JTree;
      import javax.swing.SwingUtilities;
      import javax.swing.UIManager;
      import javax.swing.UnsupportedLookAndFeelException;
      import javax.swing.tree.DefaultTreeCellRenderer;
      import javax.swing.tree.DefaultTreeModel;
      import javax.swing.tree.TreeNode;
      import javax.swing.tree.TreePath;

      /**
       * Demonstrates bug in <code>DefaultTreeCellRenderer</code>.
       * <p>
       * Initializing UI variables resides in constructor instead of
       * <code>updateUI</code> method.
       * </p>
       * <p>
       * This demonstration not only shows the bug, but also shows the fix that must
       * be made to <code>DefaultTreeCellRenderer</code> (based on java 6.0 source
       * code as packaged in the JDK).
       * </p>
       *
       * @author Piet Blok
       */
      public class TreeCellRendererBug {

          static abstract class SampleGenerator {

      public final String title;

      JFrame frame;

      public SampleGenerator(String title) {
      this.title = title;
      }

      public abstract JTree createTree();

      public void show() {
      SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {
      frame = new JFrame(title);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setLocationByPlatform(true);
      JTree tree = createTree();
      frame.getContentPane().add(new JScrollPane(tree),
      BorderLayout.CENTER);
      frame.setSize(400, 400);
      frame.setVisible(true);
      setAllNodesVisible(tree,
      (DefaultTreeModel) tree.getModel(), (TreeNode) tree
      .getModel().getRoot());
      }
      });
      }

      @SuppressWarnings("unchecked")
      private void setAllNodesVisible(JTree tree, DefaultTreeModel model,
      TreeNode node) {
      tree.scrollPathToVisible(new TreePath(model.getPathToRoot(node)));
      Enumeration<TreeNode> children = node.children();
      while (children.hasMoreElements()) {
      setAllNodesVisible(tree, model, children.nextElement());
      }
      }

          }

          /**
           * @param args
           */
          public static void main(String[] args) {
      /*
      * A simple tree with no renderer set works OK when L&F changes.
      */
      new SampleGenerator("Implicit DefaultTreeCellRenderer") {

      @Override
      public JTree createTree() {
      return new JTree();
      }
      }.show();
      /*
      * A simple tree with a DefaultTreeCellRenderer explicitly set. This
      * renderer will not react to L&F changes. This is a bug.
      */
      new SampleGenerator("Explicit DefaultTreeCellRenderer BUG") {

      @Override
      public JTree createTree() {
      JTree tree = new JTree();
      tree.setCellRenderer(new DefaultTreeCellRenderer());
      return tree;
      }
      }.show();
      /*
      * A simple tree with a repaired DefaultTreeCellRenderer explicitly set.
      * This renderer will react to L&F changes.
      */
      new SampleGenerator("Explicit fixed DefaultTreeCellRenderer") {

      @Override
      public JTree createTree() {
      JTree tree = new JTree();
      tree.setCellRenderer(new DefaultTreeCellRenderer() {

      private static final long serialVersionUID = 1L;

      @Override
      public void updateUI() {
      super.updateUI();
      setLeafIcon(UIManager.getIcon("Tree.leafIcon"));
      setClosedIcon(UIManager.getIcon("Tree.closedIcon"));
      setOpenIcon(UIManager.getIcon("Tree.openIcon"));

      setTextSelectionColor(UIManager
      .getColor("Tree.selectionForeground"));
      setTextNonSelectionColor(UIManager
      .getColor("Tree.textForeground"));
      setBackgroundSelectionColor(UIManager
      .getColor("Tree.selectionBackground"));
      setBackgroundNonSelectionColor(UIManager
      .getColor("Tree.textBackground"));
      setBorderSelectionColor(UIManager
      .getColor("Tree.selectionBorderColor"));
      }

      });
      return tree;
      }
      }.show();
      /*
      * Create a L&F selection frame.
      */
      SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {
      createPLAFChooser();
      }
      });
          }

          private static void createPLAFChooser() {
      UIManager.LookAndFeelInfo[] plafs = UIManager
      .getInstalledLookAndFeels();
      final JFrame frame = new JFrame("Choose a L&F");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setLocationByPlatform(true);
      JPanel buttonPanel = new JPanel(new GridLayout(0, 1));
      frame.getContentPane().add(buttonPanel, BorderLayout.CENTER);
      for (final UIManager.LookAndFeelInfo plaf : plafs) {
      JButton item = new JButton(new AbstractAction(plaf.getName()) {

      private static final long serialVersionUID = 1L;

      @Override
      public void actionPerformed(ActionEvent event) {
      try {
      UIManager.setLookAndFeel(plaf.getClassName());
      for (Window window : Window.getWindows()) {
      SwingUtilities.updateComponentTreeUI(window);
      }
      } catch (ClassNotFoundException e) {
      e.printStackTrace();
      } catch (InstantiationException e) {
      e.printStackTrace();
      } catch (IllegalAccessException e) {
      e.printStackTrace();
      } catch (UnsupportedLookAndFeelException e) {
      e.printStackTrace();
      }

      }
      });

      buttonPanel.add(item);
      }
      frame.setVisible(true);
      SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {
      frame.setSize(frame.getPreferredSize());
      }
      });

          }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      A developer can subclass DefaultTreeCellRenderer and override the updateUI method.

      However, one is never certain that in newer releases the same set of UI variables are used.

      Therefore the fix should be applied to the JRE/JDK itself.

            alexsch Alexandr Scherbatiy
            ryeung Roger Yeung (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: