changeset 619:7d45141e5d30

6584657: GTK Look and Feel: Bugs in menu item layout Reviewed-by: peterz, alexp
author mlapshin
date Fri, 08 Aug 2008 20:49:26 +0400
parents 59940b984f90
children 3f7b2ea2d611
files src/share/classes/javax/swing/SwingUtilities.java src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java src/share/classes/sun/swing/MenuItemLayoutHelper.java
diffstat 10 files changed, 2000 insertions(+), 1665 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/javax/swing/SwingUtilities.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/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/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java	Fri Jul 25 14:26:27 2008 -0400
+++ b/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/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 + "]";
+        }
+    }
+}