Path: blob/master/src/java.desktop/share/classes/sun/swing/MenuItemLayoutHelper.java
41153 views
/*1* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.swing;2627import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;2829import javax.swing.*;30import javax.swing.plaf.basic.BasicHTML;31import javax.swing.text.View;32import java.awt.*;33import java.awt.event.KeyEvent;34import java.util.Map;35import java.util.HashMap;3637/**38* Calculates preferred size and layouts menu items.39*/40public class MenuItemLayoutHelper {4142/* Client Property keys for calculation of maximal widths */43public static final StringUIClientPropertyKey MAX_ARROW_WIDTH =44new StringUIClientPropertyKey("maxArrowWidth");45public static final StringUIClientPropertyKey MAX_CHECK_WIDTH =46new StringUIClientPropertyKey("maxCheckWidth");47public static final StringUIClientPropertyKey MAX_ICON_WIDTH =48new StringUIClientPropertyKey("maxIconWidth");49public static final StringUIClientPropertyKey MAX_TEXT_WIDTH =50new StringUIClientPropertyKey("maxTextWidth");51public static final StringUIClientPropertyKey MAX_ACC_WIDTH =52new StringUIClientPropertyKey("maxAccWidth");53public static final StringUIClientPropertyKey MAX_LABEL_WIDTH =54new StringUIClientPropertyKey("maxLabelWidth");5556private JMenuItem mi;57private JComponent miParent;5859private Font font;60private Font accFont;61private FontMetrics fm;62private FontMetrics accFm;6364private Icon icon;65private Icon checkIcon;66private Icon arrowIcon;67private String text;68private String accText;6970private boolean isColumnLayout;71private boolean useCheckAndArrow;72private boolean isLeftToRight;73private boolean isTopLevelMenu;74private View htmlView;7576private int verticalAlignment;77private int horizontalAlignment;78private int verticalTextPosition;79private int horizontalTextPosition;80private int gap;81private int leadingGap;82private int afterCheckIconGap;83private int minTextOffset;8485private int leftTextExtraWidth;8687private Rectangle viewRect;8889private RectSize iconSize;90private RectSize textSize;91private RectSize accSize;92private RectSize checkSize;93private RectSize arrowSize;94private RectSize labelSize;9596/**97* The empty protected constructor is necessary for derived classes.98*/99protected MenuItemLayoutHelper() {100}101102public MenuItemLayoutHelper(JMenuItem mi, Icon checkIcon, Icon arrowIcon,103Rectangle viewRect, int gap, String accDelimiter,104boolean isLeftToRight, Font font, Font accFont,105boolean useCheckAndArrow, String propertyPrefix) {106reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,107isLeftToRight, font, accFont, useCheckAndArrow, propertyPrefix);108}109110protected void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon,111Rectangle viewRect, int gap, String accDelimiter,112boolean isLeftToRight, Font font, Font accFont,113boolean useCheckAndArrow, String propertyPrefix) {114this.mi = mi;115this.miParent = getMenuItemParent(mi);116this.accText = getAccText(accDelimiter);117this.verticalAlignment = mi.getVerticalAlignment();118this.horizontalAlignment = mi.getHorizontalAlignment();119this.verticalTextPosition = mi.getVerticalTextPosition();120this.horizontalTextPosition = mi.getHorizontalTextPosition();121this.useCheckAndArrow = useCheckAndArrow;122this.font = font;123this.accFont = accFont;124this.fm = mi.getFontMetrics(font);125this.accFm = mi.getFontMetrics(accFont);126this.isLeftToRight = isLeftToRight;127this.isColumnLayout = isColumnLayout(isLeftToRight,128horizontalAlignment, horizontalTextPosition,129verticalTextPosition);130this.isTopLevelMenu = (this.miParent == null) ? true : false;131this.checkIcon = checkIcon;132this.icon = getIcon(propertyPrefix);133this.arrowIcon = arrowIcon;134this.text = mi.getText();135this.gap = gap;136this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix);137this.minTextOffset = getMinTextOffset(propertyPrefix);138this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey);139this.viewRect = viewRect;140141this.iconSize = new RectSize();142this.textSize = new RectSize();143this.accSize = new RectSize();144this.checkSize = new RectSize();145this.arrowSize = new RectSize();146this.labelSize = new RectSize();147calcExtraWidths();148calcWidthsAndHeights();149setOriginalWidths();150calcMaxWidths();151152this.leadingGap = getLeadingGap(propertyPrefix);153calcMaxTextOffset(viewRect);154}155156private void calcExtraWidths() {157leftTextExtraWidth = getLeftExtraWidth(text);158}159160private int getLeftExtraWidth(String str) {161int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, str);162if (lsb < 0) {163return -lsb;164} else {165return 0;166}167}168169private void setOriginalWidths() {170iconSize.origWidth = iconSize.width;171textSize.origWidth = textSize.width;172accSize.origWidth = accSize.width;173checkSize.origWidth = checkSize.width;174arrowSize.origWidth = arrowSize.width;175}176177@SuppressWarnings("deprecation")178private String getAccText(String acceleratorDelimiter) {179String accText = "";180KeyStroke accelerator = mi.getAccelerator();181if (accelerator != null) {182int modifiers = accelerator.getModifiers();183if (modifiers > 0) {184accText = KeyEvent.getKeyModifiersText(modifiers);185accText += acceleratorDelimiter;186}187int keyCode = accelerator.getKeyCode();188if (keyCode != 0) {189accText += KeyEvent.getKeyText(keyCode);190} else {191accText += accelerator.getKeyChar();192}193}194return accText;195}196197private Icon getIcon(String propertyPrefix) {198// In case of column layout, .checkIconFactory is defined for this UI,199// the icon is compatible with it and useCheckAndArrow() is true,200// then the icon is handled by the checkIcon.201Icon icon = null;202MenuItemCheckIconFactory iconFactory =203(MenuItemCheckIconFactory) UIManager.get(propertyPrefix204+ ".checkIconFactory");205if (!isColumnLayout || !useCheckAndArrow || iconFactory == null206|| !iconFactory.isCompatible(checkIcon, propertyPrefix)) {207icon = mi.getIcon();208}209return icon;210}211212private int getMinTextOffset(String propertyPrefix) {213int minimumTextOffset = 0;214Object minimumTextOffsetObject =215UIManager.get(propertyPrefix + ".minimumTextOffset");216if (minimumTextOffsetObject instanceof Integer) {217minimumTextOffset = (Integer) minimumTextOffsetObject;218}219return minimumTextOffset;220}221222private int getAfterCheckIconGap(String propertyPrefix) {223int afterCheckIconGap = gap;224Object afterCheckIconGapObject =225UIManager.get(propertyPrefix + ".afterCheckIconGap");226if (afterCheckIconGapObject instanceof Integer) {227afterCheckIconGap = (Integer) afterCheckIconGapObject;228}229return afterCheckIconGap;230}231232private int getLeadingGap(String propertyPrefix) {233if (checkSize.getMaxWidth() > 0) {234return getCheckOffset(propertyPrefix);235} else {236return gap; // There is no any check icon237}238}239240private int getCheckOffset(String propertyPrefix) {241int checkIconOffset = gap;242Object checkIconOffsetObject =243UIManager.get(propertyPrefix + ".checkIconOffset");244if (checkIconOffsetObject instanceof Integer) {245checkIconOffset = (Integer) checkIconOffsetObject;246}247return checkIconOffset;248}249250protected void calcWidthsAndHeights() {251// iconRect252if (icon != null) {253iconSize.width = icon.getIconWidth();254iconSize.height = icon.getIconHeight();255}256257// accRect258if (!accText.isEmpty()) {259accSize.width = SwingUtilities2.stringWidth(mi, accFm, accText);260accSize.height = accFm.getHeight();261}262263// textRect264if (text == null) {265text = "";266} else if (!text.isEmpty()) {267if (htmlView != null) {268// Text is HTML269textSize.width =270(int) htmlView.getPreferredSpan(View.X_AXIS);271textSize.height =272(int) htmlView.getPreferredSpan(View.Y_AXIS);273} else {274// Text isn't HTML275textSize.width = SwingUtilities2.stringWidth(mi, fm, text);276textSize.height = fm.getHeight();277}278}279280if (useCheckAndArrow) {281// checkIcon282if (checkIcon != null) {283checkSize.width = checkIcon.getIconWidth();284checkSize.height = checkIcon.getIconHeight();285}286// arrowRect287if (arrowIcon != null) {288arrowSize.width = arrowIcon.getIconWidth();289arrowSize.height = arrowIcon.getIconHeight();290}291}292293// labelRect294if (isColumnLayout) {295labelSize.width = iconSize.width + textSize.width + gap;296labelSize.height = max(checkSize.height, iconSize.height,297textSize.height, accSize.height, arrowSize.height);298} else {299Rectangle textRect = new Rectangle();300Rectangle iconRect = new Rectangle();301SwingUtilities.layoutCompoundLabel(mi, fm, text, icon,302verticalAlignment, horizontalAlignment,303verticalTextPosition, horizontalTextPosition,304viewRect, iconRect, textRect, gap);305textRect.width += leftTextExtraWidth;306Rectangle labelRect = iconRect.union(textRect);307labelSize.height = labelRect.height;308labelSize.width = labelRect.width;309}310}311312protected void calcMaxWidths() {313calcMaxWidth(checkSize, MAX_CHECK_WIDTH);314calcMaxWidth(arrowSize, MAX_ARROW_WIDTH);315calcMaxWidth(accSize, MAX_ACC_WIDTH);316317if (isColumnLayout) {318calcMaxWidth(iconSize, MAX_ICON_WIDTH);319calcMaxWidth(textSize, MAX_TEXT_WIDTH);320int curGap = gap;321if ((iconSize.getMaxWidth() == 0)322|| (textSize.getMaxWidth() == 0)) {323curGap = 0;324}325labelSize.maxWidth =326calcMaxValue(MAX_LABEL_WIDTH, iconSize.maxWidth327+ textSize.maxWidth + curGap);328} else {329// We shouldn't use current icon and text widths330// in maximal widths calculation for complex layout.331iconSize.maxWidth = getParentIntProperty(MAX_ICON_WIDTH);332calcMaxWidth(labelSize, MAX_LABEL_WIDTH);333// If maxLabelWidth is wider334// than the widest icon + the widest text + gap,335// we should update the maximal text witdh336int candidateTextWidth = labelSize.maxWidth - iconSize.maxWidth;337if (iconSize.maxWidth > 0) {338candidateTextWidth -= gap;339}340textSize.maxWidth = calcMaxValue(MAX_TEXT_WIDTH, candidateTextWidth);341}342}343344protected void calcMaxWidth(RectSize rs, Object key) {345rs.maxWidth = calcMaxValue(key, rs.width);346}347348/**349* Calculates and returns maximal value through specified parent component350* client property.351*352* @param propertyName name of the property, which stores the maximal value.353* @param value a value which pretends to be maximal354* @return maximal value among the parent property and the value.355*/356protected int calcMaxValue(Object propertyName, int value) {357// Get maximal value from parent client property358int maxValue = getParentIntProperty(propertyName);359// Store new maximal width in parent client property360if (value > maxValue) {361if (miParent != null) {362miParent.putClientProperty(propertyName, value);363}364return value;365} else {366return maxValue;367}368}369370/**371* Returns parent client property as int.372* @param propertyName name of the parent property.373* @return value of the property as int.374*/375protected int getParentIntProperty(Object propertyName) {376Object value = null;377if (miParent != null) {378value = miParent.getClientProperty(propertyName);379}380if ((value == null) || !(value instanceof Integer)) {381value = 0;382}383return (Integer) value;384}385386public static boolean isColumnLayout(boolean isLeftToRight,387JMenuItem mi) {388assert(mi != null);389return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(),390mi.getHorizontalTextPosition(), mi.getVerticalTextPosition());391}392393/**394* Answers should we do column layout for a menu item or not.395* We do it when a user doesn't set any alignments396* and text positions manually, except the vertical alignment.397*/398public static boolean isColumnLayout(boolean isLeftToRight,399int horizontalAlignment,400int horizontalTextPosition,401int verticalTextPosition) {402if (verticalTextPosition != SwingConstants.CENTER) {403return false;404}405if (isLeftToRight) {406if (horizontalAlignment != SwingConstants.LEADING407&& horizontalAlignment != SwingConstants.LEFT) {408return false;409}410if (horizontalTextPosition != SwingConstants.TRAILING411&& horizontalTextPosition != SwingConstants.RIGHT) {412return false;413}414} else {415if (horizontalAlignment != SwingConstants.LEADING416&& horizontalAlignment != SwingConstants.RIGHT) {417return false;418}419if (horizontalTextPosition != SwingConstants.TRAILING420&& horizontalTextPosition != SwingConstants.LEFT) {421return false;422}423}424return true;425}426427/**428* Calculates maximal text offset.429* It is required for some L&Fs (ex: Vista L&F).430* The offset is meaningful only for L2R column layout.431*432* @param viewRect the rectangle, the maximal text offset433* will be calculated for.434*/435private void calcMaxTextOffset(Rectangle viewRect) {436if (!isColumnLayout || !isLeftToRight) {437return;438}439440// Calculate the current text offset441int offset = viewRect.x + leadingGap + checkSize.maxWidth442+ afterCheckIconGap + iconSize.maxWidth + gap;443if (checkSize.maxWidth == 0) {444offset -= afterCheckIconGap;445}446if (iconSize.maxWidth == 0) {447offset -= gap;448}449450// maximal text offset shouldn't be less than minimal text offset;451if (offset < minTextOffset) {452offset = minTextOffset;453}454455// Calculate and store the maximal text offset456calcMaxValue(SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET, offset);457}458459/**460* Layout icon, text, check icon, accelerator text and arrow icon461* in the viewRect and return their positions.462*463* If horizontalAlignment, verticalTextPosition and horizontalTextPosition464* are default (user doesn't set any manually) the layouting algorithm is:465* Elements are layouted in the five columns:466* check icon + icon + text + accelerator text + arrow icon467*468* In the other case elements are layouted in the four columns:469* check icon + label + accelerator text + arrow icon470* Label is union of icon and text.471*472* The order of columns can be reversed.473* It depends on the menu item orientation.474*/475public LayoutResult layoutMenuItem() {476LayoutResult lr = createLayoutResult();477prepareForLayout(lr);478479if (isColumnLayout()) {480if (isLeftToRight()) {481doLTRColumnLayout(lr, getLTRColumnAlignment());482} else {483doRTLColumnLayout(lr, getRTLColumnAlignment());484}485} else {486if (isLeftToRight()) {487doLTRComplexLayout(lr, getLTRColumnAlignment());488} else {489doRTLComplexLayout(lr, getRTLColumnAlignment());490}491}492493alignAccCheckAndArrowVertically(lr);494return lr;495}496497private LayoutResult createLayoutResult() {498return new LayoutResult(499new Rectangle(iconSize.width, iconSize.height),500new Rectangle(textSize.width, textSize.height),501new Rectangle(accSize.width, accSize.height),502new Rectangle(checkSize.width, checkSize.height),503new Rectangle(arrowSize.width, arrowSize.height),504new Rectangle(labelSize.width, labelSize.height)505);506}507508public ColumnAlignment getLTRColumnAlignment() {509return ColumnAlignment.LEFT_ALIGNMENT;510}511512public ColumnAlignment getRTLColumnAlignment() {513return ColumnAlignment.RIGHT_ALIGNMENT;514}515516protected void prepareForLayout(LayoutResult lr) {517lr.checkRect.width = checkSize.maxWidth;518lr.accRect.width = accSize.maxWidth;519lr.arrowRect.width = arrowSize.maxWidth;520}521522/**523* Aligns the accelertor text and the check and arrow icons vertically524* with the center of the label rect.525*/526private void alignAccCheckAndArrowVertically(LayoutResult lr) {527lr.accRect.y = (int)(lr.labelRect.y528+ (float)lr.labelRect.height/2529- (float)lr.accRect.height/2);530fixVerticalAlignment(lr, lr.accRect);531if (useCheckAndArrow) {532lr.arrowRect.y = (int)(lr.labelRect.y533+ (float)lr.labelRect.height/2534- (float)lr.arrowRect.height/2);535lr.checkRect.y = (int)(lr.labelRect.y536+ (float)lr.labelRect.height/2537- (float)lr.checkRect.height/2);538fixVerticalAlignment(lr, lr.arrowRect);539fixVerticalAlignment(lr, lr.checkRect);540}541}542543/**544* Fixes vertical alignment of all menu item elements if rect.y545* or (rect.y + rect.height) is out of viewRect bounds546*/547private void fixVerticalAlignment(LayoutResult lr, Rectangle r) {548int delta = 0;549if (r.y < viewRect.y) {550delta = viewRect.y - r.y;551} else if (r.y + r.height > viewRect.y + viewRect.height) {552delta = viewRect.y + viewRect.height - r.y - r.height;553}554if (delta != 0) {555lr.checkRect.y += delta;556lr.iconRect.y += delta;557lr.textRect.y += delta;558lr.accRect.y += delta;559lr.arrowRect.y += delta;560lr.labelRect.y += delta;561}562}563564private void doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment) {565// Set maximal width for all the five basic rects566// (three other ones are already maximal)567lr.iconRect.width = iconSize.maxWidth;568lr.textRect.width = textSize.maxWidth;569570// Set X coordinates571// All rects will be aligned at the left side572calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,573lr.iconRect, lr.textRect);574575// Tune afterCheckIconGap576if (lr.checkRect.width > 0) { // there is the afterCheckIconGap577lr.iconRect.x += afterCheckIconGap - gap;578lr.textRect.x += afterCheckIconGap - gap;579}580581calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,582lr.arrowRect, lr.accRect);583584// Take into account minimal text offset585int textOffset = lr.textRect.x - viewRect.x;586if (!isTopLevelMenu && (textOffset < minTextOffset)) {587lr.textRect.x += minTextOffset - textOffset;588}589590alignRects(lr, alignment);591592// Set Y coordinate for text and icon.593// Y coordinates for other rects594// will be calculated later in layoutMenuItem.595calcTextAndIconYPositions(lr);596597// Calculate valid X and Y coordinates for labelRect598lr.setLabelRect(lr.textRect.union(lr.iconRect));599}600601private void doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment) {602lr.labelRect.width = labelSize.maxWidth;603604// Set X coordinates605calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,606lr.labelRect);607608// Tune afterCheckIconGap609if (lr.checkRect.width > 0) { // there is the afterCheckIconGap610lr.labelRect.x += afterCheckIconGap - gap;611}612613calcXPositionsRTL(viewRect.x + viewRect.width,614leadingGap, gap, lr.arrowRect, lr.accRect);615616// Take into account minimal text offset617int labelOffset = lr.labelRect.x - viewRect.x;618if (!isTopLevelMenu && (labelOffset < minTextOffset)) {619lr.labelRect.x += minTextOffset - labelOffset;620}621622alignRects(lr, alignment);623624// Center labelRect vertically625calcLabelYPosition(lr);626627layoutIconAndTextInLabelRect(lr);628}629630private void doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment) {631// Set maximal width for all the five basic rects632// (three other ones are already maximal)633lr.iconRect.width = iconSize.maxWidth;634lr.textRect.width = textSize.maxWidth;635636// Set X coordinates637calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,638lr.checkRect, lr.iconRect, lr.textRect);639640// Tune the gap after check icon641if (lr.checkRect.width > 0) { // there is the gap after check icon642lr.iconRect.x -= afterCheckIconGap - gap;643lr.textRect.x -= afterCheckIconGap - gap;644}645646calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect,647lr.accRect);648649// Take into account minimal text offset650int textOffset = (viewRect.x + viewRect.width)651- (lr.textRect.x + lr.textRect.width);652if (!isTopLevelMenu && (textOffset < minTextOffset)) {653lr.textRect.x -= minTextOffset - textOffset;654}655656alignRects(lr, alignment);657658// Set Y coordinates for text and icon.659// Y coordinates for other rects660// will be calculated later in layoutMenuItem.661calcTextAndIconYPositions(lr);662663// Calculate valid X and Y coordinate for labelRect664lr.setLabelRect(lr.textRect.union(lr.iconRect));665}666667private void doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment) {668lr.labelRect.width = labelSize.maxWidth;669670// Set X coordinates671calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,672lr.checkRect, lr.labelRect);673674// Tune the gap after check icon675if (lr.checkRect.width > 0) { // there is the gap after check icon676lr.labelRect.x -= afterCheckIconGap - gap;677}678679calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, lr.accRect);680681// Take into account minimal text offset682int labelOffset = (viewRect.x + viewRect.width)683- (lr.labelRect.x + lr.labelRect.width);684if (!isTopLevelMenu && (labelOffset < minTextOffset)) {685lr.labelRect.x -= minTextOffset - labelOffset;686}687688alignRects(lr, alignment);689690// Center labelRect vertically691calcLabelYPosition(lr);692693layoutIconAndTextInLabelRect(lr);694}695696private void alignRects(LayoutResult lr, ColumnAlignment alignment) {697alignRect(lr.checkRect, alignment.getCheckAlignment(),698checkSize.getOrigWidth());699alignRect(lr.iconRect, alignment.getIconAlignment(),700iconSize.getOrigWidth());701alignRect(lr.textRect, alignment.getTextAlignment(),702textSize.getOrigWidth());703alignRect(lr.accRect, alignment.getAccAlignment(),704accSize.getOrigWidth());705alignRect(lr.arrowRect, alignment.getArrowAlignment(),706arrowSize.getOrigWidth());707}708709private void alignRect(Rectangle rect, int alignment, int origWidth) {710if (alignment == SwingConstants.RIGHT) {711rect.x = rect.x + rect.width - origWidth;712}713rect.width = origWidth;714}715716protected void layoutIconAndTextInLabelRect(LayoutResult lr) {717lr.setTextRect(new Rectangle());718lr.setIconRect(new Rectangle());719SwingUtilities.layoutCompoundLabel(720mi, fm, text,icon, verticalAlignment, horizontalAlignment,721verticalTextPosition, horizontalTextPosition, lr.labelRect,722lr.iconRect, lr.textRect, gap);723}724725private void calcXPositionsLTR(int startXPos, int leadingGap,726int gap, Rectangle... rects) {727int curXPos = startXPos + leadingGap;728for (Rectangle rect : rects) {729rect.x = curXPos;730if (rect.width > 0) {731curXPos += rect.width + gap;732}733}734}735736private void calcXPositionsRTL(int startXPos, int leadingGap,737int gap, Rectangle... rects) {738int curXPos = startXPos - leadingGap;739for (Rectangle rect : rects) {740rect.x = curXPos - rect.width;741if (rect.width > 0) {742curXPos -= rect.width + gap;743}744}745}746747/**748* Sets Y coordinates of text and icon749* taking into account the vertical alignment750*/751private void calcTextAndIconYPositions(LayoutResult lr) {752if (verticalAlignment == SwingUtilities.TOP) {753lr.textRect.y = (int)(viewRect.y754+ (float)lr.labelRect.height/2755- (float)lr.textRect.height/2);756lr.iconRect.y = (int)(viewRect.y757+ (float)lr.labelRect.height/2758- (float)lr.iconRect.height/2);759} else if (verticalAlignment == SwingUtilities.CENTER) {760lr.textRect.y = (int)(viewRect.y761+ (float)viewRect.height/2762- (float)lr.textRect.height/2);763lr.iconRect.y = (int)(viewRect.y764+ (float)viewRect.height/2765- (float)lr.iconRect.height/2);766}767else if (verticalAlignment == SwingUtilities.BOTTOM) {768lr.textRect.y = (int)(viewRect.y769+ viewRect.height770- (float)lr.labelRect.height/2771- (float)lr.textRect.height/2);772lr.iconRect.y = (int)(viewRect.y773+ viewRect.height774- (float)lr.labelRect.height/2775- (float)lr.iconRect.height/2);776}777}778779/**780* Sets labelRect Y coordinate781* taking into account the vertical alignment782*/783private void calcLabelYPosition(LayoutResult lr) {784if (verticalAlignment == SwingUtilities.TOP) {785lr.labelRect.y = viewRect.y;786} else if (verticalAlignment == SwingUtilities.CENTER) {787lr.labelRect.y = (int)(viewRect.y788+ (float)viewRect.height/2789- (float)lr.labelRect.height/2);790} else if (verticalAlignment == SwingUtilities.BOTTOM) {791lr.labelRect.y = viewRect.y + viewRect.height792- lr.labelRect.height;793}794}795796/**797* Returns parent of this component if it is not a top-level menu798* Otherwise returns null.799* @param menuItem the menu item whose parent will be returned.800* @return parent of this component if it is not a top-level menu801* Otherwise returns null.802*/803public static JComponent getMenuItemParent(JMenuItem menuItem) {804Container parent = menuItem.getParent();805if ((parent instanceof JComponent) &&806(!(menuItem instanceof JMenu) ||807!((JMenu)menuItem).isTopLevelMenu())) {808return (JComponent) parent;809} else {810return null;811}812}813814public static void clearUsedParentClientProperties(JMenuItem menuItem) {815clearUsedClientProperties(getMenuItemParent(menuItem));816}817818public static void clearUsedClientProperties(JComponent c) {819if (c != null) {820c.putClientProperty(MAX_ARROW_WIDTH, null);821c.putClientProperty(MAX_CHECK_WIDTH, null);822c.putClientProperty(MAX_ACC_WIDTH, null);823c.putClientProperty(MAX_TEXT_WIDTH, null);824c.putClientProperty(MAX_ICON_WIDTH, null);825c.putClientProperty(MAX_LABEL_WIDTH, null);826c.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null);827}828}829830/**831* Finds and returns maximal integer value in the given array.832* @param values array where the search will be performed.833* @return maximal vaule.834*/835public static int max(int... values) {836int maxValue = Integer.MIN_VALUE;837for (int i : values) {838if (i > maxValue) {839maxValue = i;840}841}842return maxValue;843}844845public static Rectangle createMaxRect() {846return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);847}848849public static void addMaxWidth(RectSize size, int gap, Dimension result) {850if (size.maxWidth > 0) {851result.width += size.maxWidth + gap;852}853}854855public static void addWidth(int width, int gap, Dimension result) {856if (width > 0) {857result.width += width + gap;858}859}860861public JMenuItem getMenuItem() {862return mi;863}864865public JComponent getMenuItemParent() {866return miParent;867}868869public Font getFont() {870return font;871}872873public Font getAccFont() {874return accFont;875}876877public FontMetrics getFontMetrics() {878return fm;879}880881public FontMetrics getAccFontMetrics() {882return accFm;883}884885public Icon getIcon() {886return icon;887}888889public Icon getCheckIcon() {890return checkIcon;891}892893public Icon getArrowIcon() {894return arrowIcon;895}896897public String getText() {898return text;899}900901public String getAccText() {902return accText;903}904905public boolean isColumnLayout() {906return isColumnLayout;907}908909public boolean useCheckAndArrow() {910return useCheckAndArrow;911}912913public boolean isLeftToRight() {914return isLeftToRight;915}916917public boolean isTopLevelMenu() {918return isTopLevelMenu;919}920921public View getHtmlView() {922return htmlView;923}924925public int getVerticalAlignment() {926return verticalAlignment;927}928929public int getHorizontalAlignment() {930return horizontalAlignment;931}932933public int getVerticalTextPosition() {934return verticalTextPosition;935}936937public int getHorizontalTextPosition() {938return horizontalTextPosition;939}940941public int getGap() {942return gap;943}944945public int getLeadingGap() {946return leadingGap;947}948949public int getAfterCheckIconGap() {950return afterCheckIconGap;951}952953public int getMinTextOffset() {954return minTextOffset;955}956957public Rectangle getViewRect() {958return viewRect;959}960961public RectSize getIconSize() {962return iconSize;963}964965public RectSize getTextSize() {966return textSize;967}968969public RectSize getAccSize() {970return accSize;971}972973public RectSize getCheckSize() {974return checkSize;975}976977public RectSize getArrowSize() {978return arrowSize;979}980981public RectSize getLabelSize() {982return labelSize;983}984985protected void setMenuItem(JMenuItem mi) {986this.mi = mi;987}988989protected void setMenuItemParent(JComponent miParent) {990this.miParent = miParent;991}992993protected void setFont(Font font) {994this.font = font;995}996997protected void setAccFont(Font accFont) {998this.accFont = accFont;999}10001001protected void setFontMetrics(FontMetrics fm) {1002this.fm = fm;1003}10041005protected void setAccFontMetrics(FontMetrics accFm) {1006this.accFm = accFm;1007}10081009protected void setIcon(Icon icon) {1010this.icon = icon;1011}10121013protected void setCheckIcon(Icon checkIcon) {1014this.checkIcon = checkIcon;1015}10161017protected void setArrowIcon(Icon arrowIcon) {1018this.arrowIcon = arrowIcon;1019}10201021protected void setText(String text) {1022this.text = text;1023}10241025protected void setAccText(String accText) {1026this.accText = accText;1027}10281029protected void setColumnLayout(boolean columnLayout) {1030isColumnLayout = columnLayout;1031}10321033protected void setUseCheckAndArrow(boolean useCheckAndArrow) {1034this.useCheckAndArrow = useCheckAndArrow;1035}10361037protected void setLeftToRight(boolean leftToRight) {1038isLeftToRight = leftToRight;1039}10401041protected void setTopLevelMenu(boolean topLevelMenu) {1042isTopLevelMenu = topLevelMenu;1043}10441045protected void setHtmlView(View htmlView) {1046this.htmlView = htmlView;1047}10481049protected void setVerticalAlignment(int verticalAlignment) {1050this.verticalAlignment = verticalAlignment;1051}10521053protected void setHorizontalAlignment(int horizontalAlignment) {1054this.horizontalAlignment = horizontalAlignment;1055}10561057protected void setVerticalTextPosition(int verticalTextPosition) {1058this.verticalTextPosition = verticalTextPosition;1059}10601061protected void setHorizontalTextPosition(int horizontalTextPosition) {1062this.horizontalTextPosition = horizontalTextPosition;1063}10641065protected void setGap(int gap) {1066this.gap = gap;1067}10681069protected void setLeadingGap(int leadingGap) {1070this.leadingGap = leadingGap;1071}10721073protected void setAfterCheckIconGap(int afterCheckIconGap) {1074this.afterCheckIconGap = afterCheckIconGap;1075}10761077protected void setMinTextOffset(int minTextOffset) {1078this.minTextOffset = minTextOffset;1079}10801081protected void setViewRect(Rectangle viewRect) {1082this.viewRect = viewRect;1083}10841085protected void setIconSize(RectSize iconSize) {1086this.iconSize = iconSize;1087}10881089protected void setTextSize(RectSize textSize) {1090this.textSize = textSize;1091}10921093protected void setAccSize(RectSize accSize) {1094this.accSize = accSize;1095}10961097protected void setCheckSize(RectSize checkSize) {1098this.checkSize = checkSize;1099}11001101protected void setArrowSize(RectSize arrowSize) {1102this.arrowSize = arrowSize;1103}11041105protected void setLabelSize(RectSize labelSize) {1106this.labelSize = labelSize;1107}11081109public int getLeftTextExtraWidth() {1110return leftTextExtraWidth;1111}11121113/**1114* Returns false if the component is a JMenu and it is a top1115* level menu (on the menubar).1116*/1117public static boolean useCheckAndArrow(JMenuItem menuItem) {1118boolean b = true;1119if ((menuItem instanceof JMenu) &&1120(((JMenu) menuItem).isTopLevelMenu())) {1121b = false;1122}1123return b;1124}11251126public static class LayoutResult {1127private Rectangle iconRect;1128private Rectangle textRect;1129private Rectangle accRect;1130private Rectangle checkRect;1131private Rectangle arrowRect;1132private Rectangle labelRect;11331134public LayoutResult() {1135iconRect = new Rectangle();1136textRect = new Rectangle();1137accRect = new Rectangle();1138checkRect = new Rectangle();1139arrowRect = new Rectangle();1140labelRect = new Rectangle();1141}11421143public LayoutResult(Rectangle iconRect, Rectangle textRect,1144Rectangle accRect, Rectangle checkRect,1145Rectangle arrowRect, Rectangle labelRect) {1146this.iconRect = iconRect;1147this.textRect = textRect;1148this.accRect = accRect;1149this.checkRect = checkRect;1150this.arrowRect = arrowRect;1151this.labelRect = labelRect;1152}11531154public Rectangle getIconRect() {1155return iconRect;1156}11571158public void setIconRect(Rectangle iconRect) {1159this.iconRect = iconRect;1160}11611162public Rectangle getTextRect() {1163return textRect;1164}11651166public void setTextRect(Rectangle textRect) {1167this.textRect = textRect;1168}11691170public Rectangle getAccRect() {1171return accRect;1172}11731174public void setAccRect(Rectangle accRect) {1175this.accRect = accRect;1176}11771178public Rectangle getCheckRect() {1179return checkRect;1180}11811182public void setCheckRect(Rectangle checkRect) {1183this.checkRect = checkRect;1184}11851186public Rectangle getArrowRect() {1187return arrowRect;1188}11891190public void setArrowRect(Rectangle arrowRect) {1191this.arrowRect = arrowRect;1192}11931194public Rectangle getLabelRect() {1195return labelRect;1196}11971198public void setLabelRect(Rectangle labelRect) {1199this.labelRect = labelRect;1200}12011202public Map<String, Rectangle> getAllRects() {1203Map<String, Rectangle> result = new HashMap<String, Rectangle>();1204result.put("checkRect", checkRect);1205result.put("iconRect", iconRect);1206result.put("textRect", textRect);1207result.put("accRect", accRect);1208result.put("arrowRect", arrowRect);1209result.put("labelRect", labelRect);1210return result;1211}1212}12131214public static class ColumnAlignment {1215private int checkAlignment;1216private int iconAlignment;1217private int textAlignment;1218private int accAlignment;1219private int arrowAlignment;12201221public static final ColumnAlignment LEFT_ALIGNMENT =1222new ColumnAlignment(1223SwingConstants.LEFT,1224SwingConstants.LEFT,1225SwingConstants.LEFT,1226SwingConstants.LEFT,1227SwingConstants.LEFT1228);12291230public static final ColumnAlignment RIGHT_ALIGNMENT =1231new ColumnAlignment(1232SwingConstants.RIGHT,1233SwingConstants.RIGHT,1234SwingConstants.RIGHT,1235SwingConstants.RIGHT,1236SwingConstants.RIGHT1237);12381239public ColumnAlignment(int checkAlignment, int iconAlignment,1240int textAlignment, int accAlignment,1241int arrowAlignment) {1242this.checkAlignment = checkAlignment;1243this.iconAlignment = iconAlignment;1244this.textAlignment = textAlignment;1245this.accAlignment = accAlignment;1246this.arrowAlignment = arrowAlignment;1247}12481249public int getCheckAlignment() {1250return checkAlignment;1251}12521253public int getIconAlignment() {1254return iconAlignment;1255}12561257public int getTextAlignment() {1258return textAlignment;1259}12601261public int getAccAlignment() {1262return accAlignment;1263}12641265public int getArrowAlignment() {1266return arrowAlignment;1267}1268}12691270public static class RectSize {1271private int width;1272private int height;1273private int origWidth;1274private int maxWidth;12751276public RectSize() {1277}12781279public RectSize(int width, int height, int origWidth, int maxWidth) {1280this.width = width;1281this.height = height;1282this.origWidth = origWidth;1283this.maxWidth = maxWidth;1284}12851286public int getWidth() {1287return width;1288}12891290public int getHeight() {1291return height;1292}12931294public int getOrigWidth() {1295return origWidth;1296}12971298public int getMaxWidth() {1299return maxWidth;1300}13011302public void setWidth(int width) {1303this.width = width;1304}13051306public void setHeight(int height) {1307this.height = height;1308}13091310public void setOrigWidth(int origWidth) {1311this.origWidth = origWidth;1312}13131314public void setMaxWidth(int maxWidth) {1315this.maxWidth = maxWidth;1316}13171318public String toString() {1319return "[w=" + width + ",h=" + height + ",ow="1320+ origWidth + ",mw=" + maxWidth + "]";1321}1322}1323}132413251326