--- a/jdk/src/share/classes/javax/swing/SwingUtilities.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/SwingUtilities.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -974,6 +974,7 @@
boolean textIsEmpty = (text == null) || text.equals("");
int lsb = 0;
+ int rsb = 0;
/* Unless both text and icon are non-null, we effectively ignore
* the value of textIconGap.
*/
@@ -1015,7 +1016,7 @@
if (lsb < 0) {
textR.width -= lsb;
}
- int rsb = SwingUtilities2.getRightSideBearing(c, fm, text);
+ rsb = SwingUtilities2.getRightSideBearing(c, fm, text);
if (rsb > 0) {
textR.width += rsb;
}
@@ -1118,6 +1119,11 @@
// lsb is negative. Shift the x location so that the text is
// visually drawn at the right location.
textR.x -= lsb;
+
+ textR.width += lsb;
+ }
+ if (rsb > 0) {
+ textR.width -= rsb;
}
return text;
--- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,6 @@
package javax.swing.plaf.basic;
-import sun.swing.MenuItemCheckIconFactory;
-import sun.swing.SwingUtilities2;
-import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
@@ -39,8 +36,7 @@
import javax.swing.plaf.*;
import javax.swing.text.View;
-import sun.swing.UIAction;
-import sun.swing.StringUIClientPropertyKey;
+import sun.swing.*;
/**
* BasicMenuItem implementation
@@ -91,24 +87,6 @@
private static final boolean VERBOSE = false; // show reuse hits/misses
private static final boolean DEBUG = false; // show bad params, misc.
- // Allows to reuse layoutInfo object.
- // Shouldn't be used directly. Use getLayoutInfo() instead.
- private final transient LayoutInfo layoutInfo = new LayoutInfo();
-
- /* Client Property keys for calculation of maximal widths */
- static final StringUIClientPropertyKey MAX_ARROW_WIDTH =
- new StringUIClientPropertyKey("maxArrowWidth");
- static final StringUIClientPropertyKey MAX_CHECK_WIDTH =
- new StringUIClientPropertyKey("maxCheckWidth");
- static final StringUIClientPropertyKey MAX_ICON_WIDTH =
- new StringUIClientPropertyKey("maxIconWidth");
- static final StringUIClientPropertyKey MAX_TEXT_WIDTH =
- new StringUIClientPropertyKey("maxTextWidth");
- static final StringUIClientPropertyKey MAX_ACC_WIDTH =
- new StringUIClientPropertyKey("maxAccWidth");
- static final StringUIClientPropertyKey MAX_LABEL_WIDTH =
- new StringUIClientPropertyKey("maxLabelWidth");
-
static void loadActionMap(LazyActionMap map) {
// NOTE: BasicMenuUI also calls into this method.
map.put(new Actions(Actions.CLICK));
@@ -199,13 +177,14 @@
//In case of column layout, .checkIconFactory is defined for this UI,
//the icon is compatible with it and useCheckAndArrow() is true,
//then the icon is handled by the checkIcon.
- boolean isColumnLayout = LayoutInfo.isColumnLayout(
+ boolean isColumnLayout = MenuItemLayoutHelper.isColumnLayout(
BasicGraphicsUtils.isLeftToRight(menuItem), menuItem);
if (isColumnLayout) {
MenuItemCheckIconFactory iconFactory =
(MenuItemCheckIconFactory) UIManager.get(prefix
+ ".checkIconFactory");
- if (iconFactory != null && useCheckAndArrow()
+ if (iconFactory != null
+ && MenuItemLayoutHelper.useCheckAndArrow(menuItem)
&& iconFactory.isCompatible(checkIcon, prefix)) {
checkIcon = iconFactory.getIcon(menuItem);
}
@@ -256,20 +235,7 @@
uninstallComponents(menuItem);
uninstallListeners();
uninstallKeyboardActions();
-
-
- // Remove values from the parent's Client Properties.
- JComponent p = getMenuItemParent(menuItem);
- if(p != null) {
- p.putClientProperty(BasicMenuItemUI.MAX_ARROW_WIDTH, null );
- p.putClientProperty(BasicMenuItemUI.MAX_CHECK_WIDTH, null );
- p.putClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH, null );
- p.putClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH, null );
- p.putClientProperty(BasicMenuItemUI.MAX_ICON_WIDTH, null );
- p.putClientProperty(BasicMenuItemUI.MAX_LABEL_WIDTH, null );
- p.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null );
- }
-
+ MenuItemLayoutHelper.clearUsedParentClientProperties(menuItem);
menuItem = null;
}
@@ -405,19 +371,6 @@
return d;
}
- // Returns parent of this component if it is not a top-level menu
- // Otherwise returns null
- private static JComponent getMenuItemParent(JMenuItem mi) {
- Container parent = mi.getParent();
- if ((parent instanceof JComponent) &&
- (!(mi instanceof JMenu) ||
- !((JMenu)mi).isTopLevelMenu())) {
- return (JComponent) parent;
- } else {
- return null;
- }
- }
-
protected Dimension getPreferredMenuItemSize(JComponent c,
Icon checkIcon,
Icon arrowIcon,
@@ -447,32 +400,36 @@
// the icon and text when user points a menu item by mouse.
JMenuItem mi = (JMenuItem) c;
- LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon,
- createMaxViewRect(), defaultTextIconGap, acceleratorDelimiter,
- BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont,
- useCheckAndArrow(), getPropertyPrefix());
+ MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
+ arrowIcon, MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap,
+ acceleratorDelimiter, BasicGraphicsUtils.isLeftToRight(mi),
+ mi.getFont(), acceleratorFont,
+ MenuItemLayoutHelper.useCheckAndArrow(menuItem),
+ getPropertyPrefix());
Dimension result = new Dimension();
// Calculate the result width
- result.width = li.leadingGap;
- addWidth(li.maxCheckWidth, li.afterCheckIconGap, result);
+ result.width = lh.getLeadingGap();
+ MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(),
+ lh.getAfterCheckIconGap(), result);
// Take into account mimimal text offset.
- if ((!li.isTopLevelMenu)
- && (li.minTextOffset > 0)
- && (result.width < li.minTextOffset)) {
- result.width = li.minTextOffset;
+ if ((!lh.isTopLevelMenu())
+ && (lh.getMinTextOffset() > 0)
+ && (result.width < lh.getMinTextOffset())) {
+ result.width = lh.getMinTextOffset();
}
- addWidth(li.maxLabelWidth, li.gap, result);
- addWidth(li.maxAccWidth, li.gap, result);
- addWidth(li.maxArrowWidth, li.gap, result);
+ MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), lh.getGap(), result);
+ MenuItemLayoutHelper.addMaxWidth(lh.getAccSize(), lh.getGap(), result);
+ MenuItemLayoutHelper.addMaxWidth(lh.getArrowSize(), lh.getGap(), result);
// Calculate the result height
- result.height = max(li.checkRect.height, li.labelRect.height,
- li.accRect.height, li.arrowRect.height);
+ result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(),
+ lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(),
+ lh.getArrowSize().getHeight());
// Take into account menu item insets
- Insets insets = li.mi.getInsets();
+ Insets insets = lh.getMenuItem().getInsets();
if(insets != null) {
result.width += insets.left + insets.right;
result.height += insets.top + insets.bottom;
@@ -492,500 +449,9 @@
result.height++;
}
- li.clear();
return result;
}
- private Rectangle createMaxViewRect() {
- return new Rectangle(0,0,Short.MAX_VALUE, Short.MAX_VALUE);
- }
-
- private void addWidth(int width, int gap, Dimension result) {
- if (width > 0) {
- result.width += width + gap;
- }
- }
-
- private static int max(int... values) {
- int maxValue = Integer.MIN_VALUE;
- for (int i : values) {
- if (i > maxValue) {
- maxValue = i;
- }
- }
- return maxValue;
- }
-
- // LayoutInfo helps to calculate preferred size and to paint a menu item
- private static class LayoutInfo {
- JMenuItem mi;
- JComponent miParent;
-
- FontMetrics fm;
- FontMetrics accFm;
-
- Icon icon;
- Icon checkIcon;
- Icon arrowIcon;
- String text;
- String accText;
-
- boolean isColumnLayout;
- boolean useCheckAndArrow;
- boolean isLeftToRight;
- boolean isTopLevelMenu;
- View htmlView;
-
- int verticalAlignment;
- int horizontalAlignment;
- int verticalTextPosition;
- int horizontalTextPosition;
- int gap;
- int leadingGap;
- int afterCheckIconGap;
- int minTextOffset;
-
- Rectangle viewRect;
- Rectangle iconRect;
- Rectangle textRect;
- Rectangle accRect;
- Rectangle checkRect;
- Rectangle arrowRect;
- Rectangle labelRect;
-
- int origIconWidth;
- int origTextWidth;
- int origAccWidth;
- int origCheckWidth;
- int origArrowWidth;
-
- int maxIconWidth;
- int maxTextWidth;
- int maxAccWidth;
- int maxCheckWidth;
- int maxArrowWidth;
- int maxLabelWidth;
-
- // Empty constructor helps to create "final" LayoutInfo object
- public LayoutInfo() {
- }
-
- public LayoutInfo(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
- Rectangle viewRect, int gap, String accDelimiter,
- boolean isLeftToRight, Font acceleratorFont,
- boolean useCheckAndArrow, String propertyPrefix) {
- reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
- isLeftToRight, acceleratorFont, useCheckAndArrow,
- propertyPrefix);
- }
-
- // Allows to reuse a LayoutInfo object
- public void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
- Rectangle viewRect, int gap, String accDelimiter,
- boolean isLeftToRight, Font acceleratorFont,
- boolean useCheckAndArrow, String propertyPrefix) {
- this.mi = mi;
- this.miParent = getMenuItemParent(mi);
- this.accText = getAccText(accDelimiter);
- this.verticalAlignment = mi.getVerticalAlignment();
- this.horizontalAlignment = mi.getHorizontalAlignment();
- this.verticalTextPosition = mi.getVerticalTextPosition();
- this.horizontalTextPosition = mi.getHorizontalTextPosition();
- this.useCheckAndArrow = useCheckAndArrow;
- this.fm = mi.getFontMetrics(mi.getFont());
- this.accFm = mi.getFontMetrics(acceleratorFont);
- this.isLeftToRight = isLeftToRight;
- this.isColumnLayout = isColumnLayout();
- this.isTopLevelMenu = (this.miParent == null)? true : false;
- this.checkIcon = checkIcon;
- this.icon = getIcon(propertyPrefix);
- this.arrowIcon = arrowIcon;
- this.text = mi.getText();
- this.gap = gap;
- this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix);
- this.minTextOffset = getMinTextOffset(propertyPrefix);
- this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey);
-
- this.viewRect = viewRect;
- this.iconRect = new Rectangle();
- this.textRect = new Rectangle();
- this.accRect = new Rectangle();
- this.checkRect = new Rectangle();
- this.arrowRect = new Rectangle();
- this.labelRect = new Rectangle();
-
- calcWidthsAndHeights();
- this.origIconWidth = iconRect.width;
- this.origTextWidth = textRect.width;
- this.origAccWidth = accRect.width;
- this.origCheckWidth = checkRect.width;
- this.origArrowWidth = arrowRect.width;
-
- calcMaxWidths();
- this.leadingGap = getLeadingGap(propertyPrefix);
- calcMaxTextOffset();
- }
-
- // Clears fields to remove all links to other objects
- // to prevent memory leaks
- public void clear() {
- mi = null;
- miParent = null;
- fm = null;
- accFm = null;
- icon = null;
- checkIcon = null;
- arrowIcon = null;
- text = null;
- accText = null;
- htmlView = null;
- viewRect = null;
- iconRect = null;
- textRect = null;
- accRect = null;
- checkRect = null;
- arrowRect = null;
- labelRect = null;
- }
-
- private String getAccText(String acceleratorDelimiter) {
- String accText = "";
- KeyStroke accelerator = mi.getAccelerator();
- if (accelerator != null) {
- int modifiers = accelerator.getModifiers();
- if (modifiers > 0) {
- accText = KeyEvent.getKeyModifiersText(modifiers);
- accText += acceleratorDelimiter;
- }
- int keyCode = accelerator.getKeyCode();
- if (keyCode != 0) {
- accText += KeyEvent.getKeyText(keyCode);
- } else {
- accText += accelerator.getKeyChar();
- }
- }
- return accText;
- }
-
- // In case of column layout, .checkIconFactory is defined for this UI,
- // the icon is compatible with it and useCheckAndArrow() is true,
- // then the icon is handled by the checkIcon.
- private Icon getIcon(String propertyPrefix) {
- Icon icon = null;
- MenuItemCheckIconFactory iconFactory =
- (MenuItemCheckIconFactory) UIManager.get(propertyPrefix
- + ".checkIconFactory");
- if (!isColumnLayout || !useCheckAndArrow || iconFactory == null
- || !iconFactory.isCompatible(checkIcon, propertyPrefix)) {
- icon = mi.getIcon();
- }
- return icon;
- }
-
- private int getMinTextOffset(String propertyPrefix) {
- int minimumTextOffset = 0;
- Object minimumTextOffsetObject =
- UIManager.get(propertyPrefix + ".minimumTextOffset");
- if (minimumTextOffsetObject instanceof Integer) {
- minimumTextOffset = (Integer) minimumTextOffsetObject;
- }
- return minimumTextOffset;
- }
-
- private int getAfterCheckIconGap(String propertyPrefix) {
- int afterCheckIconGap = gap;
- Object afterCheckIconGapObject =
- UIManager.get(propertyPrefix + ".afterCheckIconGap");
- if (afterCheckIconGapObject instanceof Integer) {
- afterCheckIconGap = (Integer) afterCheckIconGapObject;
- }
- return afterCheckIconGap;
- }
-
- private int getLeadingGap(String propertyPrefix) {
- if (maxCheckWidth > 0) {
- return getCheckOffset(propertyPrefix);
- } else {
- return gap; // There is no any check icon
- }
- }
-
- private int getCheckOffset(String propertyPrefix) {
- int checkIconOffset = gap;
- Object checkIconOffsetObject =
- UIManager.get(propertyPrefix + ".checkIconOffset");
- if (checkIconOffsetObject instanceof Integer) {
- checkIconOffset = (Integer) checkIconOffsetObject;
- }
- return checkIconOffset;
- }
-
- private void calcWidthsAndHeights()
- {
- // iconRect
- if (icon != null) {
- iconRect.width = icon.getIconWidth();
- iconRect.height = icon.getIconHeight();
- }
-
- // accRect
- if (!accText.equals("")) {
- accRect.width = SwingUtilities2.stringWidth(
- mi, accFm, accText);
- accRect.height = accFm.getHeight();
- }
-
- // textRect
- if (text == null) {
- text = "";
- } else if (!text.equals("")) {
- if (htmlView != null) {
- // Text is HTML
- textRect.width =
- (int) htmlView.getPreferredSpan(View.X_AXIS);
- textRect.height =
- (int) htmlView.getPreferredSpan(View.Y_AXIS);
- } else {
- // Text isn't HTML
- textRect.width =
- SwingUtilities2.stringWidth(mi, fm, text);
- textRect.height = fm.getHeight();
- }
- }
-
- if (useCheckAndArrow) {
- // checkIcon
- if (checkIcon != null) {
- checkRect.width = checkIcon.getIconWidth();
- checkRect.height = checkIcon.getIconHeight();
- }
- // arrowRect
- if (arrowIcon != null) {
- arrowRect.width = arrowIcon.getIconWidth();
- arrowRect.height = arrowIcon.getIconHeight();
- }
- }
-
- // labelRect
- if (isColumnLayout) {
- labelRect.width = iconRect.width + textRect.width + gap;
- labelRect.height = max(checkRect.height, iconRect.height,
- textRect.height, accRect.height, arrowRect.height);
- } else {
- textRect = new Rectangle();
- iconRect = new Rectangle();
- SwingUtilities.layoutCompoundLabel(mi, fm, text, icon,
- verticalAlignment, horizontalAlignment,
- verticalTextPosition, horizontalTextPosition,
- viewRect, iconRect, textRect, gap);
- labelRect = iconRect.union(textRect);
- }
- }
-
- private void calcMaxWidths() {
- maxCheckWidth = calcMaxValue(BasicMenuItemUI.MAX_CHECK_WIDTH,
- checkRect.width);
- maxArrowWidth = calcMaxValue(BasicMenuItemUI.MAX_ARROW_WIDTH,
- arrowRect.width);
- maxAccWidth = calcMaxValue(BasicMenuItemUI.MAX_ACC_WIDTH,
- accRect.width);
-
- if (isColumnLayout) {
- maxIconWidth = calcMaxValue(BasicMenuItemUI.MAX_ICON_WIDTH,
- iconRect.width);
- maxTextWidth = calcMaxValue(BasicMenuItemUI.MAX_TEXT_WIDTH,
- textRect.width);
- int curGap = gap;
- if ((maxIconWidth == 0) || (maxTextWidth == 0)) {
- curGap = 0;
- }
- maxLabelWidth =
- calcMaxValue(BasicMenuItemUI.MAX_LABEL_WIDTH,
- maxIconWidth + maxTextWidth + curGap);
- } else {
- // We shouldn't use current icon and text widths
- // in maximal widths calculation for complex layout.
- maxIconWidth = getParentIntProperty(BasicMenuItemUI.MAX_ICON_WIDTH);
- maxLabelWidth = calcMaxValue(BasicMenuItemUI.MAX_LABEL_WIDTH,
- labelRect.width);
- // If maxLabelWidth is wider
- // than the widest icon + the widest text + gap,
- // we should update the maximal text witdh
- int candidateTextWidth = maxLabelWidth - maxIconWidth;
- if (maxIconWidth > 0) {
- candidateTextWidth -= gap;
- }
- maxTextWidth = calcMaxValue(BasicMenuItemUI.MAX_TEXT_WIDTH,
- candidateTextWidth);
- }
- }
-
- // Calculates and returns maximal value
- // through specified parent component client property.
- private int calcMaxValue(Object propertyName, int value) {
- // Get maximal value from parent client property
- int maxValue = getParentIntProperty(propertyName);
- // Store new maximal width in parent client property
- if (value > maxValue) {
- if (miParent != null) {
- miParent.putClientProperty(propertyName, value);
- }
- return value;
- } else {
- return maxValue;
- }
- }
-
- // Returns parent client property as int
- private int getParentIntProperty(Object propertyName) {
- Object value = null;
- if (miParent != null) {
- value = miParent.getClientProperty(propertyName);
- }
- if ((value == null) || !(value instanceof Integer)){
- value = 0;
- }
- return (Integer)value;
- }
-
- private boolean isColumnLayout() {
- return isColumnLayout(isLeftToRight, horizontalAlignment,
- horizontalTextPosition, verticalTextPosition);
- }
-
- public static boolean isColumnLayout(boolean isLeftToRight,
- JMenuItem mi) {
- assert(mi != null);
- return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(),
- mi.getHorizontalTextPosition(), mi.getVerticalTextPosition());
- }
-
- // Answers should we do column layout for a menu item or not.
- // We do it when a user doesn't set any alignments
- // and text positions manually, except the vertical alignment.
- public static boolean isColumnLayout( boolean isLeftToRight,
- int horizontalAlignment, int horizontalTextPosition,
- int verticalTextPosition) {
- if (verticalTextPosition != SwingConstants.CENTER) {
- return false;
- }
- if (isLeftToRight) {
- if (horizontalAlignment != SwingConstants.LEADING
- && horizontalAlignment != SwingConstants.LEFT) {
- return false;
- }
- if (horizontalTextPosition != SwingConstants.TRAILING
- && horizontalTextPosition != SwingConstants.RIGHT) {
- return false;
- }
- } else {
- if (horizontalAlignment != SwingConstants.LEADING
- && horizontalAlignment != SwingConstants.RIGHT) {
- return false;
- }
- if (horizontalTextPosition != SwingConstants.TRAILING
- && horizontalTextPosition != SwingConstants.LEFT) {
- return false;
- }
- }
- return true;
- }
-
- // Calculates maximal text offset.
- // It is required for some L&Fs (ex: Vista L&F).
- // The offset is meaningful only for L2R column layout.
- private void calcMaxTextOffset() {
- if (!isColumnLayout || !isLeftToRight) {
- return;
- }
-
- // Calculate the current text offset
- int offset = viewRect.x + leadingGap + maxCheckWidth
- + afterCheckIconGap + maxIconWidth + gap;
- if (maxCheckWidth == 0) {
- offset -= afterCheckIconGap;
- }
- if (maxIconWidth == 0) {
- offset -= gap;
- }
-
- // maximal text offset shouldn't be less than minimal text offset;
- if (offset < minTextOffset) {
- offset = minTextOffset;
- }
-
- // Calculate and store the maximal text offset
- calcMaxValue(BASICMENUITEMUI_MAX_TEXT_OFFSET, offset);
- }
-
- public String toString() {
- StringBuilder result = new StringBuilder();
- result.append(super.toString()).append("\n");
- result.append("accFm = ").append(accFm).append("\n");
- result.append("accRect = ").append(accRect).append("\n");
- result.append("accText = ").append(accText).append("\n");
- result.append("afterCheckIconGap = ").append(afterCheckIconGap)
- .append("\n");
- result.append("arrowIcon = ").append(arrowIcon).append("\n");
- result.append("arrowRect = ").append(arrowRect).append("\n");
- result.append("checkIcon = ").append(checkIcon).append("\n");
- result.append("checkRect = ").append(checkRect).append("\n");
- result.append("fm = ").append(fm).append("\n");
- result.append("gap = ").append(gap).append("\n");
- result.append("horizontalAlignment = ").append(horizontalAlignment)
- .append("\n");
- result.append("horizontalTextPosition = ")
- .append(horizontalTextPosition).append("\n");
- result.append("htmlView = ").append(htmlView).append("\n");
- result.append("icon = ").append(icon).append("\n");
- result.append("iconRect = ").append(iconRect).append("\n");
- result.append("isColumnLayout = ").append(isColumnLayout).append("\n");
- result.append("isLeftToRight = ").append(isLeftToRight).append("\n");
- result.append("isTopLevelMenu = ").append(isTopLevelMenu).append("\n");
- result.append("labelRect = ").append(labelRect).append("\n");
- result.append("leadingGap = ").append(leadingGap).append("\n");
- result.append("maxAccWidth = ").append(maxAccWidth).append("\n");
- result.append("maxArrowWidth = ").append(maxArrowWidth).append("\n");
- result.append("maxCheckWidth = ").append(maxCheckWidth).append("\n");
- result.append("maxIconWidth = ").append(maxIconWidth).append("\n");
- result.append("maxLabelWidth = ").append(maxLabelWidth).append("\n");
- result.append("maxTextWidth = ").append(maxTextWidth).append("\n");
- result.append("maxTextOffset = ")
- .append(getParentIntProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET))
- .append("\n");
- result.append("mi = ").append(mi).append("\n");
- result.append("minTextOffset = ").append(minTextOffset).append("\n");
- result.append("miParent = ").append(miParent).append("\n");
- result.append("origAccWidth = ").append(origAccWidth).append("\n");
- result.append("origArrowWidth = ").append(origArrowWidth).append("\n");
- result.append("origCheckWidth = ").append(origCheckWidth).append("\n");
- result.append("origIconWidth = ").append(origIconWidth).append("\n");
- result.append("origTextWidth = ").append(origTextWidth).append("\n");
- result.append("text = ").append(text).append("\n");
- result.append("textRect = ").append(textRect).append("\n");
- result.append("useCheckAndArrow = ").append(useCheckAndArrow)
- .append("\n");
- result.append("verticalAlignment = ").append(verticalAlignment)
- .append("\n");
- result.append("verticalTextPosition = ")
- .append(verticalTextPosition).append("\n");
- result.append("viewRect = ").append(viewRect).append("\n");
- return result.toString();
- }
- } // End of LayoutInfo
-
- // Reuses layoutInfo object to reduce the amount of produced garbage
- private LayoutInfo getLayoutInfo(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
- Rectangle viewRect, int gap, String accDelimiter,
- boolean isLeftToRight, Font acceleratorFont,
- boolean useCheckAndArrow, String propertyPrefix) {
- // layoutInfo is final and always not null
- layoutInfo.reset(mi, checkIcon, arrowIcon, viewRect,
- gap, accDelimiter, isLeftToRight, acceleratorFont,
- useCheckAndArrow, propertyPrefix);
- return layoutInfo;
- }
-
/**
* We draw the background in paintMenuItem()
* so override update (which fills the background of opaque
@@ -1016,122 +482,132 @@
Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
applyInsets(viewRect, mi.getInsets());
- LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon,
- viewRect, defaultTextIconGap, acceleratorDelimiter,
- BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont,
- useCheckAndArrow(), getPropertyPrefix());
- layoutMenuItem(li);
+ MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
+ arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter,
+ BasicGraphicsUtils.isLeftToRight(mi), mi.getFont(),
+ acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem),
+ getPropertyPrefix());
+ MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
paintBackground(g, mi, background);
- paintCheckIcon(g, li, holdc, foreground);
- paintIcon(g, li, holdc);
- paintText(g, li);
- paintAccText(g, li);
- paintArrowIcon(g, li, foreground);
+ paintCheckIcon(g, lh, lr, holdc, foreground);
+ paintIcon(g, lh, lr, holdc);
+ paintText(g, lh, lr);
+ paintAccText(g, lh, lr);
+ paintArrowIcon(g, lh, lr, foreground);
// Restore original graphics font and color
g.setColor(holdc);
g.setFont(holdf);
-
- li.clear();
}
- private void paintIcon(Graphics g, LayoutInfo li, Color holdc) {
- if (li.icon != null) {
+ private void paintIcon(Graphics g, MenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr, Color holdc) {
+ if (lh.getIcon() != null) {
Icon icon;
- ButtonModel model = li.mi.getModel();
+ ButtonModel model = lh.getMenuItem().getModel();
if (!model.isEnabled()) {
- icon = li.mi.getDisabledIcon();
+ icon = lh.getMenuItem().getDisabledIcon();
} else if (model.isPressed() && model.isArmed()) {
- icon = li.mi.getPressedIcon();
+ icon = lh.getMenuItem().getPressedIcon();
if (icon == null) {
// Use default icon
- icon = li.mi.getIcon();
+ icon = lh.getMenuItem().getIcon();
}
} else {
- icon = li.mi.getIcon();
+ icon = lh.getMenuItem().getIcon();
}
if (icon != null) {
- icon.paintIcon(li.mi, g, li.iconRect.x, li.iconRect.y);
+ icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x,
+ lr.getIconRect().y);
g.setColor(holdc);
}
}
}
- private void paintCheckIcon(Graphics g, LayoutInfo li,
+ private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr,
Color holdc, Color foreground) {
- if (li.checkIcon != null) {
- ButtonModel model = li.mi.getModel();
- if (model.isArmed()
- || (li.mi instanceof JMenu && model.isSelected())) {
+ if (lh.getCheckIcon() != null) {
+ ButtonModel model = lh.getMenuItem().getModel();
+ if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
+ && model.isSelected())) {
g.setColor(foreground);
} else {
g.setColor(holdc);
}
- if (li.useCheckAndArrow) {
- li.checkIcon.paintIcon(li.mi, g, li.checkRect.x,
- li.checkRect.y);
+ if (lh.useCheckAndArrow()) {
+ lh.getCheckIcon().paintIcon(lh.getMenuItem(), g,
+ lr.getCheckRect().x, lr.getCheckRect().y);
}
g.setColor(holdc);
}
}
- private void paintAccText(Graphics g, LayoutInfo li) {
- if (!li.accText.equals("")) {
- ButtonModel model = li.mi.getModel();
- g.setFont(acceleratorFont);
+ private void paintAccText(Graphics g, MenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (!lh.getAccText().equals("")) {
+ ButtonModel model = lh.getMenuItem().getModel();
+ g.setFont(lh.getAccFontMetrics().getFont());
if (!model.isEnabled()) {
// *** paint the accText disabled
if (disabledForeground != null) {
g.setColor(disabledForeground);
- SwingUtilities2.drawString(li.mi, g, li.accText,
- li.accRect.x,
- li.accRect.y + li.accFm.getAscent());
+ SwingUtilities2.drawString(lh.getMenuItem(), g,
+ lh.getAccText(), lr.getAccRect().x,
+ lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
} else {
- g.setColor(li.mi.getBackground().brighter());
- SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x,
- li.accRect.y + li.accFm.getAscent());
- g.setColor(li.mi.getBackground().darker());
- SwingUtilities2.drawString(li.mi, g, li.accText,
- li.accRect.x - 1,
- li.accRect.y + li.accFm.getAscent() - 1);
+ g.setColor(lh.getMenuItem().getBackground().brighter());
+ SwingUtilities2.drawString(lh.getMenuItem(), g,
+ lh.getAccText(), lr.getAccRect().x,
+ lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
+ g.setColor(lh.getMenuItem().getBackground().darker());
+ SwingUtilities2.drawString(lh.getMenuItem(), g,
+ lh.getAccText(), lr.getAccRect().x - 1,
+ lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1);
}
} else {
// *** paint the accText normally
- if (model.isArmed() ||
- (li.mi instanceof JMenu && model.isSelected())) {
+ if (model.isArmed()
+ || (lh.getMenuItem() instanceof JMenu
+ && model.isSelected())) {
g.setColor(acceleratorSelectionForeground);
} else {
g.setColor(acceleratorForeground);
}
- SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x,
- li.accRect.y + li.accFm.getAscent());
+ SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(),
+ lr.getAccRect().x, lr.getAccRect().y +
+ lh.getAccFontMetrics().getAscent());
}
}
}
- private void paintText(Graphics g, LayoutInfo li) {
- if (!li.text.equals("")) {
- if (li.htmlView != null) {
+ private void paintText(Graphics g, MenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (!lh.getText().equals("")) {
+ if (lh.getHtmlView() != null) {
// Text is HTML
- li.htmlView.paint(g, li.textRect);
+ lh.getHtmlView().paint(g, lr.getTextRect());
} else {
// Text isn't HTML
- paintText(g, li.mi, li.textRect, li.text);
+ paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText());
}
}
}
- private void paintArrowIcon(Graphics g, LayoutInfo li, Color foreground) {
- if (li.arrowIcon != null) {
- ButtonModel model = li.mi.getModel();
- if (model.isArmed()
- || (li.mi instanceof JMenu && model.isSelected())) {
+ private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr,
+ Color foreground) {
+ if (lh.getArrowIcon() != null) {
+ ButtonModel model = lh.getMenuItem().getModel();
+ if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
+ && model.isSelected())) {
g.setColor(foreground);
}
- if (li.useCheckAndArrow) {
- li.arrowIcon.paintIcon(li.mi, g, li.arrowRect.x, li.arrowRect.y);
+ if (lh.useCheckAndArrow()) {
+ lh.getArrowIcon().paintIcon(lh.getMenuItem(), g,
+ lr.getArrowRect().x, lr.getArrowRect().y);
}
}
}
@@ -1216,346 +692,6 @@
}
}
-
- /**
- * Layout icon, text, check icon, accelerator text and arrow icon
- * in the viewRect and return their positions.
- *
- * If horizontalAlignment, verticalTextPosition and horizontalTextPosition
- * are default (user doesn't set any manually) the layouting algorithm is:
- * Elements are layouted in the five columns:
- * check icon + icon + text + accelerator text + arrow icon
- *
- * In the other case elements are layouted in the four columns:
- * check icon + label + accelerator text + arrow icon
- * Label is icon and text rectangles union.
- *
- * The order of columns can be reversed.
- * It depends on the menu item orientation.
- */
- private void layoutMenuItem(LayoutInfo li)
- {
- li.checkRect.width = li.maxCheckWidth;
- li.accRect.width = li.maxAccWidth;
- li.arrowRect.width = li.maxArrowWidth;
-
- if (li.isColumnLayout) {
- if (li.isLeftToRight) {
- doLTRColumnLayout(li);
- } else {
- doRTLColumnLayout(li);
- }
- } else {
- if (li.isLeftToRight) {
- doLTRComplexLayout(li);
- } else {
- doRTLComplexLayout(li);
- }
- }
-
- alignAccCheckAndArrowVertically(li);
- }
-
- // Aligns the accelertor text and the check and arrow icons vertically
- // with the center of the label rect.
- private void alignAccCheckAndArrowVertically(LayoutInfo li) {
- li.accRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2
- - (float)li.accRect.height/2);
- fixVerticalAlignment(li, li.accRect);
- if (li.useCheckAndArrow) {
- li.arrowRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2
- - (float)li.arrowRect.height/2);
- li.checkRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2
- - (float)li.checkRect.height/2);
- fixVerticalAlignment(li, li.arrowRect);
- fixVerticalAlignment(li, li.checkRect);
- }
- }
-
- // Fixes vertical alignment of all menu item elements if a rect.y
- // or (rect.y + rect.height) is out of viewRect bounds
- private void fixVerticalAlignment(LayoutInfo li, Rectangle r) {
- int delta = 0;
- if (r.y < li.viewRect.y) {
- delta = li.viewRect.y - r.y;
- } else if (r.y + r.height > li.viewRect.y + li.viewRect.height) {
- delta = li.viewRect.y + li.viewRect.height - r.y - r.height;
- }
- if (delta != 0) {
- li.checkRect.y += delta;
- li.iconRect.y += delta;
- li.textRect.y += delta;
- li.accRect.y += delta;
- li.arrowRect.y += delta;
- }
- }
-
- private void doLTRColumnLayout(LayoutInfo li) {
- // Set maximal width for all the five basic rects
- // (three other ones are already maximal)
- li.iconRect.width = li.maxIconWidth;
- li.textRect.width = li.maxTextWidth;
-
- // Set X coordinates
- // All rects will be aligned at the left side
- calcXPositionsL2R(li.viewRect.x, li.leadingGap, li.gap, li.checkRect,
- li.iconRect, li.textRect);
-
- // Tune afterCheckIconGap
- if (li.checkRect.width > 0) { // there is the afterCheckIconGap
- li.iconRect.x += li.afterCheckIconGap - li.gap;
- li.textRect.x += li.afterCheckIconGap - li.gap;
- }
-
- calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.gap,
- li.arrowRect, li.accRect);
-
- // Take into account minimal text offset
- int textOffset = li.textRect.x - li.viewRect.x;
- if (!li.isTopLevelMenu && (textOffset < li.minTextOffset)) {
- li.textRect.x += li.minTextOffset - textOffset;
- }
-
- // Take into account the left side bearings for text and accelerator text.
- fixTextRects(li);
-
- // Set Y coordinate for text and icon.
- // Y coordinates for other rects
- // will be calculated later in layoutMenuItem.
- calcTextAndIconYPositions(li);
-
- // Calculate valid X and Y coordinates for labelRect
- li.labelRect = li.textRect.union(li.iconRect);
- }
-
- private void doLTRComplexLayout(LayoutInfo li) {
- li.labelRect.width = li.maxLabelWidth;
-
- // Set X coordinates
- calcXPositionsL2R(li.viewRect.x, li.leadingGap, li.gap, li.checkRect,
- li.labelRect);
-
- // Tune afterCheckIconGap
- if (li.checkRect.width > 0) { // there is the afterCheckIconGap
- li.labelRect.x += li.afterCheckIconGap - li.gap;
- }
-
- calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.gap,
- li.arrowRect, li.accRect);
-
- // Take into account minimal text offset
- int labelOffset = li.labelRect.x - li.viewRect.x;
- if (!li.isTopLevelMenu && (labelOffset < li.minTextOffset)) {
- li.labelRect.x += li.minTextOffset - labelOffset;
- }
-
- // Take into account the left side bearing for accelerator text.
- // The LSB for text is taken into account in layoutCompoundLabel() below.
- fixAccTextRect(li);
-
- // Layout icon and text with SwingUtilities.layoutCompoundLabel()
- // within the labelRect
- li.textRect = new Rectangle();
- li.iconRect = new Rectangle();
- SwingUtilities.layoutCompoundLabel(
- li.mi, li.fm, li.text, li.icon, li.verticalAlignment,
- li.horizontalAlignment, li.verticalTextPosition,
- li.horizontalTextPosition, li.labelRect,
- li.iconRect, li.textRect, li.gap);
- }
-
- private void doRTLColumnLayout(LayoutInfo li) {
- // Set maximal width for all the five basic rects
- // (three other ones are already maximal)
- li.iconRect.width = li.maxIconWidth;
- li.textRect.width = li.maxTextWidth;
-
- // Set X coordinates
- calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.leadingGap,
- li.gap, li.checkRect, li.iconRect, li.textRect);
-
- // Tune the gap after check icon
- if (li.checkRect.width > 0) { // there is the gap after check icon
- li.iconRect.x -= li.afterCheckIconGap - li.gap;
- li.textRect.x -= li.afterCheckIconGap - li.gap;
- }
-
- calcXPositionsL2R(li.viewRect.x, li.gap, li.arrowRect,
- li.accRect);
-
- // Take into account minimal text offset
- int textOffset = (li.viewRect.x + li.viewRect.width)
- - (li.textRect.x + li.textRect.width);
- if (!li.isTopLevelMenu && (textOffset < li.minTextOffset)) {
- li.textRect.x -= li.minTextOffset - textOffset;
- }
-
- // Align icon, text, accelerator text, check icon and arrow icon
- // at the right side
- rightAlignAllRects(li);
-
- // Take into account the left side bearings for text and accelerator text.
- fixTextRects(li);
-
- // Set Y coordinates for text and icon.
- // Y coordinates for other rects
- // will be calculated later in layoutMenuItem.
- calcTextAndIconYPositions(li);
-
- // Calculate valid X and Y coordinate for labelRect
- li.labelRect = li.textRect.union(li.iconRect);
- }
-
- private void doRTLComplexLayout(LayoutInfo li) {
- li.labelRect.width = li.maxLabelWidth;
-
- // Set X coordinates
- calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.leadingGap,
- li.gap, li.checkRect, li.labelRect);
-
- // Tune the gap after check icon
- if (li.checkRect.width > 0) { // there is the gap after check icon
- li.labelRect.x -= li.afterCheckIconGap - li.gap;
- }
-
- calcXPositionsL2R(li.viewRect.x, li.gap, li.arrowRect,
- li.accRect);
-
- // Take into account minimal text offset
- int labelOffset = (li.viewRect.x + li.viewRect.width)
- - (li.labelRect.x + li.labelRect.width);
- if (!li.isTopLevelMenu && (labelOffset < li.minTextOffset)) {
- li.labelRect.x -= li.minTextOffset - labelOffset;
- }
-
- // Align icon, text, accelerator text, check icon and arrow icon
- // at the right side
- rightAlignAllRects(li);
-
- // Take into account the left side bearing for accelerator text.
- // The LSB for text is taken into account in layoutCompoundLabel() below.
- fixAccTextRect(li);
-
- // Layout icon and text with SwingUtilities.layoutCompoundLabel()
- // within the labelRect
- li.textRect = new Rectangle();
- li.iconRect = new Rectangle();
- SwingUtilities.layoutCompoundLabel(
- menuItem, li.fm, li.text, li.icon, li.verticalAlignment,
- li.horizontalAlignment, li.verticalTextPosition,
- li.horizontalTextPosition, li.labelRect,
- li.iconRect, li.textRect, li.gap);
- }
-
- private void calcXPositionsL2R(int startXPos, int leadingGap,
- int gap, Rectangle... rects) {
- int curXPos = startXPos + leadingGap;
- for (Rectangle rect : rects) {
- rect.x = curXPos;
- if (rect.width > 0) {
- curXPos += rect.width + gap;
- }
- }
- }
-
- private void calcXPositionsL2R(int startXPos, int gap, Rectangle... rects) {
- calcXPositionsL2R(startXPos, gap, gap, rects);
- }
-
- private void calcXPositionsR2L(int startXPos, int leadingGap,
- int gap, Rectangle... rects) {
- int curXPos = startXPos - leadingGap;
- for (Rectangle rect : rects) {
- rect.x = curXPos - rect.width;
- if (rect.width > 0) {
- curXPos -= rect.width + gap;
- }
- }
- }
-
- private void calcXPositionsR2L(int startXPos, int gap, Rectangle... rects) {
- calcXPositionsR2L(startXPos, gap, gap, rects);
- }
-
- // Takes into account the left side bearings for text and accelerator text
- private void fixTextRects(LayoutInfo li) {
- if (li.htmlView == null) { // The text isn't a HTML
- int lsb = SwingUtilities2.getLeftSideBearing(li.mi, li.fm, li.text);
- if (lsb < 0) {
- li.textRect.x -= lsb;
- }
- }
- fixAccTextRect(li);
- }
-
- // Takes into account the left side bearing for accelerator text
- private void fixAccTextRect(LayoutInfo li) {
- int lsb = SwingUtilities2
- .getLeftSideBearing(li.mi, li.accFm, li.accText);
- if (lsb < 0) {
- li.accRect.x -= lsb;
- }
- }
-
- // Sets Y coordinates of text and icon
- // taking into account the vertical alignment
- private void calcTextAndIconYPositions(LayoutInfo li) {
- if (li.verticalAlignment == SwingUtilities.TOP) {
- li.textRect.y = (int)(li.viewRect.y
- + (float)li.labelRect.height/2
- - (float)li.textRect.height/2);
- li.iconRect.y = (int)(li.viewRect.y
- + (float)li.labelRect.height/2
- - (float)li.iconRect.height/2);
- } else if (li.verticalAlignment == SwingUtilities.CENTER) {
- li.textRect.y = (int)(li.viewRect.y
- + (float)li.viewRect.height/2
- - (float)li.textRect.height/2);
- li.iconRect.y = (int)(li.viewRect.y
- + (float)li.viewRect.height/2
- - (float)li.iconRect.height/2);
- }
- else if (li.verticalAlignment == SwingUtilities.BOTTOM) {
- li.textRect.y = (int)(li.viewRect.y + li.viewRect.height
- - (float)li.labelRect.height/2
- - (float)li.textRect.height/2);
- li.iconRect.y = (int)(li.viewRect.y + li.viewRect.height
- - (float)li.labelRect.height/2
- - (float)li.iconRect.height/2);
- }
- }
-
- // Aligns icon, text, accelerator text, check icon and arrow icon
- // at the right side
- private void rightAlignAllRects(LayoutInfo li) {
- li.iconRect.x = li.iconRect.x + li.iconRect.width - li.origIconWidth;
- li.iconRect.width = li.origIconWidth;
- li.textRect.x = li.textRect.x + li.textRect.width - li.origTextWidth;
- li.textRect.width = li.origTextWidth;
- li.accRect.x = li.accRect.x + li.accRect.width
- - li.origAccWidth;
- li.accRect.width = li.origAccWidth;
- li.checkRect.x = li.checkRect.x + li.checkRect.width
- - li.origCheckWidth;
- li.checkRect.width = li.origCheckWidth;
- li.arrowRect.x = li.arrowRect.x + li.arrowRect.width -
- li.origArrowWidth;
- li.arrowRect.width = li.origArrowWidth;
- }
-
- /*
- * Returns false if the component is a JMenu and it is a top
- * level menu (on the menubar).
- */
- private boolean useCheckAndArrow(){
- boolean b = true;
- if((menuItem instanceof JMenu) &&
- (((JMenu)menuItem).isTopLevelMenu())) {
- b = false;
- }
- return b;
- }
-
public MenuElement[] getPath() {
MenuSelectionManager m = MenuSelectionManager.defaultManager();
MenuElement oldPath[] = m.getSelectedPath();
--- a/jdk/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,6 @@
import java.awt.Container;
import java.awt.Dimension;
-import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
/**
* The default layout manager for Popup menus and menubars. This
@@ -49,18 +48,7 @@
public Dimension preferredLayoutSize(Container target) {
if (target instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu) target;
-
- // Before the calculation of menu preferred size
- // clear the previously calculated maximal widths and offsets
- // in menu's Client Properties
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH, null);
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_ARROW_WIDTH, null);
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_CHECK_WIDTH, null);
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_ICON_WIDTH, null);
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_LABEL_WIDTH, null);
- popupMenu.putClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH, null);
- popupMenu.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null);
-
+ sun.swing.MenuItemLayoutHelper.clearUsedClientProperties(popupMenu);
if (popupMenu.getComponentCount() == 0) {
return new Dimension(0, 0);
}
--- a/jdk/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,19 +47,22 @@
super(target, axis);
}
- public void invalidateLayout(Container target) {
+ public Dimension preferredLayoutSize(Container target) {
if (target instanceof JPopupMenu) {
- SynthPopupMenuUI popupUI = (SynthPopupMenuUI)((JPopupMenu)target).
- getUI();
- popupUI.resetAlignmentHints();
+ JPopupMenu popupMenu = (JPopupMenu) target;
+
+ popupMenu.putClientProperty(
+ SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null);
+ sun.swing.MenuItemLayoutHelper.clearUsedClientProperties(popupMenu);
+
+ if (popupMenu.getComponentCount() == 0) {
+ return new Dimension(0, 0);
+ }
}
+
+ // Make BoxLayout recalculate cached preferred sizes
super.invalidateLayout(target);
- }
- public Dimension preferredLayoutSize(Container target) {
- if (target instanceof JPopupMenu && target.getComponentCount() == 0) {
- return new Dimension(0, 0);
- }
return super.preferredLayoutSize(target);
}
}
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package javax.swing.plaf.synth;
import sun.swing.SwingUtilities2;
+import sun.swing.MenuItemLayoutHelper;
+
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicHTML;
@@ -411,6 +413,198 @@
}
+ /**
+ * A quick note about how preferred sizes are calculated... Generally
+ * speaking, SynthPopupMenuUI will run through the list of its children
+ * (from top to bottom) and ask each for its preferred size. Each menu
+ * item will add up the max width of each element (icons, text,
+ * accelerator spacing, accelerator text or arrow icon) encountered thus
+ * far, so by the time all menu items have been calculated, we will
+ * know the maximum (preferred) menu item size for that popup menu.
+ * Later when it comes time to paint each menu item, we can use those
+ * same accumulated max element sizes in order to layout the item.
+ */
+ static Dimension getPreferredMenuItemSize(SynthContext context,
+ SynthContext accContext, JComponent c,
+ Icon checkIcon, Icon arrowIcon, int defaultTextIconGap,
+ String acceleratorDelimiter, boolean useCheckAndArrow,
+ String propertyPrefix) {
+
+ JMenuItem mi = (JMenuItem) c;
+ SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper(
+ context, accContext, mi, checkIcon, arrowIcon,
+ MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap,
+ acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi),
+ useCheckAndArrow, propertyPrefix);
+
+ Dimension result = new Dimension();
+
+ // Calculate the result width
+ int gap = lh.getGap();
+ result.width = 0;
+ MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result);
+ MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result);
+ MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result);
+ // The last gap is unnecessary
+ result.width -= gap;
+
+ // Calculate the result height
+ result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(),
+ lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(),
+ lh.getArrowSize().getHeight());
+
+ // Take into account menu item insets
+ Insets insets = lh.getMenuItem().getInsets();
+ if (insets != null) {
+ result.width += insets.left + insets.right;
+ result.height += insets.top + insets.bottom;
+ }
+
+ // if the width is even, bump it up one. This is critical
+ // for the focus dash lhne to draw properly
+ if (result.width % 2 == 0) {
+ result.width++;
+ }
+
+ // if the height is even, bump it up one. This is critical
+ // for the text to center properly
+ if (result.height % 2 == 0) {
+ result.height++;
+ }
+
+ return result;
+ }
+
+ static void applyInsets(Rectangle rect, Insets insets) {
+ if (insets != null) {
+ rect.x += insets.left;
+ rect.y += insets.top;
+ rect.width -= (insets.right + rect.x);
+ rect.height -= (insets.bottom + rect.y);
+ }
+ }
+
+ static void paint(SynthContext context, SynthContext accContext, Graphics g,
+ Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter,
+ int defaultTextIconGap, String propertyPrefix) {
+ JMenuItem mi = (JMenuItem) context.getComponent();
+ SynthStyle style = context.getStyle();
+ g.setFont(style.getFont(context));
+
+ Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
+ applyInsets(viewRect, mi.getInsets());
+
+ SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper(
+ context, accContext, mi, checkIcon,
+ arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter,
+ SynthLookAndFeel.isLeftToRight(mi),
+ MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix);
+ MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
+
+ paintMenuItem(g, lh, lr);
+ }
+
+ static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ // Save original graphics font and color
+ Font holdf = g.getFont();
+ Color holdc = g.getColor();
+
+ paintBackground(g, lh);
+ paintCheckIcon(g, lh, lr);
+ paintIcon(g, lh, lr);
+ paintText(g, lh, lr);
+ paintAccText(g, lh, lr);
+ paintArrowIcon(g, lh, lr);
+
+ // Restore original graphics font and color
+ g.setColor(holdc);
+ g.setFont(holdf);
+ }
+
+ static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) {
+ paintBackground(lh.getContext(), g, lh.getMenuItem());
+ }
+
+ static void paintBackground(SynthContext context, Graphics g, JComponent c) {
+ context.getPainter().paintMenuItemBackground(context, g, 0, 0,
+ c.getWidth(), c.getHeight());
+ }
+
+ static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (lh.getIcon() != null) {
+ Icon icon;
+ JMenuItem mi = lh.getMenuItem();
+ ButtonModel model = mi.getModel();
+ if (!model.isEnabled()) {
+ icon = mi.getDisabledIcon();
+ } else if (model.isPressed() && model.isArmed()) {
+ icon = mi.getPressedIcon();
+ if (icon == null) {
+ // Use default icon
+ icon = mi.getIcon();
+ }
+ } else {
+ icon = mi.getIcon();
+ }
+
+ if (icon != null) {
+ Rectangle iconRect = lr.getIconRect();
+ SynthIcon.paintIcon(icon, lh.getContext(), g, iconRect.x,
+ iconRect.y, iconRect.width, iconRect.height);
+ }
+ }
+ }
+
+ static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (lh.getCheckIcon() != null) {
+ Rectangle checkRect = lr.getCheckRect();
+ SynthIcon.paintIcon(lh.getCheckIcon(), lh.getContext(), g,
+ checkRect.x, checkRect.y, checkRect.width, checkRect.height);
+ }
+ }
+
+ static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ String accText = lh.getAccText();
+ if (accText != null && !accText.equals("")) {
+ g.setColor(lh.getAccStyle().getColor(lh.getAccContext(),
+ ColorType.TEXT_FOREGROUND));
+ g.setFont(lh.getAccStyle().getFont(lh.getAccContext()));
+ lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText,
+ lr.getAccRect().x, lr.getAccRect().y, -1);
+ }
+ }
+
+ static void paintText(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (!lh.getText().equals("")) {
+ if (lh.getHtmlView() != null) {
+ // Text is HTML
+ lh.getHtmlView().paint(g, lr.getTextRect());
+ } else {
+ // Text isn't HTML
+ g.setColor(lh.getStyle().getColor(
+ lh.getContext(), ColorType.TEXT_FOREGROUND));
+ g.setFont(lh.getStyle().getFont(lh.getContext()));
+ lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(),
+ lr.getTextRect().x, lr.getTextRect().y,
+ lh.getMenuItem().getDisplayedMnemonicIndex());
+ }
+ }
+ }
+
+ static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh,
+ MenuItemLayoutHelper.LayoutResult lr) {
+ if (lh.getArrowIcon() != null) {
+ Rectangle arrowRect = lr.getArrowRect();
+ SynthIcon.paintIcon(lh.getArrowIcon(), lh.getContext(), g,
+ arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height);
+ }
+ }
+
/**
* Wraps a SynthIcon around the Icon interface, forwarding calls to
* the SynthIcon with a given SynthContext.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java Fri Aug 08 20:49:26 2008 +0400
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.swing.plaf.synth;
+
+import sun.swing.StringUIClientPropertyKey;
+import sun.swing.MenuItemLayoutHelper;
+import sun.swing.plaf.synth.SynthIcon;
+
+import javax.swing.*;
+import javax.swing.text.View;
+import java.awt.*;
+
+/**
+ * Calculates preferred size and layouts synth menu items.
+ *
+ * All JMenuItems (and JMenus) include enough space for the insets
+ * plus one or more elements. When we say "label" below, we mean
+ * "icon and/or text."
+ *
+ * Cases to consider for SynthMenuItemUI (visualized here in a
+ * LTR orientation; the RTL case would be reversed):
+ * label
+ * check icon + label
+ * check icon + label + accelerator
+ * label + accelerator
+ *
+ * Cases to consider for SynthMenuUI (again visualized here in a
+ * LTR orientation):
+ * label + arrow
+ *
+ * Note that in the above scenarios, accelerator and arrow icon are
+ * mutually exclusive. This means that if a popup menu contains a mix
+ * of JMenus and JMenuItems, we only need to allow enough space for
+ * max(maxAccelerator, maxArrow), and both accelerators and arrow icons
+ * can occupy the same "column" of space in the menu.
+ */
+class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper {
+
+ public static final StringUIClientPropertyKey MAX_ACC_OR_ARROW_WIDTH =
+ new StringUIClientPropertyKey("maxAccOrArrowWidth");
+
+ public static final ColumnAlignment LTR_ALIGNMENT_1 =
+ new ColumnAlignment(
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT
+ );
+ public static final ColumnAlignment LTR_ALIGNMENT_2 =
+ new ColumnAlignment(
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.RIGHT
+ );
+ public static final ColumnAlignment RTL_ALIGNMENT_1 =
+ new ColumnAlignment(
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT
+ );
+ public static final ColumnAlignment RTL_ALIGNMENT_2 =
+ new ColumnAlignment(
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.LEFT
+ );
+
+ private SynthContext context;
+ private SynthContext accContext;
+ private SynthStyle style;
+ private SynthStyle accStyle;
+ private SynthGraphicsUtils gu;
+ private SynthGraphicsUtils accGu;
+ private boolean alignAcceleratorText;
+ private int maxAccOrArrowWidth;
+
+ public SynthMenuItemLayoutHelper(SynthContext context, SynthContext accContext,
+ JMenuItem mi, Icon checkIcon, Icon arrowIcon,
+ Rectangle viewRect, int gap, String accDelimiter,
+ boolean isLeftToRight, boolean useCheckAndArrow,
+ String propertyPrefix) {
+ this.context = context;
+ this.accContext = accContext;
+ this.style = context.getStyle();
+ this.accStyle = accContext.getStyle();
+ this.gu = style.getGraphicsUtils(context);
+ this.accGu = accStyle.getGraphicsUtils(accContext);
+ this.alignAcceleratorText = getAlignAcceleratorText(propertyPrefix);
+ reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
+ isLeftToRight, style.getFont(context), accStyle.getFont(accContext),
+ useCheckAndArrow, propertyPrefix);
+ setLeadingGap(0);
+ }
+
+ private boolean getAlignAcceleratorText(String propertyPrefix) {
+ return style.getBoolean(context,
+ propertyPrefix + ".alignAcceleratorText", true);
+ }
+
+ protected void calcWidthsAndHeights() {
+ // iconRect
+ if (getIcon() != null) {
+ getIconSize().setWidth(SynthIcon.getIconWidth(getIcon(), context));
+ getIconSize().setHeight(SynthIcon.getIconHeight(getIcon(), context));
+ }
+
+ // accRect
+ if (!getAccText().equals("")) {
+ getAccSize().setWidth(accGu.computeStringWidth(getAccContext(),
+ getAccFontMetrics().getFont(), getAccFontMetrics(),
+ getAccText()));
+ getAccSize().setHeight(getAccFontMetrics().getHeight());
+ }
+
+ // textRect
+ if (getText() == null) {
+ setText("");
+ } else if (!getText().equals("")) {
+ if (getHtmlView() != null) {
+ // Text is HTML
+ getTextSize().setWidth(
+ (int) getHtmlView().getPreferredSpan(View.X_AXIS));
+ getTextSize().setHeight(
+ (int) getHtmlView().getPreferredSpan(View.Y_AXIS));
+ } else {
+ // Text isn't HTML
+ getTextSize().setWidth(gu.computeStringWidth(context,
+ getFontMetrics().getFont(), getFontMetrics(),
+ getText()));
+ getTextSize().setHeight(getFontMetrics().getHeight());
+ }
+ }
+
+ if (useCheckAndArrow()) {
+ // checkIcon
+ if (getCheckIcon() != null) {
+ getCheckSize().setWidth(
+ SynthIcon.getIconWidth(getCheckIcon(), context));
+ getCheckSize().setHeight(
+ SynthIcon.getIconHeight(getCheckIcon(), context));
+ }
+ // arrowRect
+ if (getArrowIcon() != null) {
+ getArrowSize().setWidth(
+ SynthIcon.getIconWidth(getArrowIcon(), context));
+ getArrowSize().setHeight(
+ SynthIcon.getIconHeight(getArrowIcon(), context));
+ }
+ }
+
+ // labelRect
+ if (isColumnLayout()) {
+ getLabelSize().setWidth(getIconSize().getWidth()
+ + getTextSize().getWidth() + getGap());
+ getLabelSize().setHeight(MenuItemLayoutHelper.max(
+ getCheckSize().getHeight(),
+ getIconSize().getHeight(),
+ getTextSize().getHeight(),
+ getAccSize().getHeight(),
+ getArrowSize().getHeight()));
+ } else {
+ Rectangle textRect = new Rectangle();
+ Rectangle iconRect = new Rectangle();
+ gu.layoutText(context, getFontMetrics(), getText(), getIcon(),
+ getHorizontalAlignment(), getVerticalAlignment(),
+ getHorizontalTextPosition(), getVerticalTextPosition(),
+ getViewRect(), iconRect, textRect, getGap());
+ Rectangle labelRect = iconRect.union(textRect);
+ getLabelSize().setHeight(labelRect.height);
+ getLabelSize().setWidth(labelRect.width);
+ }
+ }
+
+ protected void calcMaxWidths() {
+ calcMaxWidth(getCheckSize(), MAX_CHECK_WIDTH);
+ maxAccOrArrowWidth =
+ calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getArrowSize().getWidth());
+ maxAccOrArrowWidth =
+ calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getAccSize().getWidth());
+
+ if (isColumnLayout()) {
+ calcMaxWidth(getIconSize(), MAX_ICON_WIDTH);
+ calcMaxWidth(getTextSize(), MAX_TEXT_WIDTH);
+ int curGap = getGap();
+ if ((getIconSize().getMaxWidth() == 0)
+ || (getTextSize().getMaxWidth() == 0)) {
+ curGap = 0;
+ }
+ getLabelSize().setMaxWidth(
+ calcMaxValue(MAX_LABEL_WIDTH, getIconSize().getMaxWidth()
+ + getTextSize().getMaxWidth() + curGap));
+ } else {
+ // We shouldn't use current icon and text widths
+ // in maximal widths calculation for complex layout.
+ getIconSize().setMaxWidth(getParentIntProperty(
+ MAX_ICON_WIDTH));
+ calcMaxWidth(getLabelSize(), MAX_LABEL_WIDTH);
+ // If maxLabelWidth is wider
+ // than the widest icon + the widest text + gap,
+ // we should update the maximal text witdh
+ int candidateTextWidth = getLabelSize().getMaxWidth() -
+ getIconSize().getMaxWidth();
+ if (getIconSize().getMaxWidth() > 0) {
+ candidateTextWidth -= getGap();
+ }
+ getTextSize().setMaxWidth(calcMaxValue(
+ MAX_TEXT_WIDTH, candidateTextWidth));
+ }
+ }
+
+ public SynthContext getContext() {
+ return context;
+ }
+
+ public SynthContext getAccContext() {
+ return accContext;
+ }
+
+ public SynthStyle getStyle() {
+ return style;
+ }
+
+ public SynthStyle getAccStyle() {
+ return accStyle;
+ }
+
+ public SynthGraphicsUtils getGraphicsUtils() {
+ return gu;
+ }
+
+ public SynthGraphicsUtils getAccGraphicsUtils() {
+ return accGu;
+ }
+
+ public boolean alignAcceleratorText() {
+ return alignAcceleratorText;
+ }
+
+ public int getMaxAccOrArrowWidth() {
+ return maxAccOrArrowWidth;
+ }
+
+ protected void prepareForLayout(LayoutResult lr) {
+ lr.getCheckRect().width = getCheckSize().getMaxWidth();
+ // An item can have an arrow or a check icon at once
+ if (useCheckAndArrow() && (!"".equals(getAccText()))) {
+ lr.getAccRect().width = maxAccOrArrowWidth;
+ } else {
+ lr.getArrowRect().width = maxAccOrArrowWidth;
+ }
+ }
+
+ public ColumnAlignment getLTRColumnAlignment() {
+ if (alignAcceleratorText()) {
+ return LTR_ALIGNMENT_2;
+ } else {
+ return LTR_ALIGNMENT_1;
+ }
+ }
+
+ public ColumnAlignment getRTLColumnAlignment() {
+ if (alignAcceleratorText()) {
+ return RTL_ALIGNMENT_2;
+ } else {
+ return RTL_ALIGNMENT_1;
+ }
+ }
+
+ protected void layoutIconAndTextInLabelRect(LayoutResult lr) {
+ lr.setTextRect(new Rectangle());
+ lr.setIconRect(new Rectangle());
+ gu.layoutText(context, getFontMetrics(), getText(), getIcon(),
+ getHorizontalAlignment(), getVerticalAlignment(),
+ getHorizontalTextPosition(), getVerticalTextPosition(),
+ lr.getLabelRect(), lr.getIconRect(), lr.getTextRect(), getGap());
+ }
+}
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,7 +37,7 @@
import javax.swing.plaf.basic.*;
import javax.swing.text.View;
import sun.swing.plaf.synth.*;
-import sun.swing.SwingUtilities2;
+import sun.swing.MenuItemLayoutHelper;
/**
@@ -59,542 +59,16 @@
return new SynthMenuItemUI();
}
- //
- // The next handful of static methods are used by both SynthMenuUI
- // and SynthMenuItemUI. This is necessitated by SynthMenuUI not
- // extending SynthMenuItemUI.
- //
-
- /*
- * All JMenuItems (and JMenus) include enough space for the insets
- * plus one or more elements. When we say "icon(s)" below, we mean
- * "check/radio indicator and/or user icon." If both are defined for
- * a given menu item, then in a LTR orientation the check/radio indicator
- * is on the left side followed by the user icon to the right; it is
- * just the opposite in a RTL orientation.
- *
- * Cases to consider for SynthMenuItemUI (visualized here in a
- * LTR orientation; the RTL case would be reversed):
- * text
- * icon(s) + text
- * icon(s) + text + accelerator
- * text + accelerator
- *
- * Cases to consider for SynthMenuUI (again visualized here in a
- * LTR orientation):
- * text + arrow
- * (user)icon + text + arrow
- *
- * Note that in the above scenarios, accelerator and arrow icon are
- * mutually exclusive. This means that if a popup menu contains a mix
- * of JMenus and JMenuItems, we only need to allow enough space for
- * max(maxAccelerator, maxArrow), and both accelerators and arrow icons
- * can occupy the same "column" of space in the menu.
- *
- * A quick note about how preferred sizes are calculated... Generally
- * speaking, SynthPopupMenuUI will run through the list of its children
- * (from top to bottom) and ask each for its preferred size. Each menu
- * item will add up the max width of each element (icons, text,
- * accelerator spacing, accelerator text or arrow icon) encountered thus
- * far, so by the time all menu items have been calculated, we will
- * know the maximum (preferred) menu item size for that popup menu.
- * Later when it comes time to paint each menu item, we can use those
- * same accumulated max element sizes in order to layout the item.
- */
- static Dimension getPreferredMenuItemSize(SynthContext context,
- SynthContext accContext, JComponent c,
- Icon checkIcon, Icon arrowIcon, int defaultTextIconGap,
- String acceleratorDelimiter) {
- JMenuItem b = (JMenuItem) c;
- Icon icon = b.getIcon();
- String text = b.getText();
- KeyStroke accelerator = b.getAccelerator();
- String acceleratorText = "";
-
- if (accelerator != null) {
- int modifiers = accelerator.getModifiers();
- if (modifiers > 0) {
- acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
- acceleratorText += acceleratorDelimiter;
- }
- int keyCode = accelerator.getKeyCode();
- if (keyCode != 0) {
- acceleratorText += KeyEvent.getKeyText(keyCode);
- } else {
- acceleratorText += accelerator.getKeyChar();
- }
- }
-
- Font font = context.getStyle().getFont(context);
- FontMetrics fm = b.getFontMetrics(font);
- FontMetrics fmAccel = b.getFontMetrics(accContext.getStyle().
- getFont(accContext));
-
- resetRects();
-
- layoutMenuItem(
- context, fm, accContext, text, fmAccel, acceleratorText,
- icon, checkIcon, arrowIcon, b.getVerticalAlignment(),
- b.getHorizontalAlignment(), b.getVerticalTextPosition(),
- b.getHorizontalTextPosition(), viewRect, iconRect, textRect,
- acceleratorRect, checkIconRect, arrowIconRect,
- text == null ? 0 : defaultTextIconGap, defaultTextIconGap);
-
- r.setBounds(textRect);
-
- int totalIconWidth = 0;
- int maxIconHeight = 0;
- if (icon != null) {
- // Add in the user icon
- totalIconWidth += iconRect.width;
- if (textRect.width > 0) {
- // Allow for some room between the user icon and the text
- totalIconWidth += defaultTextIconGap;
- }
- maxIconHeight = Math.max(iconRect.height, maxIconHeight);
- }
- if (checkIcon != null) {
- // Add in the checkIcon
- totalIconWidth += checkIconRect.width;
- if (textRect.width > 0 || icon != null) {
- // Allow for some room between the check/radio indicator
- // and the text (or user icon, if both are specified)
- totalIconWidth += defaultTextIconGap;
- }
- maxIconHeight = Math.max(checkIconRect.height, maxIconHeight);
- }
-
- int arrowWidth = 0;
- if (arrowIcon != null) {
- // Add in the arrowIcon
- arrowWidth += defaultTextIconGap;
- arrowWidth += arrowIconRect.width;
- maxIconHeight = Math.max(arrowIconRect.height, maxIconHeight);
- }
-
- int accelSpacing = 0;
- if (acceleratorRect.width > 0) {
- // Allow for some room between the text and the accelerator
- accelSpacing += 4*defaultTextIconGap;
- }
-
- // Take text and all icons into account when determining height
- r.height = Math.max(r.height, maxIconHeight);
-
- // To make the accelerator texts appear in a column,
- // find the widest MenuItem text and the widest accelerator text.
-
- // Get the parent, which stores the information.
- Container parent = b.getParent();
-
- if (parent instanceof JPopupMenu) {
- SynthPopupMenuUI popupUI = (SynthPopupMenuUI)SynthLookAndFeel.
- getUIOfType(((JPopupMenu)parent).getUI(),
- SynthPopupMenuUI.class);
-
- if (popupUI != null) {
- // This gives us the widest MenuItem text encountered thus
- // far in the parent JPopupMenu
- r.width = popupUI.adjustTextWidth(r.width);
-
- // Add in the widest icon (includes both user and
- // check/radio icons) encountered thus far
- r.width += popupUI.adjustIconWidth(totalIconWidth);
-
- // Add in the widest text/accelerator spacing
- // encountered thus far
- r.width += popupUI.adjustAccelSpacingWidth(accelSpacing);
-
- // Add in the widest accelerator text (or arrow)
- // encountered thus far (at least one of these values
- // will always be zero, so we combine them here to
- // avoid double counting)
- int totalAccelOrArrow = acceleratorRect.width + arrowWidth;
- r.width += popupUI.adjustAcceleratorWidth(totalAccelOrArrow);
- }
- }
- else if (parent != null && !(b instanceof JMenu &&
- ((JMenu)b).isTopLevelMenu())) {
- r.width +=
- totalIconWidth + accelSpacing +
- acceleratorRect.width + arrowWidth;
- }
-
- Insets insets = b.getInsets();
- if(insets != null) {
- r.width += insets.left + insets.right;
- r.height += insets.top + insets.bottom;
- }
-
- // if the width is even, bump it up one. This is critical
- // for the focus dash line to draw properly
- if(r.width%2 == 0) {
- r.width++;
- }
-
- // if the height is even, bump it up one. This is critical
- // for the text to center properly
- if(r.height%2 == 0) {
- r.height++;
- }
- return r.getSize();
- }
-
- static void paint(SynthContext context, SynthContext accContext,
- Graphics g, Icon checkIcon, Icon arrowIcon,
- String acceleratorDelimiter,
- int defaultTextIconGap) {
- JComponent c = context.getComponent();
- JMenuItem b = (JMenuItem)c;
- ButtonModel model = b.getModel();
- Insets i = b.getInsets();
-
- resetRects();
-
- viewRect.setBounds(0, 0, b.getWidth(), b.getHeight());
-
- viewRect.x += i.left;
- viewRect.y += i.top;
- viewRect.width -= (i.right + viewRect.x);
- viewRect.height -= (i.bottom + viewRect.y);
-
- SynthStyle style = context.getStyle();
- Font f = style.getFont(context);
- g.setFont(f);
- FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f);
- FontMetrics accFM = SwingUtilities2.getFontMetrics(c, g,
- accContext.getStyle().
- getFont(accContext));
-
- // get Accelerator text
- KeyStroke accelerator = b.getAccelerator();
- String acceleratorText = "";
- if (accelerator != null) {
- int modifiers = accelerator.getModifiers();
- if (modifiers > 0) {
- acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
- acceleratorText += acceleratorDelimiter;
- }
-
- int keyCode = accelerator.getKeyCode();
- if (keyCode != 0) {
- acceleratorText += KeyEvent.getKeyText(keyCode);
- } else {
- acceleratorText += accelerator.getKeyChar();
- }
- }
-
- // Layout the text and icon
- String text = layoutMenuItem(context, fm, accContext,
- b.getText(), accFM, acceleratorText, b.getIcon(),
- checkIcon, arrowIcon,
- b.getVerticalAlignment(), b.getHorizontalAlignment(),
- b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
- viewRect, iconRect, textRect, acceleratorRect,
- checkIconRect, arrowIconRect,
- b.getText() == null ? 0 : defaultTextIconGap,
- defaultTextIconGap
- );
-
- // Paint the Check
- if (checkIcon != null) {
- SynthIcon.paintIcon(checkIcon, context, g, checkIconRect.x,
- checkIconRect.y, checkIconRect.width, checkIconRect.height);
- }
-
- // Paint the Icon
- if(b.getIcon() != null) {
- Icon icon;
- if(!model.isEnabled()) {
- icon = b.getDisabledIcon();
- } else if(model.isPressed() && model.isArmed()) {
- icon = b.getPressedIcon();
- if(icon == null) {
- // Use default icon
- icon = b.getIcon();
- }
- } else {
- icon = b.getIcon();
- }
-
- if (icon!=null) {
- SynthIcon.paintIcon(icon, context, g, iconRect.x,
- iconRect.y, iconRect.width, iconRect.height);
- }
- }
-
- // Draw the Text
- if(text != null) {
- View v = (View) c.getClientProperty(BasicHTML.propertyKey);
- if (v != null) {
- v.paint(g, textRect);
- } else {
- g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND));
- g.setFont(style.getFont(context));
- style.getGraphicsUtils(context).paintText(context, g, text,
- textRect.x, textRect.y, b.getDisplayedMnemonicIndex());
- }
- }
-
- // Draw the Accelerator Text
- if(acceleratorText != null && !acceleratorText.equals("")) {
- // Get the maxAccWidth from the parent to calculate the offset.
- int accOffset = 0;
- Container parent = b.getParent();
- if (parent != null && parent instanceof JPopupMenu) {
- SynthPopupMenuUI popupUI = (SynthPopupMenuUI)
- ((JPopupMenu)parent).getUI();
-
- // Note that we can only get here for SynthMenuItemUI
- // (not SynthMenuUI) since acceleratorText is defined,
- // so this cast should be safe
- SynthMenuItemUI miUI = (SynthMenuItemUI)
- SynthLookAndFeel.getUIOfType(b.getUI(),
- SynthMenuItemUI.class);
-
- if (popupUI != null && miUI != null) {
- String prop =
- miUI.getPropertyPrefix() + ".alignAcceleratorText";
- boolean align = style.getBoolean(context, prop, true);
-
- // Calculate the offset, with which the accelerator texts
- // will be drawn.
- if (align) {
- // When align==true and we're in the LTR case,
- // we add an offset here so that all accelerators
- // will be left-justified in their own column.
- int max = popupUI.getMaxAcceleratorWidth();
- if (max > 0) {
- accOffset = max - acceleratorRect.width;
- if (!SynthLookAndFeel.isLeftToRight(c)) {
- // In the RTL, flip the sign so that all
- // accelerators will be right-justified.
- accOffset = -accOffset;
- }
- }
- } //else {
- // Don't need to do anything special here; in the
- // LTR case, the accelerator is already justified
- // against the right edge of the menu (and against
- // the left edge in the RTL case).
- //}
- }
- }
-
- SynthStyle accStyle = accContext.getStyle();
-
- g.setColor(accStyle.getColor(accContext,
- ColorType.TEXT_FOREGROUND));
- g.setFont(accStyle.getFont(accContext));
- accStyle.getGraphicsUtils(accContext).paintText(
- accContext, g, acceleratorText, acceleratorRect.x -
- accOffset, acceleratorRect.y, -1);
- }
-
- // Paint the Arrow
- if (arrowIcon != null) {
- SynthIcon.paintIcon(arrowIcon, context, g, arrowIconRect.x,
- arrowIconRect.y, arrowIconRect.width, arrowIconRect.height);
+ public void uninstallUI(JComponent c) {
+ super.uninstallUI(c);
+ // Remove values from the parent's Client Properties.
+ JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c);
+ if (p != null) {
+ p.putClientProperty(
+ SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null);
}
}
- /**
- * Compute and return the location of the icons origin, the
- * location of origin of the text baseline, and a possibly clipped
- * version of the compound labels string. Locations are computed
- * relative to the viewRect rectangle.
- */
-
- private static String layoutMenuItem(
- SynthContext context,
- FontMetrics fm,
- SynthContext accContext,
- String text,
- FontMetrics fmAccel,
- String acceleratorText,
- Icon icon,
- Icon checkIcon,
- Icon arrowIcon,
- int verticalAlignment,
- int horizontalAlignment,
- int verticalTextPosition,
- int horizontalTextPosition,
- Rectangle viewRect,
- Rectangle iconRect,
- Rectangle textRect,
- Rectangle acceleratorRect,
- Rectangle checkIconRect,
- Rectangle arrowIconRect,
- int textIconGap,
- int menuItemGap
- )
- {
- // If parent is JPopupMenu, get and store it's UI
- SynthPopupMenuUI popupUI = null;
- JComponent b = context.getComponent();
- Container parent = b.getParent();
- if(parent instanceof JPopupMenu) {
- popupUI = (SynthPopupMenuUI)SynthLookAndFeel.
- getUIOfType(((JPopupMenu)parent).getUI(),
- SynthPopupMenuUI.class);
- }
-
- context.getStyle().getGraphicsUtils(context).layoutText(
- context, fm, text, icon,horizontalAlignment, verticalAlignment,
- horizontalTextPosition, verticalTextPosition, viewRect,
- iconRect, textRect, textIconGap);
-
- /* Initialize the acceleratorText bounds rectangle textRect. If a null
- * or and empty String was specified we substitute "" here
- * and use 0,0,0,0 for acceleratorTextRect.
- */
- if( (acceleratorText == null) || acceleratorText.equals("") ) {
- acceleratorRect.width = acceleratorRect.height = 0;
- acceleratorText = "";
- }
- else {
- SynthStyle style = accContext.getStyle();
- acceleratorRect.width = style.getGraphicsUtils(accContext).
- computeStringWidth(accContext, fmAccel.getFont(), fmAccel,
- acceleratorText);
- acceleratorRect.height = fmAccel.getHeight();
- }
-
- // Initialize the checkIcon bounds rectangle width & height.
- if (checkIcon != null) {
- checkIconRect.width = SynthIcon.getIconWidth(checkIcon,
- context);
- checkIconRect.height = SynthIcon.getIconHeight(checkIcon,
- context);
- }
- else {
- checkIconRect.width = checkIconRect.height = 0;
- }
-
- // Initialize the arrowIcon bounds rectangle width & height.
- if (arrowIcon != null) {
- arrowIconRect.width = SynthIcon.getIconWidth(arrowIcon,
- context);
- arrowIconRect.height = SynthIcon.getIconHeight(arrowIcon,
- context);
- } else {
- arrowIconRect.width = arrowIconRect.height = 0;
- }
-
- // Note: layoutText() has already left room for
- // the user icon, so no need to adjust textRect below
- // to account for the user icon. However, we do have to
- // reposition textRect when the check icon is visible.
-
- Rectangle labelRect = iconRect.union(textRect);
- if( SynthLookAndFeel.isLeftToRight(context.getComponent()) ) {
- // Position the check and user icons
- iconRect.x = viewRect.x;
- if (checkIcon != null) {
- checkIconRect.x = viewRect.x;
- iconRect.x += menuItemGap + checkIconRect.width;
- textRect.x += menuItemGap + checkIconRect.width;
- }
-
- // Position the arrow icon
- arrowIconRect.x =
- viewRect.x + viewRect.width - arrowIconRect.width;
-
- // Position the accelerator text rect
- acceleratorRect.x =
- viewRect.x + viewRect.width - acceleratorRect.width;
-
- /* Align icons and text horizontally */
- if(popupUI != null) {
- int thisTextOffset = popupUI.adjustTextOffset(textRect.x
- - viewRect.x);
- textRect.x = thisTextOffset + viewRect.x;
-
- if(icon != null) {
- // REMIND: The following code currently assumes the
- // default (TRAILING) horizontalTextPosition, which means
- // it will always place the icon to the left of the text.
- // Other values of horizontalTextPosition aren't very
- // useful for menu items, so we ignore them for now, but
- // someday we might want to fix this situation.
- int thisIconOffset =
- popupUI.adjustIconOffset(iconRect.x - viewRect.x);
- iconRect.x = thisIconOffset + viewRect.x;
- }
- }
- } else {
- // Position the accelerator text rect
- acceleratorRect.x = viewRect.x;
-
- // Position the arrow icon
- arrowIconRect.x = viewRect.x;
-
- // Position the check and user icons
- iconRect.x =
- viewRect.x + viewRect.width - iconRect.width;
- if (checkIcon != null) {
- checkIconRect.x =
- viewRect.x + viewRect.width - checkIconRect.width;
- textRect.x -= menuItemGap + checkIconRect.width;
- iconRect.x -= menuItemGap + checkIconRect.width;
- }
-
- /* Align icons and text horizontally */
- if(popupUI != null) {
- int thisTextOffset = viewRect.x + viewRect.width
- - textRect.x - textRect.width;
- thisTextOffset = popupUI.adjustTextOffset(thisTextOffset);
- textRect.x = viewRect.x + viewRect.width
- - thisTextOffset - textRect.width;
- if(icon != null) {
- // REMIND: The following code currently assumes the
- // default (TRAILING) horizontalTextPosition, which means
- // it will always place the icon to the right of the text.
- // Other values of horizontalTextPosition aren't very
- // useful for menu items, so we ignore them for now, but
- // someday we might want to fix this situation.
- int thisIconOffset = viewRect.x + viewRect.width
- - iconRect.x - iconRect.width;
- thisIconOffset =
- popupUI.adjustIconOffset(thisIconOffset);
- iconRect.x = viewRect.x + viewRect.width
- - thisIconOffset - iconRect.width;
- }
- }
- }
-
- // Align the accelerator text and all icons vertically
- // with the center of the label rect.
- int midY = labelRect.y + (labelRect.height/2);
- iconRect.y = midY - (iconRect.height/2);
- acceleratorRect.y = midY - (acceleratorRect.height/2);
- arrowIconRect.y = midY - (arrowIconRect.height/2);
- checkIconRect.y = midY - (checkIconRect.height/2);
-
- return text;
- }
-
- // these rects are used for painting and preferredsize calculations.
- // they used to be regenerated constantly. Now they are reused.
- static Rectangle iconRect = new Rectangle();
- static Rectangle textRect = new Rectangle();
- static Rectangle acceleratorRect = new Rectangle();
- static Rectangle checkIconRect = new Rectangle();
- static Rectangle arrowIconRect = new Rectangle();
- static Rectangle viewRect = new Rectangle(Short.MAX_VALUE,Short.MAX_VALUE);
- static Rectangle r = new Rectangle();
-
- private static void resetRects() {
- iconRect.setBounds(0, 0, 0, 0);
- textRect.setBounds(0, 0, 0, 0);
- acceleratorRect.setBounds(0, 0, 0, 0);
- checkIconRect.setBounds(0, 0, 0, 0);
- arrowIconRect.setBounds(0, 0, 0, 0);
- viewRect.setBounds(0,0,Short.MAX_VALUE, Short.MAX_VALUE);
- r.setBounds(0, 0, 0, 0);
- }
-
-
protected void installDefaults() {
updateStyle(menuItem);
}
@@ -718,9 +192,11 @@
int defaultTextIconGap) {
SynthContext context = getContext(c);
SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
- Dimension value = getPreferredMenuItemSize(context, accContext,
- c, checkIcon, arrowIcon, defaultTextIconGap,
- acceleratorDelimiter);
+ Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize(
+ context, accContext, c, checkIcon, arrowIcon,
+ defaultTextIconGap, acceleratorDelimiter,
+ MenuItemLayoutHelper.useCheckAndArrow(menuItem),
+ getPropertyPrefix());
context.dispose();
accContext.dispose();
return value;
@@ -751,14 +227,13 @@
String prefix = getPropertyPrefix();
Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
- paint(context, accContext, g, checkIcon, arrowIcon,
- acceleratorDelimiter, defaultTextIconGap);
+ SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon,
+ acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix());
accContext.dispose();
}
void paintBackground(SynthContext context, Graphics g, JComponent c) {
- context.getPainter().paintMenuItemBackground(context, g, 0, 0,
- c.getWidth(), c.getHeight());
+ SynthGraphicsUtils.paintBackground(context, g, c);
}
public void paintBorder(SynthContext context, Graphics g, int x,
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
import java.util.Arrays;
import java.util.ArrayList;
import sun.swing.plaf.synth.SynthUI;
-
+import sun.swing.MenuItemLayoutHelper;
/**
* Synth's MenuUI.
@@ -86,7 +86,7 @@
acceleratorDelimiter = style.getString(context, prefix +
".acceleratorDelimiter", "+");
- if (useCheckAndArrow()) {
+ if (MenuItemLayoutHelper.useCheckAndArrow(menuItem)) {
checkIcon = style.getIcon(context, prefix + ".checkIcon");
arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
} else {
@@ -111,6 +111,16 @@
accContext.dispose();
}
+ public void uninstallUI(JComponent c) {
+ super.uninstallUI(c);
+ // Remove values from the parent's Client Properties.
+ JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c);
+ if (p != null) {
+ p.putClientProperty(
+ SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null);
+ }
+ }
+
protected void uninstallDefaults() {
SynthContext context = getContext(menuItem, ENABLED);
style.uninstallDefaults(context);
@@ -182,9 +192,11 @@
int defaultTextIconGap) {
SynthContext context = getContext(c);
SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
- Dimension value = SynthMenuItemUI.getPreferredMenuItemSize(
- context, accContext, c, checkIcon, arrowIcon,
- defaultTextIconGap, acceleratorDelimiter);
+ Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize(
+ context, accContext, c, checkIcon, arrowIcon,
+ defaultTextIconGap, acceleratorDelimiter,
+ MenuItemLayoutHelper.useCheckAndArrow(menuItem),
+ getPropertyPrefix());
context.dispose();
accContext.dispose();
return value;
@@ -211,21 +223,12 @@
protected void paint(SynthContext context, Graphics g) {
SynthContext accContext = getContext(menuItem,
Region.MENU_ITEM_ACCELERATOR);
- SynthStyle style = context.getStyle();
- Icon checkIcon;
- Icon arrowIcon;
- if (useCheckAndArrow()) {
- // Refetch the appropriate icons for the current state
- String prefix = getPropertyPrefix();
- checkIcon = style.getIcon(context, prefix + ".checkIcon");
- arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
- } else {
- // Not needed in this case
- checkIcon = null;
- arrowIcon = null;
- }
- SynthMenuItemUI.paint(context, accContext, g, checkIcon, arrowIcon,
- acceleratorDelimiter, defaultTextIconGap);
+ // Refetch the appropriate check indicator for the current state
+ String prefix = getPropertyPrefix();
+ Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
+ Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
+ SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon,
+ acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix());
accContext.dispose();
}
@@ -239,8 +242,4 @@
updateStyle((JMenu)e.getSource());
}
}
-
- private boolean useCheckAndArrow() {
- return !((JMenu)menuItem).isTopLevelMenu();
- }
}
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java Fri Jul 25 14:26:27 2008 -0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java Fri Aug 08 20:49:26 2008 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,34 +58,6 @@
*/
class SynthPopupMenuUI extends BasicPopupMenuUI implements
PropertyChangeListener, SynthUI {
- /**
- * Maximum size of the text portion of the children menu items.
- */
- private int maxTextWidth;
-
- /**
- * Maximum size of the icon portion of the children menu items.
- */
- private int maxIconWidth;
-
- /**
- * Maximum size of the spacing between the text and accelerator
- * portions of the children menu items.
- */
- private int maxAccelSpacingWidth;
-
- /**
- * Maximum size of the text for the accelerator portion of the children
- * menu items.
- */
- private int maxAcceleratorWidth;
-
- /*
- * Maximum icon and text offsets of the children menu items.
- */
- private int maxTextOffset;
- private int maxIconOffset;
-
private SynthStyle style;
public static ComponentUI createUI(JComponent x) {
@@ -153,90 +125,6 @@
return SynthLookAndFeel.getComponentState(c);
}
- /**
- * Resets the max text and accerator widths,
- * text and icon offsets.
- */
- void resetAlignmentHints() {
- maxTextWidth = maxIconWidth
- = maxAccelSpacingWidth = maxAcceleratorWidth
- = maxTextOffset = maxIconOffset = 0;
- }
-
- /**
- * Adjusts the width needed to display the maximum menu item string.
- *
- * @param width Text width.
- * @return max width
- */
- int adjustTextWidth(int width) {
- maxTextWidth = Math.max(maxTextWidth, width);
- return maxTextWidth;
- }
-
- /**
- * Adjusts the width needed to display the maximum menu item icon.
- *
- * @param width Icon width.
- * @return max width
- */
- int adjustIconWidth(int width) {
- maxIconWidth = Math.max(maxIconWidth, width);
- return maxIconWidth;
- }
-
- /**
- * Adjusts the width needed to pad between the maximum menu item
- * text and accelerator.
- *
- * @param width Spacing width.
- * @return max width
- */
- int adjustAccelSpacingWidth(int width) {
- maxAccelSpacingWidth = Math.max(maxAccelSpacingWidth, width);
- return maxAccelSpacingWidth;
- }
-
- /**
- * Adjusts the width needed to display the maximum accelerator.
- *
- * @param width Text width.
- * @return max width
- */
- int adjustAcceleratorWidth(int width) {
- maxAcceleratorWidth = Math.max(maxAcceleratorWidth, width);
- return maxAcceleratorWidth;
- }
-
- /**
- * Maximum size needed to display accelerators of children menu items.
- */
- int getMaxAcceleratorWidth() {
- return maxAcceleratorWidth;
- }
-
- /**
- * Adjusts the text offset needed to align text horizontally.
- *
- * @param offset Text offset
- * @return max offset
- */
- int adjustTextOffset(int offset) {
- maxTextOffset = Math.max(maxTextOffset, offset);
- return maxTextOffset;
- }
-
- /**
- * Adjusts the icon offset needed to align icons horizontally
- *
- * @param offset Icon offset
- * @return max offset
- */
- int adjustIconOffset(int offset) {
- maxIconOffset = Math.max(maxIconOffset, offset);
- return maxIconOffset;
- }
-
public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/swing/MenuItemLayoutHelper.java Fri Aug 08 20:49:26 2008 +0400
@@ -0,0 +1,1339 @@
+/*
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.swing;
+
+import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.View;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Calculates preferred size and layouts menu items.
+ */
+public class MenuItemLayoutHelper {
+
+ /* Client Property keys for calculation of maximal widths */
+ public static final StringUIClientPropertyKey MAX_ARROW_WIDTH =
+ new StringUIClientPropertyKey("maxArrowWidth");
+ public static final StringUIClientPropertyKey MAX_CHECK_WIDTH =
+ new StringUIClientPropertyKey("maxCheckWidth");
+ public static final StringUIClientPropertyKey MAX_ICON_WIDTH =
+ new StringUIClientPropertyKey("maxIconWidth");
+ public static final StringUIClientPropertyKey MAX_TEXT_WIDTH =
+ new StringUIClientPropertyKey("maxTextWidth");
+ public static final StringUIClientPropertyKey MAX_ACC_WIDTH =
+ new StringUIClientPropertyKey("maxAccWidth");
+ public static final StringUIClientPropertyKey MAX_LABEL_WIDTH =
+ new StringUIClientPropertyKey("maxLabelWidth");
+
+ private JMenuItem mi;
+ private JComponent miParent;
+
+ private Font font;
+ private Font accFont;
+ private FontMetrics fm;
+ private FontMetrics accFm;
+
+ private Icon icon;
+ private Icon checkIcon;
+ private Icon arrowIcon;
+ private String text;
+ private String accText;
+
+ private boolean isColumnLayout;
+ private boolean useCheckAndArrow;
+ private boolean isLeftToRight;
+ private boolean isTopLevelMenu;
+ private View htmlView;
+
+ private int verticalAlignment;
+ private int horizontalAlignment;
+ private int verticalTextPosition;
+ private int horizontalTextPosition;
+ private int gap;
+ private int leadingGap;
+ private int afterCheckIconGap;
+ private int minTextOffset;
+
+ private Rectangle viewRect;
+
+ private RectSize iconSize;
+ private RectSize textSize;
+ private RectSize accSize;
+ private RectSize checkSize;
+ private RectSize arrowSize;
+ private RectSize labelSize;
+
+ /**
+ * The empty protected constructor is necessary for derived classes.
+ */
+ protected MenuItemLayoutHelper() {
+ }
+
+ public MenuItemLayoutHelper(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
+ Rectangle viewRect, int gap, String accDelimiter,
+ boolean isLeftToRight, Font font, Font accFont,
+ boolean useCheckAndArrow, String propertyPrefix) {
+ reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
+ isLeftToRight, font, accFont, useCheckAndArrow, propertyPrefix);
+ }
+
+ protected void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
+ Rectangle viewRect, int gap, String accDelimiter,
+ boolean isLeftToRight, Font font, Font accFont,
+ boolean useCheckAndArrow, String propertyPrefix) {
+ this.mi = mi;
+ this.miParent = getMenuItemParent(mi);
+ this.accText = getAccText(accDelimiter);
+ this.verticalAlignment = mi.getVerticalAlignment();
+ this.horizontalAlignment = mi.getHorizontalAlignment();
+ this.verticalTextPosition = mi.getVerticalTextPosition();
+ this.horizontalTextPosition = mi.getHorizontalTextPosition();
+ this.useCheckAndArrow = useCheckAndArrow;
+ this.font = font;
+ this.accFont = accFont;
+ this.fm = mi.getFontMetrics(font);
+ this.accFm = mi.getFontMetrics(accFont);
+ this.isLeftToRight = isLeftToRight;
+ this.isColumnLayout = isColumnLayout(isLeftToRight,
+ horizontalAlignment, horizontalTextPosition,
+ verticalTextPosition);
+ this.isTopLevelMenu = (this.miParent == null) ? true : false;
+ this.checkIcon = checkIcon;
+ this.icon = getIcon(propertyPrefix);
+ this.arrowIcon = arrowIcon;
+ this.text = mi.getText();
+ this.gap = gap;
+ this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix);
+ this.minTextOffset = getMinTextOffset(propertyPrefix);
+ this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey);
+ this.viewRect = viewRect;
+
+ this.iconSize = new RectSize();
+ this.textSize = new RectSize();
+ this.accSize = new RectSize();
+ this.checkSize = new RectSize();
+ this.arrowSize = new RectSize();
+ this.labelSize = new RectSize();
+ calcWidthsAndHeights();
+ setOriginalWidths();
+ calcMaxWidths();
+
+ this.leadingGap = getLeadingGap(propertyPrefix);
+ calcMaxTextOffset(viewRect);
+ }
+
+ private void setOriginalWidths() {
+ iconSize.origWidth = iconSize.width;
+ textSize.origWidth = textSize.width;
+ accSize.origWidth = accSize.width;
+ checkSize.origWidth = checkSize.width;
+ arrowSize.origWidth = arrowSize.width;
+ }
+
+ private String getAccText(String acceleratorDelimiter) {
+ String accText = "";
+ KeyStroke accelerator = mi.getAccelerator();
+ if (accelerator != null) {
+ int modifiers = accelerator.getModifiers();
+ if (modifiers > 0) {
+ accText = KeyEvent.getKeyModifiersText(modifiers);
+ accText += acceleratorDelimiter;
+ }
+ int keyCode = accelerator.getKeyCode();
+ if (keyCode != 0) {
+ accText += KeyEvent.getKeyText(keyCode);
+ } else {
+ accText += accelerator.getKeyChar();
+ }
+ }
+ return accText;
+ }
+
+ private Icon getIcon(String propertyPrefix) {
+ // In case of column layout, .checkIconFactory is defined for this UI,
+ // the icon is compatible with it and useCheckAndArrow() is true,
+ // then the icon is handled by the checkIcon.
+ Icon icon = null;
+ MenuItemCheckIconFactory iconFactory =
+ (MenuItemCheckIconFactory) UIManager.get(propertyPrefix
+ + ".checkIconFactory");
+ if (!isColumnLayout || !useCheckAndArrow || iconFactory == null
+ || !iconFactory.isCompatible(checkIcon, propertyPrefix)) {
+ icon = mi.getIcon();
+ }
+ return icon;
+ }
+
+ private int getMinTextOffset(String propertyPrefix) {
+ int minimumTextOffset = 0;
+ Object minimumTextOffsetObject =
+ UIManager.get(propertyPrefix + ".minimumTextOffset");
+ if (minimumTextOffsetObject instanceof Integer) {
+ minimumTextOffset = (Integer) minimumTextOffsetObject;
+ }
+ return minimumTextOffset;
+ }
+
+ private int getAfterCheckIconGap(String propertyPrefix) {
+ int afterCheckIconGap = gap;
+ Object afterCheckIconGapObject =
+ UIManager.get(propertyPrefix + ".afterCheckIconGap");
+ if (afterCheckIconGapObject instanceof Integer) {
+ afterCheckIconGap = (Integer) afterCheckIconGapObject;
+ }
+ return afterCheckIconGap;
+ }
+
+ private int getLeadingGap(String propertyPrefix) {
+ if (checkSize.getMaxWidth() > 0) {
+ return getCheckOffset(propertyPrefix);
+ } else {
+ return gap; // There is no any check icon
+ }
+ }
+
+ private int getCheckOffset(String propertyPrefix) {
+ int checkIconOffset = gap;
+ Object checkIconOffsetObject =
+ UIManager.get(propertyPrefix + ".checkIconOffset");
+ if (checkIconOffsetObject instanceof Integer) {
+ checkIconOffset = (Integer) checkIconOffsetObject;
+ }
+ return checkIconOffset;
+ }
+
+ protected void calcWidthsAndHeights() {
+ // iconRect
+ if (icon != null) {
+ iconSize.width = icon.getIconWidth();
+ iconSize.height = icon.getIconHeight();
+ }
+
+ // accRect
+ if (!accText.equals("")) {
+ accSize.width = SwingUtilities2.stringWidth(mi, accFm, accText);
+ accSize.height = accFm.getHeight();
+ }
+
+ // textRect
+ if (text == null) {
+ text = "";
+ } else if (!text.equals("")) {
+ if (htmlView != null) {
+ // Text is HTML
+ textSize.width =
+ (int) htmlView.getPreferredSpan(View.X_AXIS);
+ textSize.height =
+ (int) htmlView.getPreferredSpan(View.Y_AXIS);
+ } else {
+ // Text isn't HTML
+ textSize.width = SwingUtilities2.stringWidth(mi, fm, text);
+ textSize.height = fm.getHeight();
+ }
+ }
+
+ if (useCheckAndArrow) {
+ // checkIcon
+ if (checkIcon != null) {
+ checkSize.width = checkIcon.getIconWidth();
+ checkSize.height = checkIcon.getIconHeight();
+ }
+ // arrowRect
+ if (arrowIcon != null) {
+ arrowSize.width = arrowIcon.getIconWidth();
+ arrowSize.height = arrowIcon.getIconHeight();
+ }
+ }
+
+ // labelRect
+ if (isColumnLayout) {
+ labelSize.width = iconSize.width + textSize.width + gap;
+ labelSize.height = max(checkSize.height, iconSize.height,
+ textSize.height, accSize.height, arrowSize.height);
+ } else {
+ Rectangle textRect = new Rectangle();
+ Rectangle iconRect = new Rectangle();
+ SwingUtilities.layoutCompoundLabel(mi, fm, text, icon,
+ verticalAlignment, horizontalAlignment,
+ verticalTextPosition, horizontalTextPosition,
+ viewRect, iconRect, textRect, gap);
+ Rectangle labelRect = iconRect.union(textRect);
+ labelSize.height = labelRect.height;
+ labelSize.width = labelRect.width;
+ }
+ }
+
+ protected void calcMaxWidths() {
+ calcMaxWidth(checkSize, MAX_CHECK_WIDTH);
+ calcMaxWidth(arrowSize, MAX_ARROW_WIDTH);
+ calcMaxWidth(accSize, MAX_ACC_WIDTH);
+
+ if (isColumnLayout) {
+ calcMaxWidth(iconSize, MAX_ICON_WIDTH);
+ calcMaxWidth(textSize, MAX_TEXT_WIDTH);
+ int curGap = gap;
+ if ((iconSize.getMaxWidth() == 0)
+ || (textSize.getMaxWidth() == 0)) {
+ curGap = 0;
+ }
+ labelSize.maxWidth =
+ calcMaxValue(MAX_LABEL_WIDTH, iconSize.maxWidth
+ + textSize.maxWidth + curGap);
+ } else {
+ // We shouldn't use current icon and text widths
+ // in maximal widths calculation for complex layout.
+ iconSize.maxWidth = getParentIntProperty(MAX_ICON_WIDTH);
+ calcMaxWidth(labelSize, MAX_LABEL_WIDTH);
+ // If maxLabelWidth is wider
+ // than the widest icon + the widest text + gap,
+ // we should update the maximal text witdh
+ int candidateTextWidth = labelSize.maxWidth - iconSize.maxWidth;
+ if (iconSize.maxWidth > 0) {
+ candidateTextWidth -= gap;
+ }
+ textSize.maxWidth = calcMaxValue(MAX_TEXT_WIDTH, candidateTextWidth);
+ }
+ }
+
+ protected void calcMaxWidth(RectSize rs, Object key) {
+ rs.maxWidth = calcMaxValue(key, rs.width);
+ }
+
+ /**
+ * Calculates and returns maximal value through specified parent component
+ * client property.
+ *
+ * @param propertyName name of the property, which stores the maximal value.
+ * @param value a value which pretends to be maximal
+ * @return maximal value among the parent property and the value.
+ */
+ protected int calcMaxValue(Object propertyName, int value) {
+ // Get maximal value from parent client property
+ int maxValue = getParentIntProperty(propertyName);
+ // Store new maximal width in parent client property
+ if (value > maxValue) {
+ if (miParent != null) {
+ miParent.putClientProperty(propertyName, value);
+ }
+ return value;
+ } else {
+ return maxValue;
+ }
+ }
+
+ /**
+ * Returns parent client property as int.
+ * @param propertyName name of the parent property.
+ * @return value of the property as int.
+ */
+ protected int getParentIntProperty(Object propertyName) {
+ Object value = null;
+ if (miParent != null) {
+ value = miParent.getClientProperty(propertyName);
+ }
+ if ((value == null) || !(value instanceof Integer)) {
+ value = 0;
+ }
+ return (Integer) value;
+ }
+
+ public static boolean isColumnLayout(boolean isLeftToRight,
+ JMenuItem mi) {
+ assert(mi != null);
+ return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(),
+ mi.getHorizontalTextPosition(), mi.getVerticalTextPosition());
+ }
+
+ /**
+ * Answers should we do column layout for a menu item or not.
+ * We do it when a user doesn't set any alignments
+ * and text positions manually, except the vertical alignment.
+ */
+ public static boolean isColumnLayout(boolean isLeftToRight,
+ int horizontalAlignment,
+ int horizontalTextPosition,
+ int verticalTextPosition) {
+ if (verticalTextPosition != SwingConstants.CENTER) {
+ return false;
+ }
+ if (isLeftToRight) {
+ if (horizontalAlignment != SwingConstants.LEADING
+ && horizontalAlignment != SwingConstants.LEFT) {
+ return false;
+ }
+ if (horizontalTextPosition != SwingConstants.TRAILING
+ && horizontalTextPosition != SwingConstants.RIGHT) {
+ return false;
+ }
+ } else {
+ if (horizontalAlignment != SwingConstants.LEADING
+ && horizontalAlignment != SwingConstants.RIGHT) {
+ return false;
+ }
+ if (horizontalTextPosition != SwingConstants.TRAILING
+ && horizontalTextPosition != SwingConstants.LEFT) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Calculates maximal text offset.
+ * It is required for some L&Fs (ex: Vista L&F).
+ * The offset is meaningful only for L2R column layout.
+ *
+ * @param viewRect the rectangle, the maximal text offset
+ * will be calculated for.
+ */
+ private void calcMaxTextOffset(Rectangle viewRect) {
+ if (!isColumnLayout || !isLeftToRight) {
+ return;
+ }
+
+ // Calculate the current text offset
+ int offset = viewRect.x + leadingGap + checkSize.maxWidth
+ + afterCheckIconGap + iconSize.maxWidth + gap;
+ if (checkSize.maxWidth == 0) {
+ offset -= afterCheckIconGap;
+ }
+ if (iconSize.maxWidth == 0) {
+ offset -= gap;
+ }
+
+ // maximal text offset shouldn't be less than minimal text offset;
+ if (offset < minTextOffset) {
+ offset = minTextOffset;
+ }
+
+ // Calculate and store the maximal text offset
+ calcMaxValue(SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET, offset);
+ }
+
+ /**
+ * Layout icon, text, check icon, accelerator text and arrow icon
+ * in the viewRect and return their positions.
+ *
+ * If horizontalAlignment, verticalTextPosition and horizontalTextPosition
+ * are default (user doesn't set any manually) the layouting algorithm is:
+ * Elements are layouted in the five columns:
+ * check icon + icon + text + accelerator text + arrow icon
+ *
+ * In the other case elements are layouted in the four columns:
+ * check icon + label + accelerator text + arrow icon
+ * Label is union of icon and text.
+ *
+ * The order of columns can be reversed.
+ * It depends on the menu item orientation.
+ */
+ public LayoutResult layoutMenuItem() {
+ LayoutResult lr = createLayoutResult();
+ prepareForLayout(lr);
+
+ if (isColumnLayout()) {
+ if (isLeftToRight()) {
+ doLTRColumnLayout(lr, getLTRColumnAlignment());
+ } else {
+ doRTLColumnLayout(lr, getRTLColumnAlignment());
+ }
+ } else {
+ if (isLeftToRight()) {
+ doLTRComplexLayout(lr, getLTRColumnAlignment());
+ } else {
+ doRTLComplexLayout(lr, getRTLColumnAlignment());
+ }
+ }
+
+ alignAccCheckAndArrowVertically(lr);
+ return lr;
+ }
+
+ private LayoutResult createLayoutResult() {
+ return new LayoutResult(
+ new Rectangle(iconSize.width, iconSize.height),
+ new Rectangle(textSize.width, textSize.height),
+ new Rectangle(accSize.width, accSize.height),
+ new Rectangle(checkSize.width, checkSize.height),
+ new Rectangle(arrowSize.width, arrowSize.height),
+ new Rectangle(labelSize.width, labelSize.height)
+ );
+ }
+
+ public ColumnAlignment getLTRColumnAlignment() {
+ return ColumnAlignment.LEFT_ALIGNMENT;
+ }
+
+ public ColumnAlignment getRTLColumnAlignment() {
+ return ColumnAlignment.RIGHT_ALIGNMENT;
+ }
+
+ protected void prepareForLayout(LayoutResult lr) {
+ lr.checkRect.width = checkSize.maxWidth;
+ lr.accRect.width = accSize.maxWidth;
+ lr.arrowRect.width = arrowSize.maxWidth;
+ }
+
+ /**
+ * Aligns the accelertor text and the check and arrow icons vertically
+ * with the center of the label rect.
+ */
+ private void alignAccCheckAndArrowVertically(LayoutResult lr) {
+ lr.accRect.y = (int)(lr.labelRect.y
+ + (float)lr.labelRect.height/2
+ - (float)lr.accRect.height/2);
+ fixVerticalAlignment(lr, lr.accRect);
+ if (useCheckAndArrow) {
+ lr.arrowRect.y = (int)(lr.labelRect.y
+ + (float)lr.labelRect.height/2
+ - (float)lr.arrowRect.height/2);
+ lr.checkRect.y = (int)(lr.labelRect.y
+ + (float)lr.labelRect.height/2
+ - (float)lr.checkRect.height/2);
+ fixVerticalAlignment(lr, lr.arrowRect);
+ fixVerticalAlignment(lr, lr.checkRect);
+ }
+ }
+
+ /**
+ * Fixes vertical alignment of all menu item elements if rect.y
+ * or (rect.y + rect.height) is out of viewRect bounds
+ */
+ private void fixVerticalAlignment(LayoutResult lr, Rectangle r) {
+ int delta = 0;
+ if (r.y < viewRect.y) {
+ delta = viewRect.y - r.y;
+ } else if (r.y + r.height > viewRect.y + viewRect.height) {
+ delta = viewRect.y + viewRect.height - r.y - r.height;
+ }
+ if (delta != 0) {
+ lr.checkRect.y += delta;
+ lr.iconRect.y += delta;
+ lr.textRect.y += delta;
+ lr.accRect.y += delta;
+ lr.arrowRect.y += delta;
+ lr.labelRect.y += delta;
+ }
+ }
+
+ private void doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
+ // Set maximal width for all the five basic rects
+ // (three other ones are already maximal)
+ lr.iconRect.width = iconSize.maxWidth;
+ lr.textRect.width = textSize.maxWidth;
+
+ // Set X coordinates
+ // All rects will be aligned at the left side
+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
+ lr.iconRect, lr.textRect);
+
+ // Tune afterCheckIconGap
+ if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
+ lr.iconRect.x += afterCheckIconGap - gap;
+ lr.textRect.x += afterCheckIconGap - gap;
+ }
+
+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
+ lr.arrowRect, lr.accRect);
+
+ // Take into account minimal text offset
+ int textOffset = lr.textRect.x - viewRect.x;
+ if (!isTopLevelMenu && (textOffset < minTextOffset)) {
+ lr.textRect.x += minTextOffset - textOffset;
+ }
+
+ alignRects(lr, alignment);
+
+ // Take into account the left side bearings for text and accelerator text.
+ fixTextRects(lr);
+
+ // Set Y coordinate for text and icon.
+ // Y coordinates for other rects
+ // will be calculated later in layoutMenuItem.
+ calcTextAndIconYPositions(lr);
+
+ // Calculate valid X and Y coordinates for labelRect
+ lr.setLabelRect(lr.textRect.union(lr.iconRect));
+ }
+
+ private void doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
+ lr.labelRect.width = labelSize.maxWidth;
+
+ // Set X coordinates
+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
+ lr.labelRect);
+
+ // Tune afterCheckIconGap
+ if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
+ lr.labelRect.x += afterCheckIconGap - gap;
+ }
+
+ calcXPositionsRTL(viewRect.x + viewRect.width,
+ leadingGap, gap, lr.arrowRect, lr.accRect);
+
+ // Take into account minimal text offset
+ int labelOffset = lr.labelRect.x - viewRect.x;
+ if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
+ lr.labelRect.x += minTextOffset - labelOffset;
+ }
+
+ alignRects(lr, alignment);
+
+ // Take into account the left side bearing for accelerator text.
+ // The LSB for text is taken into account in layoutCompoundLabel() below.
+ fixAccTextRect(lr);
+
+ // Center labelRect vertically
+ calcLabelYPosition(lr);
+
+ layoutIconAndTextInLabelRect(lr);
+ }
+
+ private void doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
+ // Set maximal width for all the five basic rects
+ // (three other ones are already maximal)
+ lr.iconRect.width = iconSize.maxWidth;
+ lr.textRect.width = textSize.maxWidth;
+
+ // Set X coordinates
+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
+ lr.checkRect, lr.iconRect, lr.textRect);
+
+ // Tune the gap after check icon
+ if (lr.checkRect.width > 0) { // there is the gap after check icon
+ lr.iconRect.x -= afterCheckIconGap - gap;
+ lr.textRect.x -= afterCheckIconGap - gap;
+ }
+
+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect,
+ lr.accRect);
+
+ // Take into account minimal text offset
+ int textOffset = (viewRect.x + viewRect.width)
+ - (lr.textRect.x + lr.textRect.width);
+ if (!isTopLevelMenu && (textOffset < minTextOffset)) {
+ lr.textRect.x -= minTextOffset - textOffset;
+ }
+
+ alignRects(lr, alignment);
+
+ // Take into account the left side bearings for text and accelerator text.
+ fixTextRects(lr);
+
+ // Set Y coordinates for text and icon.
+ // Y coordinates for other rects
+ // will be calculated later in layoutMenuItem.
+ calcTextAndIconYPositions(lr);
+
+ // Calculate valid X and Y coordinate for labelRect
+ lr.setLabelRect(lr.textRect.union(lr.iconRect));
+ }
+
+ private void doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
+ lr.labelRect.width = labelSize.maxWidth;
+
+ // Set X coordinates
+ calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
+ lr.checkRect, lr.labelRect);
+
+ // Tune the gap after check icon
+ if (lr.checkRect.width > 0) { // there is the gap after check icon
+ lr.labelRect.x -= afterCheckIconGap - gap;
+ }
+
+ calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, lr.accRect);
+
+ // Take into account minimal text offset
+ int labelOffset = (viewRect.x + viewRect.width)
+ - (lr.labelRect.x + lr.labelRect.width);
+ if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
+ lr.labelRect.x -= minTextOffset - labelOffset;
+ }
+
+ alignRects(lr, alignment);
+
+ // Take into account the left side bearing for accelerator text.
+ // The LSB for text is taken into account in layoutCompoundLabel() below.
+ fixAccTextRect(lr);
+
+ // Center labelRect vertically
+ calcLabelYPosition(lr);
+
+ layoutIconAndTextInLabelRect(lr);
+ }
+
+ private void alignRects(LayoutResult lr, ColumnAlignment alignment) {
+ alignRect(lr.checkRect, alignment.getCheckAlignment(),
+ checkSize.getOrigWidth());
+ alignRect(lr.iconRect, alignment.getIconAlignment(),
+ iconSize.getOrigWidth());
+ alignRect(lr.textRect, alignment.getTextAlignment(),
+ textSize.getOrigWidth());
+ alignRect(lr.accRect, alignment.getAccAlignment(),
+ accSize.getOrigWidth());
+ alignRect(lr.arrowRect, alignment.getArrowAlignment(),
+ arrowSize.getOrigWidth());
+ }
+
+ private void alignRect(Rectangle rect, int alignment, int origWidth) {
+ if (alignment != SwingUtilities.LEFT) {
+ rect.x = rect.x + rect.width - origWidth;
+ rect.width = origWidth;
+ }
+ }
+
+ protected void layoutIconAndTextInLabelRect(LayoutResult lr) {
+ lr.setTextRect(new Rectangle());
+ lr.setIconRect(new Rectangle());
+ SwingUtilities.layoutCompoundLabel(
+ mi, fm, text,icon, verticalAlignment, horizontalAlignment,
+ verticalTextPosition, horizontalTextPosition, lr.labelRect,
+ lr.iconRect, lr.textRect, gap);
+ }
+
+ private void calcXPositionsLTR(int startXPos, int leadingGap,
+ int gap, Rectangle... rects) {
+ int curXPos = startXPos + leadingGap;
+ for (Rectangle rect : rects) {
+ rect.x = curXPos;
+ if (rect.width > 0) {
+ curXPos += rect.width + gap;
+ }
+ }
+ }
+
+ private void calcXPositionsRTL(int startXPos, int leadingGap,
+ int gap, Rectangle... rects) {
+ int curXPos = startXPos - leadingGap;
+ for (Rectangle rect : rects) {
+ rect.x = curXPos - rect.width;
+ if (rect.width > 0) {
+ curXPos -= rect.width + gap;
+ }
+ }
+ }
+
+ /**
+ * Takes into account the left side bearings for text and accelerator text
+ */
+ private void fixTextRects(LayoutResult lr) {
+ if (htmlView == null) { // The text isn't a HTML
+ int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, text);
+ if (lsb < 0) {
+ lr.textRect.x -= lsb;
+ }
+ }
+ fixAccTextRect(lr);
+ }
+
+ /**
+ * Takes into account the left side bearing for accelerator text
+ */
+ private void fixAccTextRect(LayoutResult lr) {
+ int lsb = SwingUtilities2.getLeftSideBearing(mi, accFm, accText);
+ if (lsb < 0) {
+ lr.accRect.x -= lsb;
+ }
+ }
+
+ /**
+ * Sets Y coordinates of text and icon
+ * taking into account the vertical alignment
+ */
+ private void calcTextAndIconYPositions(LayoutResult lr) {
+ if (verticalAlignment == SwingUtilities.TOP) {
+ lr.textRect.y = (int)(viewRect.y
+ + (float)lr.labelRect.height/2
+ - (float)lr.textRect.height/2);
+ lr.iconRect.y = (int)(viewRect.y
+ + (float)lr.labelRect.height/2
+ - (float)lr.iconRect.height/2);
+ } else if (verticalAlignment == SwingUtilities.CENTER) {
+ lr.textRect.y = (int)(viewRect.y
+ + (float)viewRect.height/2
+ - (float)lr.textRect.height/2);
+ lr.iconRect.y = (int)(viewRect.y
+ + (float)viewRect.height/2
+ - (float)lr.iconRect.height/2);
+ }
+ else if (verticalAlignment == SwingUtilities.BOTTOM) {
+ lr.textRect.y = (int)(viewRect.y
+ + viewRect.height
+ - (float)lr.labelRect.height/2
+ - (float)lr.textRect.height/2);
+ lr.iconRect.y = (int)(viewRect.y
+ + viewRect.height
+ - (float)lr.labelRect.height/2
+ - (float)lr.iconRect.height/2);
+ }
+ }
+
+ /**
+ * Sets labelRect Y coordinate
+ * taking into account the vertical alignment
+ */
+ private void calcLabelYPosition(LayoutResult lr) {
+ if (verticalAlignment == SwingUtilities.TOP) {
+ lr.labelRect.y = viewRect.y;
+ } else if (verticalAlignment == SwingUtilities.CENTER) {
+ lr.labelRect.y = (int)(viewRect.y
+ + (float)viewRect.height/2
+ - (float)lr.labelRect.height/2);
+ } else if (verticalAlignment == SwingUtilities.BOTTOM) {
+ lr.labelRect.y = viewRect.y + viewRect.height
+ - lr.labelRect.height;
+ }
+ }
+
+ /**
+ * Returns parent of this component if it is not a top-level menu
+ * Otherwise returns null.
+ * @param menuItem the menu item whose parent will be returned.
+ * @return parent of this component if it is not a top-level menu
+ * Otherwise returns null.
+ */
+ public static JComponent getMenuItemParent(JMenuItem menuItem) {
+ Container parent = menuItem.getParent();
+ if ((parent instanceof JComponent) &&
+ (!(menuItem instanceof JMenu) ||
+ !((JMenu)menuItem).isTopLevelMenu())) {
+ return (JComponent) parent;
+ } else {
+ return null;
+ }
+ }
+
+ public static void clearUsedParentClientProperties(JMenuItem menuItem) {
+ clearUsedClientProperties(getMenuItemParent(menuItem));
+ }
+
+ public static void clearUsedClientProperties(JComponent c) {
+ if (c != null) {
+ c.putClientProperty(MAX_ARROW_WIDTH, null);
+ c.putClientProperty(MAX_CHECK_WIDTH, null);
+ c.putClientProperty(MAX_ACC_WIDTH, null);
+ c.putClientProperty(MAX_TEXT_WIDTH, null);
+ c.putClientProperty(MAX_ICON_WIDTH, null);
+ c.putClientProperty(MAX_LABEL_WIDTH, null);
+ c.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null);
+ }
+ }
+
+ /**
+ * Finds and returns maximal integer value in the given array.
+ * @param values array where the search will be performed.
+ * @return maximal vaule.
+ */
+ public static int max(int... values) {
+ int maxValue = Integer.MIN_VALUE;
+ for (int i : values) {
+ if (i > maxValue) {
+ maxValue = i;
+ }
+ }
+ return maxValue;
+ }
+
+ public static Rectangle createMaxRect() {
+ return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ public static void addMaxWidth(RectSize size, int gap, Dimension result) {
+ if (size.maxWidth > 0) {
+ result.width += size.maxWidth + gap;
+ }
+ }
+
+ public static void addWidth(int width, int gap, Dimension result) {
+ if (width > 0) {
+ result.width += width + gap;
+ }
+ }
+
+ public JMenuItem getMenuItem() {
+ return mi;
+ }
+
+ public JComponent getMenuItemParent() {
+ return miParent;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public Font getAccFont() {
+ return accFont;
+ }
+
+ public FontMetrics getFontMetrics() {
+ return fm;
+ }
+
+ public FontMetrics getAccFontMetrics() {
+ return accFm;
+ }
+
+ public Icon getIcon() {
+ return icon;
+ }
+
+ public Icon getCheckIcon() {
+ return checkIcon;
+ }
+
+ public Icon getArrowIcon() {
+ return arrowIcon;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public String getAccText() {
+ return accText;
+ }
+
+ public boolean isColumnLayout() {
+ return isColumnLayout;
+ }
+
+ public boolean useCheckAndArrow() {
+ return useCheckAndArrow;
+ }
+
+ public boolean isLeftToRight() {
+ return isLeftToRight;
+ }
+
+ public boolean isTopLevelMenu() {
+ return isTopLevelMenu;
+ }
+
+ public View getHtmlView() {
+ return htmlView;
+ }
+
+ public int getVerticalAlignment() {
+ return verticalAlignment;
+ }
+
+ public int getHorizontalAlignment() {
+ return horizontalAlignment;
+ }
+
+ public int getVerticalTextPosition() {
+ return verticalTextPosition;
+ }
+
+ public int getHorizontalTextPosition() {
+ return horizontalTextPosition;
+ }
+
+ public int getGap() {
+ return gap;
+ }
+
+ public int getLeadingGap() {
+ return leadingGap;
+ }
+
+ public int getAfterCheckIconGap() {
+ return afterCheckIconGap;
+ }
+
+ public int getMinTextOffset() {
+ return minTextOffset;
+ }
+
+ public Rectangle getViewRect() {
+ return viewRect;
+ }
+
+ public RectSize getIconSize() {
+ return iconSize;
+ }
+
+ public RectSize getTextSize() {
+ return textSize;
+ }
+
+ public RectSize getAccSize() {
+ return accSize;
+ }
+
+ public RectSize getCheckSize() {
+ return checkSize;
+ }
+
+ public RectSize getArrowSize() {
+ return arrowSize;
+ }
+
+ public RectSize getLabelSize() {
+ return labelSize;
+ }
+
+ protected void setMenuItem(JMenuItem mi) {
+ this.mi = mi;
+ }
+
+ protected void setMenuItemParent(JComponent miParent) {
+ this.miParent = miParent;
+ }
+
+ protected void setFont(Font font) {
+ this.font = font;
+ }
+
+ protected void setAccFont(Font accFont) {
+ this.accFont = accFont;
+ }
+
+ protected void setFontMetrics(FontMetrics fm) {
+ this.fm = fm;
+ }
+
+ protected void setAccFontMetrics(FontMetrics accFm) {
+ this.accFm = accFm;
+ }
+
+ protected void setIcon(Icon icon) {
+ this.icon = icon;
+ }
+
+ protected void setCheckIcon(Icon checkIcon) {
+ this.checkIcon = checkIcon;
+ }
+
+ protected void setArrowIcon(Icon arrowIcon) {
+ this.arrowIcon = arrowIcon;
+ }
+
+ protected void setText(String text) {
+ this.text = text;
+ }
+
+ protected void setAccText(String accText) {
+ this.accText = accText;
+ }
+
+ protected void setColumnLayout(boolean columnLayout) {
+ isColumnLayout = columnLayout;
+ }
+
+ protected void setUseCheckAndArrow(boolean useCheckAndArrow) {
+ this.useCheckAndArrow = useCheckAndArrow;
+ }
+
+ protected void setLeftToRight(boolean leftToRight) {
+ isLeftToRight = leftToRight;
+ }
+
+ protected void setTopLevelMenu(boolean topLevelMenu) {
+ isTopLevelMenu = topLevelMenu;
+ }
+
+ protected void setHtmlView(View htmlView) {
+ this.htmlView = htmlView;
+ }
+
+ protected void setVerticalAlignment(int verticalAlignment) {
+ this.verticalAlignment = verticalAlignment;
+ }
+
+ protected void setHorizontalAlignment(int horizontalAlignment) {
+ this.horizontalAlignment = horizontalAlignment;
+ }
+
+ protected void setVerticalTextPosition(int verticalTextPosition) {
+ this.verticalTextPosition = verticalTextPosition;
+ }
+
+ protected void setHorizontalTextPosition(int horizontalTextPosition) {
+ this.horizontalTextPosition = horizontalTextPosition;
+ }
+
+ protected void setGap(int gap) {
+ this.gap = gap;
+ }
+
+ protected void setLeadingGap(int leadingGap) {
+ this.leadingGap = leadingGap;
+ }
+
+ protected void setAfterCheckIconGap(int afterCheckIconGap) {
+ this.afterCheckIconGap = afterCheckIconGap;
+ }
+
+ protected void setMinTextOffset(int minTextOffset) {
+ this.minTextOffset = minTextOffset;
+ }
+
+ protected void setViewRect(Rectangle viewRect) {
+ this.viewRect = viewRect;
+ }
+
+ protected void setIconSize(RectSize iconSize) {
+ this.iconSize = iconSize;
+ }
+
+ protected void setTextSize(RectSize textSize) {
+ this.textSize = textSize;
+ }
+
+ protected void setAccSize(RectSize accSize) {
+ this.accSize = accSize;
+ }
+
+ protected void setCheckSize(RectSize checkSize) {
+ this.checkSize = checkSize;
+ }
+
+ protected void setArrowSize(RectSize arrowSize) {
+ this.arrowSize = arrowSize;
+ }
+
+ protected void setLabelSize(RectSize labelSize) {
+ this.labelSize = labelSize;
+ }
+
+ /**
+ * Returns false if the component is a JMenu and it is a top
+ * level menu (on the menubar).
+ */
+ public static boolean useCheckAndArrow(JMenuItem menuItem) {
+ boolean b = true;
+ if ((menuItem instanceof JMenu) &&
+ (((JMenu) menuItem).isTopLevelMenu())) {
+ b = false;
+ }
+ return b;
+ }
+
+ public static class LayoutResult {
+ private Rectangle iconRect;
+ private Rectangle textRect;
+ private Rectangle accRect;
+ private Rectangle checkRect;
+ private Rectangle arrowRect;
+ private Rectangle labelRect;
+
+ public LayoutResult() {
+ iconRect = new Rectangle();
+ textRect = new Rectangle();
+ accRect = new Rectangle();
+ checkRect = new Rectangle();
+ arrowRect = new Rectangle();
+ labelRect = new Rectangle();
+ }
+
+ public LayoutResult(Rectangle iconRect, Rectangle textRect,
+ Rectangle accRect, Rectangle checkRect,
+ Rectangle arrowRect, Rectangle labelRect) {
+ this.iconRect = iconRect;
+ this.textRect = textRect;
+ this.accRect = accRect;
+ this.checkRect = checkRect;
+ this.arrowRect = arrowRect;
+ this.labelRect = labelRect;
+ }
+
+ public Rectangle getIconRect() {
+ return iconRect;
+ }
+
+ public void setIconRect(Rectangle iconRect) {
+ this.iconRect = iconRect;
+ }
+
+ public Rectangle getTextRect() {
+ return textRect;
+ }
+
+ public void setTextRect(Rectangle textRect) {
+ this.textRect = textRect;
+ }
+
+ public Rectangle getAccRect() {
+ return accRect;
+ }
+
+ public void setAccRect(Rectangle accRect) {
+ this.accRect = accRect;
+ }
+
+ public Rectangle getCheckRect() {
+ return checkRect;
+ }
+
+ public void setCheckRect(Rectangle checkRect) {
+ this.checkRect = checkRect;
+ }
+
+ public Rectangle getArrowRect() {
+ return arrowRect;
+ }
+
+ public void setArrowRect(Rectangle arrowRect) {
+ this.arrowRect = arrowRect;
+ }
+
+ public Rectangle getLabelRect() {
+ return labelRect;
+ }
+
+ public void setLabelRect(Rectangle labelRect) {
+ this.labelRect = labelRect;
+ }
+
+ public Map<String, Rectangle> getAllRects() {
+ Map<String, Rectangle> result = new HashMap<String, Rectangle>();
+ result.put("checkRect", checkRect);
+ result.put("iconRect", iconRect);
+ result.put("textRect", textRect);
+ result.put("accRect", accRect);
+ result.put("arrowRect", arrowRect);
+ result.put("labelRect", labelRect);
+ return result;
+ }
+ }
+
+ public static class ColumnAlignment {
+ private int checkAlignment;
+ private int iconAlignment;
+ private int textAlignment;
+ private int accAlignment;
+ private int arrowAlignment;
+
+ public static final ColumnAlignment LEFT_ALIGNMENT =
+ new ColumnAlignment(
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT,
+ SwingConstants.LEFT
+ );
+
+ public static final ColumnAlignment RIGHT_ALIGNMENT =
+ new ColumnAlignment(
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT,
+ SwingConstants.RIGHT
+ );
+
+ public ColumnAlignment(int checkAlignment, int iconAlignment,
+ int textAlignment, int accAlignment,
+ int arrowAlignment) {
+ this.checkAlignment = checkAlignment;
+ this.iconAlignment = iconAlignment;
+ this.textAlignment = textAlignment;
+ this.accAlignment = accAlignment;
+ this.arrowAlignment = arrowAlignment;
+ }
+
+ public int getCheckAlignment() {
+ return checkAlignment;
+ }
+
+ public int getIconAlignment() {
+ return iconAlignment;
+ }
+
+ public int getTextAlignment() {
+ return textAlignment;
+ }
+
+ public int getAccAlignment() {
+ return accAlignment;
+ }
+
+ public int getArrowAlignment() {
+ return arrowAlignment;
+ }
+ }
+
+ public static class RectSize {
+ private int width;
+ private int height;
+ private int origWidth;
+ private int maxWidth;
+
+ public RectSize() {
+ }
+
+ public RectSize(int width, int height, int origWidth, int maxWidth) {
+ this.width = width;
+ this.height = height;
+ this.origWidth = origWidth;
+ this.maxWidth = maxWidth;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getOrigWidth() {
+ return origWidth;
+ }
+
+ public int getMaxWidth() {
+ return maxWidth;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public void setOrigWidth(int origWidth) {
+ this.origWidth = origWidth;
+ }
+
+ public void setMaxWidth(int maxWidth) {
+ this.maxWidth = maxWidth;
+ }
+
+ public String toString() {
+ return "[w=" + width + ",h=" + height + ",ow="
+ + origWidth + ",mw=" + maxWidth + "]";
+ }
+ }
+}