diff options
Diffstat (limited to 'javax/swing/plaf/basic/BasicMenuItemUI.java')
-rw-r--r-- | javax/swing/plaf/basic/BasicMenuItemUI.java | 487 |
1 files changed, 345 insertions, 142 deletions
diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java index 9166c49ee..69c9c4507 100644 --- a/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -40,6 +40,7 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; @@ -82,6 +83,7 @@ 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. @@ -183,7 +185,43 @@ public class BasicMenuItemUI extends MenuItemUI /** 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 rectangle used for temporary storage to avoid creation of new + * rectangles. + */ + private Rectangle cachedRect; + /** * A class to handle PropertChangeEvents for the JMenuItem * @author Anthony Balkissoon abalkiss at redhat dot com. @@ -242,6 +280,15 @@ public class BasicMenuItemUI extends MenuItemUI 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(); + cachedRect = new Rectangle(); } /** @@ -378,50 +425,69 @@ public class BasicMenuItemUI extends MenuItemUI int defaultTextIconGap) { JMenuItem m = (JMenuItem) c; - Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m, - defaultTextIconGap); - - // if menu item has accelerator then take accelerator's size into account - // when calculating preferred size. - KeyStroke accelerator = m.getAccelerator(); - Rectangle rect; - - if (accelerator != null) + String accelText = getAcceleratorString(m); + + // Layout the menu item. The result gets stored in the rectangle + // fields of this class. + layoutMenuItem(m, accelText); + + // The union of the text and icon areas is the label area. + cachedRect.setBounds(textRect); + Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y, + iconRect.width, + iconRect.height, + cachedRect); + + // Find the widest menu item text and accelerator and store it in + // client properties of the parent, so that we can align the accelerators + // properly. Of course, we only need can do this, if the parent is + // a JComponent and this menu item is not a toplevel menu. + Container parent = m.getParent(); + if (parent != null && parent instanceof JComponent + && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - rect = getAcceleratorRect( - accelerator, - m.getToolkit().getFontMetrics(acceleratorFont)); + JComponent p = (JComponent) parent; - // add width of accelerator's text - d.width += rect.width + defaultAcceleratorLabelGap; + // The widest text so far. + Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth"); + int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue(); + if (pref.width < maxTextValue) + pref.width = maxTextValue; + else + p.putClientProperty("maxTextWidth", new Integer(pref.width)); - // adjust the heigth of the preferred size if necessary - if (d.height < rect.height) - d.height = rect.height; + // The widest accelerator so far. + Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth"); + int maxAccelValue = maxAccelWidth == null ? 0 + : maxAccelWidth.intValue(); + if (accelRect.width > maxAccelValue) + { + maxAccelValue = accelRect.width; + p.putClientProperty("maxAccelWidth", new Integer(accelRect.width)); + } + pref.width += maxAccelValue; + pref.width += defaultTextIconGap; } - if (checkIcon != null) + // Add arrow and check size if appropriate. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - d.width += checkIcon.getIconWidth() + defaultTextIconGap; - - if (checkIcon.getIconHeight() > d.height) - d.height = checkIcon.getIconHeight(); + pref.width += checkIconRect.width; + pref.width += defaultTextIconGap; + pref.width += arrowIconRect.width; + pref.width += defaultTextIconGap; } - if (arrowIcon != null && (c instanceof JMenu)) - { - int pWidth = m.getParent().getWidth(); - if (!((JMenu)c).isTopLevelMenu() && d.width < pWidth) - d.width = pWidth - - m.getInsets().left - m.getInsets().right; - else - d.width += arrowIcon.getIconWidth() + MenuGap; - - if (arrowIcon.getIconHeight() > d.height) - d.height = arrowIcon.getIconHeight(); - } - - return d; + // Add a gap ~2 times as wide as the defaultTextIconGap. + pref.width += 2 * defaultTextIconGap; + + // Respect the insets of the menu item. + Insets i = m.getInsets(); + pref.width += i.left + i.right; + pref.height += i.top + i.bottom; + + // Return a copy, so that nobody messes with our textRect. + return pref.getSize(); } /** @@ -541,7 +607,7 @@ public class BasicMenuItemUI extends MenuItemUI */ public void paint(Graphics g, JComponent c) { - paintMenuItem(g, c, checkIcon, arrowIcon, c.getBackground(), + paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground, c.getForeground(), defaultTextIconGap); } @@ -560,16 +626,18 @@ public class BasicMenuItemUI extends MenuItemUI // Menu item is considered to be highlighted when it is selected. // But we don't want to paint the background of JCheckBoxMenuItems ButtonModel mod = menuItem.getModel(); - if (menuItem.isContentAreaFilled()) + Color saved = g.getColor(); + if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected())) { - if ((menuItem.isSelected() && checkIcon == null) || (mod != null && - mod.isArmed()) - && (menuItem.getParent() instanceof MenuElement)) - g.setColor(selectionBackground); - else - g.setColor(bgColor); + g.setColor(bgColor); + g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); + } + else if (menuItem.isOpaque()) + { + g.setColor(menuItem.getBackground()); g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight()); - } + } + g.setColor(saved); } /** @@ -595,87 +663,123 @@ public class BasicMenuItemUI extends MenuItemUI 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); + + // Fetch fonts. + Font oldFont = g.getFont(); + Font font = c.getFont(); + g.setFont(font); + FontMetrics accelFm = m.getFontMetrics(acceleratorFont); + + // Create accelerator string. + String accelText = getAcceleratorString(m); + + // Layout menu item. The result gets stored in the rectangle fields + // of this class. + layoutMenuItem(m, accelText); + + // Paint the background. paintBackground(g, m, background); - /* - * MenuItems insets are equal to menuItems margin, space between text and - * menuItems border. We need to paint insets region as well. - */ - Insets insets = m.getInsets(); - br.x -= insets.left; - br.y -= insets.top; - br.width += insets.right + insets.left; - br.height += insets.top + insets.bottom; + Color oldColor = g.getColor(); - // If this menu item is a JCheckBoxMenuItem then paint check icon + // Paint the check icon. if (checkIcon != null) { - 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; + checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y); + } + + // 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(); + } + + if (icon != null) + { + icon.paintIcon(m, g, iconRect.x, iconRect.y); + } } - // if this is a submenu, then paint arrow icon to indicate it. - if (arrowIcon != null && (c instanceof JMenu)) + // Paint the text. + String text = m.getText(); + if (text != null) { - if (!((JMenu) c).isTopLevelMenu()) + // Handle HTML. + View html = (View) m.getClientProperty(BasicHTML.propertyKey); + if (html != null) + { + html.paint(g, textRect); + } + else { - int width = arrowIcon.getIconWidth(); - int height = arrowIcon.getIconHeight(); - int offset = (vr.height - height) / 2; - arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset); + paintText(g, m, textRect, text); } } - // 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 accelerator text. + if (! accelText.equals("")) + { + // Align the accelerator text. In getPreferredMenuItemSize() we + // store a client property 'maxAccelWidth' in the parent which holds + // the maximum accelerator width for the children of this parent. + // We use this here to align the accelerators properly. + int accelOffset = 0; + Container parent = m.getParent(); + if (parent != null && parent instanceof JComponent) + { + JComponent p = (JComponent) parent; + Integer maxAccelWidth = + (Integer) p.getClientProperty("maxAccelWidth"); + int maxAccelValue = maxAccelWidth == null ? 0 + : maxAccelWidth.intValue(); + accelOffset = maxAccelValue - accelRect.width; + } - // paint accelerator - String acceleratorText = ""; + 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 - accelOffset, + accelRect.y + accelFm.getAscent()); + } - if (m.getAccelerator() != null) + // Paint arrow. + if (arrowIcon != null + && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - 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); - - paintAccelerator(g, m, ar, acceleratorText); + arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y); } + + g.setFont(oldFont); + g.setColor(oldColor); + } /** @@ -860,37 +964,6 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * 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. @@ -1139,4 +1212,134 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.repaint(); } } + + /** + * A helper method to create the accelerator string from the menu item's + * accelerator property. The returned string is empty if there is + * no accelerator defined. + * + * @param m the menu item + * + * @return the accelerator string, not null + */ + private String getAcceleratorString(JMenuItem m) + { + // 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(); + } + return accelText; + } + + /** + * A helper method that lays out the menu item. The layout is stored + * in the fields of this class. + * + * @param m the menu item to layout + * @param accelText the accelerator text + */ + private void layoutMenuItem(JMenuItem m, String accelText) + { + int width = m.getWidth(); + int height = m.getHeight(); + + // 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(); + viewRect.x += insets.left; + viewRect.y += insets.top; + viewRect.width -= (insets.left + insets.right); + viewRect.height -= (insets.top + insets.bottom); + + // Fetch the fonts. + Font font = m.getFont(); + FontMetrics fm = m.getFontMetrics(font); + FontMetrics accelFm = m.getFontMetrics(acceleratorFont); + + String text = m.getText(); + SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(), + m.getVerticalAlignment(), + m.getHorizontalAlignment(), + m.getVerticalTextPosition(), + m.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + defaultTextIconGap); + + // Initialize accelerator width and height. + if (! accelText.equals("")) + { + accelRect.width = accelFm.stringWidth(accelText); + accelRect.height = accelFm.getHeight(); + } + + // Initialize check and arrow icon width and height. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + if (checkIcon != null) + { + checkIconRect.width = checkIcon.getIconWidth(); + checkIconRect.height = checkIcon.getIconHeight(); + } + if (arrowIcon != null) + { + arrowIconRect.width = arrowIcon.getIconWidth(); + arrowIconRect.height = arrowIcon.getIconHeight(); + } + } + + // The union of the icon and text of the menu item is the 'label area'. + cachedRect.setBounds(textRect); + Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x, + iconRect.y, + iconRect.width, + iconRect.height, + cachedRect); + 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); + } + } } |