As reported in Bug#23527 the accelerators were not painted (correctly)
in the current BasicMenuItemUI. I looked at the code and found that it
was quite messed up. I moved the code around, rewrote parts of it and
fixed the issue. The accelerators are still not painted 100% correctly,
but I believe that this is caused by the getPreferredSize() returning
too small dimension (the accelerators are positioned relative to the
right border of the menu item). I will look into this soon.

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

        PR 23527
        * javax/swing/plaf/basic/BasicMenuItemUI.java
        (viewRect): New field.
        (textRect): New field.
        (accelRect): New field.
        (iconRect): New field.
        (arrowIconRect): New field.
        (checkIconRect): New field.
        (BasicMenuItemUI): Initialize new fields.
        (paintMenuItem): Rewritten to correctly layout and paint
        the menu item in a more straightforward way. Use cached
rectangle
        objects for layout.
        (paintAccelerator): Pulled inside the paintMenuItem method.

/Roman

-- 
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: javax/swing/plaf/basic/BasicMenuItemUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java,v
retrieving revision 1.44
diff -u -1 -0 -r1.44 BasicMenuItemUI.java
--- javax/swing/plaf/basic/BasicMenuItemUI.java	28 Mar 2006 10:35:05 -0000	1.44
+++ javax/swing/plaf/basic/BasicMenuItemUI.java	29 Mar 2006 11:56:54 -0000
@@ -75,20 +75,21 @@
 import javax.swing.UIManager;
 import javax.swing.event.MenuDragMouseEvent;
 import javax.swing.event.MenuDragMouseListener;
 import javax.swing.event.MenuKeyEvent;
 import javax.swing.event.MenuKeyListener;
 import javax.swing.event.MouseInputListener;
 import javax.swing.plaf.ActionMapUIResource;
 import javax.swing.plaf.ComponentInputMapUIResource;
 import javax.swing.plaf.ComponentUI;
 import javax.swing.plaf.MenuItemUI;
+import javax.swing.text.View;
 
 /**
  * UI Delegate for JMenuItem.
  */
 public class BasicMenuItemUI extends MenuItemUI
 {
   /**
    * Font to be used when displaying menu item's accelerator.
    */
   protected Font acceleratorFont;
@@ -176,21 +177,51 @@
    */
   private int defaultAcceleratorLabelGap = 10;
 
   /**
    * The gap between different menus on the MenuBar.
    */
   private int MenuGap = 10;
   
   /** A PropertyChangeListener to make UI updates after property changes **/
   PropertyChangeHandler propertyChangeListener;
-  
+
+  /**
+   * The view rectangle used for layout of the menu item.
+   */
+  private Rectangle viewRect;
+
+  /**
+   * The rectangle that holds the area of the label.
+   */
+  private Rectangle textRect;
+
+  /**
+   * The rectangle that holds the area of the accelerator.
+   */
+  private Rectangle accelRect;
+
+  /**
+   * The rectangle that holds the area of the icon.
+   */
+  private Rectangle iconRect;
+
+  /**
+   * The rectangle that holds the area of the icon.
+   */
+  private Rectangle arrowIconRect;
+
+  /**
+   * The rectangle that holds the area of the check icon.
+   */
+  private Rectangle checkIconRect;
+
   /**
    * A class to handle PropertChangeEvents for the JMenuItem
    * @author Anthony Balkissoon abalkiss at redhat dot com.   
    */
   class PropertyChangeHandler implements PropertyChangeListener
   {
     /**
      * This method is called when a property of the menuItem is changed.
      * Currently it is only used to update the accelerator key bindings.
      * 
@@ -235,20 +266,28 @@
   /**
    * Creates a new BasicMenuItemUI object.
    */
   public BasicMenuItemUI()
   {
     mouseInputListener = createMouseInputListener(menuItem);
     menuDragMouseListener = createMenuDragMouseListener(menuItem);
     menuKeyListener = createMenuKeyListener(menuItem);
     itemListener = new ItemHandler();
     propertyChangeListener = new PropertyChangeHandler();
+
+    // Initialize rectangles for layout.
+    viewRect = new Rectangle();
+    textRect = new Rectangle();
+    iconRect = new Rectangle();
+    arrowIconRect = new Rectangle();
+    checkIconRect = new Rectangle();
+    accelRect = new Rectangle();
   }
 
   /**
    * Create MenuDragMouseListener to listen for mouse dragged events.
    * 
    * @param c
    *          menu item to listen to
    * @return The MenuDragMouseListener
    */
   protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
@@ -590,101 +629,209 @@
    * @param foreground
    *          Foreground color of the menu item
    * @param defaultTextIconGap
    *          space to use between icon and text when painting menu item
    */
   protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
                                Icon arrowIcon, Color background,
                                Color foreground, int defaultTextIconGap)
   {
     JMenuItem m = (JMenuItem) c;
-    Rectangle tr = new Rectangle(); // text rectangle
-    Rectangle ir = new Rectangle(); // icon rectangle
-    Rectangle vr = new Rectangle(); // view rectangle
-    Rectangle br = new Rectangle(); // border rectangle
-    Rectangle ar = new Rectangle(); // accelerator rectangle
-    Rectangle cr = new Rectangle(); // checkIcon rectangle
-
-    int vertAlign = m.getVerticalAlignment();
-    int horAlign = m.getHorizontalAlignment();
-    int vertTextPos = m.getVerticalTextPosition();
-    int horTextPos = m.getHorizontalTextPosition();
-    
-    Font f = m.getFont();
-    g.setFont(f);
-    FontMetrics fm = g.getFontMetrics(f);
-    SwingUtilities.calculateInnerArea(m, vr);
-    paintBackground(g, m, background);
+    int width = m.getWidth();
+    int height = m.getHeight();
 
-    /*
-     * MenuItems insets are equal to menuItems margin, space between text and
-     * menuItems border. We need to paint insets region as well.
-     */
+    // Reset rectangles.
+    iconRect.setBounds(0, 0, 0, 0);
+    textRect.setBounds(0, 0, 0, 0);
+    accelRect.setBounds(0, 0, 0, 0);
+    checkIconRect.setBounds(0, 0, 0, 0);
+    arrowIconRect.setBounds(0, 0, 0, 0);
+    viewRect.setBounds(0, 0, width, height);
+
+    // Substract insets to the view rect.
     Insets insets = m.getInsets();
-    br.x -= insets.left;
-    br.y -= insets.top;
-    br.width += insets.right + insets.left;
-    br.height += insets.top + insets.bottom;
+    viewRect.x += insets.left;
+    viewRect.y += insets.top;
+    viewRect.width -= (insets.left + insets.right);
+    viewRect.height -= (insets.top + insets.bottom);
+
+    // Fetch fonts.
+    Font oldFont = g.getFont();
+    Font font = c.getFont();
+    g.setFont(font);
+    FontMetrics fm = m.getFontMetrics(font);
+    FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+    // Create accelerator string.
+    KeyStroke accel = m.getAccelerator();
+    String accelText = "";
+    if (accel != null)
+      {
+        int mods = accel.getModifiers();
+        if (mods > 0)
+          {
+            accelText = KeyEvent.getKeyModifiersText(mods);
+            accelText += acceleratorDelimiter;
+          }
+        int keycode = accel.getKeyCode();
+        if (keycode != 0)
+          accelText += KeyEvent.getKeyText(keycode);
+        else
+          accelText += accel.getKeyChar();
+      }
+    // Layout text and icon.
+    String text = m.getText();
+    SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
+                                       m.getVerticalAlignment(),
+                                       m.getHorizontalAlignment(),
+                                       m.getVerticalTextPosition(),
+                                       m.getHorizontalTextPosition(),
+                                       viewRect, iconRect, textRect,
+                                       defaultTextIconGap);
 
-    // If this menu item is a JCheckBoxMenuItem then paint check icon
-    if (checkIcon != null)
+    // Initialize accelerator width and height.
+    if (! accelText.equals(""))
       {
-        SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign,
-                                           horAlign, vertTextPos, horTextPos,
-                                           vr, cr, tr, defaultTextIconGap);
-        checkIcon.paintIcon(m, g, cr.x, cr.y);
-        // We need to calculate position of the menu text and position of
-        // user menu icon if there exists one relative to the check icon.
-        // So we need to adjust view rectangle s.t. its starting point is at
-        // checkIcon.width + defaultTextIconGap.
-        vr.x = cr.x + cr.width + defaultTextIconGap;
+        accelRect.width = accelFm.stringWidth(accelText);
+        accelRect.height = accelFm.getHeight();
       }
 
-    // if this is a submenu, then paint arrow icon to indicate it.
-    if (arrowIcon != null && (c instanceof JMenu))
+    // Initialize check and arrow icon width and height.
+    if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
       {
-        if (!((JMenu) c).isTopLevelMenu())
+        if (checkIcon != null)
           {
-            int width = arrowIcon.getIconWidth();
-            int height = arrowIcon.getIconHeight();
-            int offset = (vr.height - height) / 2;
-            arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset);
+            checkIconRect.width = checkIcon.getIconWidth();
+            checkIconRect.height = checkIcon.getIconHeight();
           }
+        if (arrowIcon != null)
+          {
+            arrowIconRect.width = arrowIcon.getIconWidth();
+            arrowIconRect.height = arrowIcon.getIconHeight();
+          }
+      }
+
+    Rectangle labelRect = iconRect.union(textRect);
+    textRect.x += defaultTextIconGap;
+    iconRect.x += defaultTextIconGap;
+
+    // Layout accelerator rect.
+    accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
+      - defaultTextIconGap - accelRect.width;
+    // Layout check and arrow icons only when not in toplevel menu.
+    if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+      {
+        checkIconRect.x = viewRect.x + defaultTextIconGap;
+        textRect.x += defaultTextIconGap + checkIconRect.width;
+        iconRect.x += defaultTextIconGap + checkIconRect.width;
+        arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
+          - arrowIconRect.width;
+      }
+
+    // Align the accelerator text and all the icons vertically centered to
+    // the menu text.
+    accelRect.y = labelRect.y + (labelRect.height / 2)
+      - (accelRect.height / 2);
+    if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+      {
+        arrowIconRect.y = labelRect.y + (labelRect.height / 2)
+          - (arrowIconRect.height / 2);
+        checkIconRect.y = labelRect.y + (labelRect.height / 2)
+          - (checkIconRect.height / 2);
+      }
+
+    // Paint the background.
+    paintBackground(g, m, background);
+
+    Color oldColor = g.getColor();
+
+    // Paint the check icon.
+    if (checkIcon != null)
+      {
+        checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
       }
 
-    // paint text and user menu icon if it exists
-    Icon i = m.getIcon();
-    SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, vertAlign,
-                                       horAlign, vertTextPos, horTextPos, vr,
-                                       ir, tr, defaultTextIconGap);
-    if (i != null)
-      i.paintIcon(c, g, ir.x, ir.y);
-    paintText(g, m, tr, m.getText());
+    // Paint the icon.
+    ButtonModel model = m.getModel();
+    if (m.getIcon() != null)
+      {
+        // Determine icon depending on the menu item
+        // state (normal/disabled/pressed).
+        Icon icon;
+        if (! m.isEnabled())
+          {
+            icon = m.getDisabledIcon();
+          }
+        else if (model.isPressed() && model.isArmed())
+          {
+            icon = m.getPressedIcon();
+            if (icon == null)
+              {
+                icon = m.getIcon();
+              }
+          }
+        else
+          {
+            icon = m.getIcon();
+          }
 
-    // paint accelerator
-    String acceleratorText = "";
+        if (icon != null)
+          {
+            icon.paintIcon(m, g, iconRect.x, iconRect.y);
+          }
+      }
 
-    if (m.getAccelerator() != null)
+    // Paint the text.
+    if (text != null)
       {
-        acceleratorText = getAcceleratorText(m.getAccelerator());
-        fm = g.getFontMetrics(acceleratorFont);
-        ar.width = fm.stringWidth(acceleratorText);
-        ar.x = br.width - ar.width;
-        vr.x = br.width - ar.width - defaultTextIconGap;
-
-        SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null,
-                                           vertAlign, horAlign, vertTextPos,
-                                           horTextPos, vr, ir, ar,
-                                           defaultTextIconGap);
+        // Handle HTML.
+        View html = (View) m.getClientProperty(BasicHTML.propertyKey);
+        if (html != null)
+          {
+            html.paint(g, textRect);
+          }
+        else
+          {
+            paintText(g, m, textRect, text);
+          }
+      }
 
-        paintAccelerator(g, m, ar, acceleratorText);
+    // Paint accelerator text.
+    if (! accelText.equals(""))
+      {
+        g.setFont(acceleratorFont);
+        if (! m.isEnabled())
+          {
+            // Paint accelerator disabled.
+            g.setColor(disabledForeground);
+          }
+        else
+          {
+            if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
+              g.setColor(acceleratorSelectionForeground);
+            else
+              g.setColor(acceleratorForeground);
+          }
+        g.drawString(accelText, accelRect.x,
+                     accelRect.y + accelFm.getAscent());
       }
+
+    // Paint arrow.
+    if (arrowIcon != null
+        && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+      {
+        arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
+      }
+
+    g.setFont(oldFont);
+    g.setColor(oldColor);
+
   }
 
   /**
    * Paints label for the given menu item
    * 
    * @param g
    *          The graphics context used to paint this menu item
    * @param menuItem
    *          menu item for which to draw its label
    * @param textRect
@@ -855,51 +1002,20 @@
    * @return $Rectangle$ reactangle which will be used to display accelerator
    */
   private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
   {
     int width = fm.stringWidth(getAcceleratorText(accelerator));
     int height = fm.getHeight();
     return new Rectangle(0, 0, width, height);
   }
 
   /**
-   * Paints accelerator inside menu item
-   * 
-   * @param g
-   *          The graphics context used to paint the border
-   * @param menuItem
-   *          Menu item for which to draw accelerator
-   * @param acceleratorRect
-   *          rectangle representing position of the accelerator relative to the
-   *          menu item
-   * @param acceleratorText
-   *          accelerator's text
-   */
-  private void paintAccelerator(Graphics g, JMenuItem menuItem,
-                                Rectangle acceleratorRect,
-                                String acceleratorText)
-  {
-    g.setFont(acceleratorFont);
-    FontMetrics fm = g.getFontMetrics(acceleratorFont);
-
-    if (menuItem.isEnabled())
-      g.setColor(acceleratorForeground);
-    else
-      // FIXME: should fix this to use 'disabledForeground', but its
-      // default value in BasicLookAndFeel is null.
-      g.setColor(Color.gray);
-
-    BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x,
-                                  acceleratorRect.y + fm.getAscent());
-  }
-
-  /**
    * This class handles mouse events occuring inside the menu item. Most of the
    * events are forwarded for processing to MenuSelectionManager of the current
    * menu hierarchy.
    */
   protected class MouseInputHandler implements MouseInputListener
   {
     /**
      * Creates a new MouseInputHandler object.
      */
     protected MouseInputHandler()

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

Reply via email to