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

MetalComboBoxUI computes wrong sizes if user-defined ArrowButton is used.

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 6
    • 1.4.0
    • client-libs
    • beta
    • x86
    • windows_nt



      Name: sv35042 Date: 10/09/2002


      FULL PRODUCT VERSION :
      java version "1.4.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
      Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

      FULL OPERATING SYSTEM VERSION :
      Microsoft Windows NT
      4.00.1381
      ServicePack: 5


      A DESCRIPTION OF THE PROBLEM :
      MetalComboBoxUI computes wrong sizes if user-defined ArrowButton
      is used, which is not subclasses from
      MetalComboBoxButton.

      If you use an user-defined ArrowButton in a MetalComboBox the
      MetalComboBoxUI computes wrong sizes for the ComboBox.
      In MetalComboBoxUI.getMinimumSize(JComponent c):

          public Dimension getMinimumSize( JComponent c ) {
      ...

              if ( !comboBox.isEditable() &&
                   arrowButton != null &&
                   arrowButton instanceof MetalComboBoxButton ) {
      ...
              }
              else if ( comboBox.isEditable() &&
                        arrowButton != null &&
                        editor != null ) {
                  size = super.getMinimumSize( c );
                  Insets margin = arrowButton.getMargin();
                  Insets insets = comboBox.getInsets();
                  if ( editor instanceof JComponent ) {
                      Insets editorInsets =
      ((JComponent)editor).getInsets();
                      size.height += editorInsets.top +
      editorInsets.bottom;
                  }
                  size.height += margin.top + margin.bottom;
                  size.height += insets.top + insets.bottom;
              }
              else {
                  size = super.getMinimumSize( c );
              }

      ...


      The arrowButton is asked for its margins but neither they nor
      the button's insets are used in computing the size of the
      ComboBox.
      This causes the ComboBox being too small.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Split the attached source into Files MyComboBox.java,
         MyComboBoxButton.java, MyComboBoxUI.java, MyLAF.java
         and T01.java
      2. Compile the files into some directory.
      3. Run T01

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      The program displays a label, a MyComboBox widget and
      a JComboBox widget.
      The MyComboBox widget is several pixel smaller than
      the JComboBox widgets.
      I'd expect the MyComboBox widget to be the same
      size, as it essentially uses the same ArrowButton, see
      below.

      Internally MyComboBox uses the MyComboBoxUI class which
      is subclassed from MetalComboBoxUI and creates a
      MyComboBoxButton for use as ArrowButton in the MyComboBox.
      MyComboBoxButton is a copy of MetalComboBoxButton, with
      just some names changed to fit the new class name
      and to circumvent accessibility problems of
      package-private classes etc.

      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      ----- MyComboBox.java

      import javax.swing.JComboBox;

      public class MyComboBox
      extends JComboBox
      {
      public String getUIClassID() {
      return "MyComboBoxUI";
      }

      public MyComboBox(Object[] items)
      {
      super(items);
      }
      }

      ----- MyComboBoxButton.java

      /*
       * @(#)MetalComboBoxButton.java 1.33 01/12/03
       *
       * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
       * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
       */

      //package javax.swing.plaf.metal;
      package myLAF;

      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.plaf.basic.*;
      import javax.swing.*;
      import javax.swing.plaf.*;
      import javax.swing.border.*;
      import java.io.Serializable;

      /**
       * JButton subclass to help out MetalComboBoxUI
       * <p>
       * <strong>Warning:</strong>
       * Serialized objects of this class will not be compatible with
       * future Swing releases. The current serialization support is
       * appropriate for short term storage or RMI between applications running
       * the same version of Swing. As of 1.4, support for long term storage
       * of all JavaBeans<sup><font size="-2">TM</font></sup>
       * has been added to the <code>java.beans</code> package.
       * Please see {@link java.beans.XMLEncoder}.
       *
       * @see MyComboBoxButton
       * @version 1.33 12/03/01
       * @author Tom Santos
       */
      public class MyComboBoxButton extends JButton {
          protected JComboBox comboBox;
          protected JList listBox;
          protected CellRendererPane rendererPane;
          protected Icon comboIcon;
          protected boolean iconOnly = false;

          public final JComboBox getComboBox() { return comboBox;}
          public final void setComboBox( JComboBox cb ) { comboBox = cb;}

          public final Icon getComboIcon() { return comboIcon;}
          public final void setComboIcon( Icon i ) { comboIcon = i;}

          public final boolean isIconOnly() { return iconOnly;}
          public final void setIconOnly( boolean isIconOnly ) { iconOnly = isIconOnly;}

          MyComboBoxButton() {
              super( "" );
              DefaultButtonModel model = new DefaultButtonModel() {
                  public void setArmed( boolean armed ) {
                      super.setArmed( isPressed() ? true : armed );
                  }
              };

              setModel( model );
              
              // Set the background and foreground to the combobox colors.
              setBackground(UIManager.getColor("ComboBox.background"));
              setForeground(UIManager.getColor("ComboBox.foreground"));
          }

          public MyComboBoxButton( JComboBox cb, Icon i,
                                      CellRendererPane pane, JList list ) {
              this();
              comboBox = cb;
              comboIcon = i;
              rendererPane = pane;
              listBox = list;
              setEnabled( comboBox.isEnabled() );
          }

          public MyComboBoxButton( JComboBox cb, Icon i, boolean onlyIcon,
                                      CellRendererPane pane, JList list ) {
              this( cb, i, pane, list );
              iconOnly = onlyIcon;
          }

          public boolean isFocusTraversable() {
      return false;
          }

          public void paintComponent( Graphics g ) {

              boolean leftToRight = true; //MetalUtils.isLeftToRight(comboBox);

              // Paint the button as usual
              super.paintComponent( g );

              Insets insets = getInsets();

              int width = getWidth() - (insets.left + insets.right);
              int height = getHeight() - (insets.top + insets.bottom);

              if ( height <= 0 || width <= 0 ) {
                  return;
              }

              int left = insets.left;
              int top = insets.top;
              int right = left + (width - 1);
              int bottom = top + (height - 1);

              int iconWidth = 0;
              int iconLeft = (leftToRight) ? right : left;

              // Paint the icon
              if ( comboIcon != null ) {
                  iconWidth = comboIcon.getIconWidth();
                  int iconHeight = comboIcon.getIconHeight();
                  int iconTop = 0;

                  if ( iconOnly ) {
                      iconLeft = (getWidth() / 2) - (iconWidth / 2);
                      iconTop = (getHeight() / 2) - (iconHeight / 2);
                  }
                  else {
      if (leftToRight) {
      iconLeft = (left + (width - 1)) - iconWidth;
      }
      else {
      iconLeft = left;
      }
                      iconTop = (top + ((bottom - top) / 2)) - (iconHeight / 2);
                  }

                  comboIcon.paintIcon( this, g, iconLeft, iconTop );

                  // Paint the focus
                  if ( comboBox.hasFocus() ) {
                      g.setColor(
      javax.swing.plaf.metal.MetalLookAndFeel.getFocusColor() );
                      g.drawRect( left - 1, top - 1, width + 3, height + 1 );
                  }
              }

              // Let the renderer paint
              if ( ! iconOnly && comboBox != null ) {
                  ListCellRenderer renderer = comboBox.getRenderer();
                  Component c;
                  boolean renderPressed = getModel().isPressed();
                  c = renderer.getListCellRendererComponent(listBox,
                                                            comboBox.getSelectedItem(),
                                                            -1,
                                                            renderPressed,
                                                            false);
                  c.setFont(rendererPane.getFont());

                  if ( model.isArmed() && model.isPressed() ) {
                      if ( isOpaque() ) {
                          c.setBackground(UIManager.getColor("Button.select"));
                      }
                      c.setForeground(comboBox.getForeground());
                  }
                  else if ( !comboBox.isEnabled() ) {
                      if ( isOpaque() ) {
                         
      c.setBackground(UIManager.getColor("ComboBox.disabledBackground"));
                      }
                      c.setForeground(UIManager.getColor("ComboBox.disabledForeground"));
                  }
                  else {
                      c.setForeground(comboBox.getForeground());
                      c.setBackground(comboBox.getBackground());
                  }


                  int cWidth = width - (insets.right + iconWidth);
                  
                  // Fix for 4238829: should lay out the JPanel.
                  boolean shouldValidate = false;
                  if (c instanceof JPanel) {
                      shouldValidate = true;
                  }
                  
      if (leftToRight) {
      rendererPane.paintComponent( g, c, this,
      left, top, cWidth, height, shouldValidate );
      }
      else {
      rendererPane.paintComponent( g, c, this,
      left + iconWidth, top, cWidth, height, shouldValidate );
      }
              }
          }
      }

      ------ MyComboBoxUI.java

      package myLAF;

      import java.awt.*;
      import javax.swing.*;
      import javax.swing.plaf.ComponentUI;
      import javax.swing.plaf.metal.*;

      import myLAF.MyComboBoxButton;

      public class MyComboBoxUI
      extends MetalComboBoxUI
      {
      public static ComponentUI createUI(JComponent c) {
      return new MyComboBoxUI();
      }

      protected JButton createArrowButton() {
      JButton button = new MyComboBoxButton(comboBox,
      new MetalComboBoxIcon(),
      comboBox.isEditable(),
      currentValuePane,
      listBox);
      button.setMargin(new Insets(0, 1, 1, 3));
      return button;
      }
      }

      ------------- MyLAF.java

      package myLAF;

      import javax.swing.*;
      import javax.swing.plaf.metal.*;

      public class MyLAF
      extends MetalLookAndFeel
      {

      public String getName() {
      return "MyLAF";
      }
      public String getID() {
      return "MyLAF";
      }
      public String getDescription() {
      return "MY LOOK AND FEEL";
      }
      public boolean isSupportedLookAndFeel() {
      return true;
      }
      protected void initClassDefaults(UIDefaults table) {
      super.initClassDefaults(table);
      table.putDefaults(new Object[] {
      "MyComboBoxUI", "myLAF.MyComboBoxUI",
      });
      }
      }

      ------- T01.java

      import java.awt.*;
      import javax.swing.*;

      //import MyComboBox;

      public class T01
      extends JFrame
      {
      public static void main(String[] av)
      {
      try {
      //
      javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
      javax.swing.UIManager.setLookAndFeel("myLAF.MyLAF");
      } catch(Exception ex) {
      System.err.println("Could not set L&F: "+ex);
      System.exit(1);
      }
      T01 t = new T01();
      t.init();
      t.pack();
      t.setVisible(true);
      }

      void init()
      {
      Container cont = getContentPane();
      cont.setLayout(new FlowLayout());
      cont.add(new JLabel("a MyComboBox:"));
      cont.add(new MyComboBox(new Object[]{"red", "green", "blue"}));
      cont.add(new JComboBox(new Object[]{"cyan", "yellow", "magenta"}));
      }
      }

      ---------- END SOURCE ----------
      (Review ID: 145722)
      ======================================================================

            alexp Alexander Potochkin (Inactive)
            svioletsunw Scott Violet (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: