1323 lines · 42.1 KB · Java
RawDownload
/* * Copyright (c) 2002, 2008, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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 int leftTextExtraWidth;     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();        calcExtraWidths();        calcWidthsAndHeights();        setOriginalWidths();        calcMaxWidths();         this.leadingGap = getLeadingGap(propertyPrefix);        calcMaxTextOffset(viewRect);    }     private void calcExtraWidths() {        leftTextExtraWidth = getLeftExtraWidth(text);    }     private int getLeftExtraWidth(String str) {        int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, str);        if (lsb < 0) {            return -lsb;        } else {            return 0;        }    }     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);            textRect.width += leftTextExtraWidth;            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),

Lines 1–500 of 1323.