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()
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil
