提交 e9fb2320 编写于 作者: M mlapshin

6584657: GTK Look and Feel: Bugs in menu item layout

Reviewed-by: peterz, alexp
上级 12b796c2
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -974,6 +974,7 @@ public class SwingUtilities implements SwingConstants ...@@ -974,6 +974,7 @@ public class SwingUtilities implements SwingConstants
boolean textIsEmpty = (text == null) || text.equals(""); boolean textIsEmpty = (text == null) || text.equals("");
int lsb = 0; int lsb = 0;
int rsb = 0;
/* Unless both text and icon are non-null, we effectively ignore /* Unless both text and icon are non-null, we effectively ignore
* the value of textIconGap. * the value of textIconGap.
*/ */
...@@ -1015,7 +1016,7 @@ public class SwingUtilities implements SwingConstants ...@@ -1015,7 +1016,7 @@ public class SwingUtilities implements SwingConstants
if (lsb < 0) { if (lsb < 0) {
textR.width -= lsb; textR.width -= lsb;
} }
int rsb = SwingUtilities2.getRightSideBearing(c, fm, text); rsb = SwingUtilities2.getRightSideBearing(c, fm, text);
if (rsb > 0) { if (rsb > 0) {
textR.width += rsb; textR.width += rsb;
} }
...@@ -1118,6 +1119,11 @@ public class SwingUtilities implements SwingConstants ...@@ -1118,6 +1119,11 @@ public class SwingUtilities implements SwingConstants
// lsb is negative. Shift the x location so that the text is // lsb is negative. Shift the x location so that the text is
// visually drawn at the right location. // visually drawn at the right location.
textR.x -= lsb; textR.x -= lsb;
textR.width += lsb;
}
if (rsb > 0) {
textR.width -= rsb;
} }
return text; return text;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,9 +25,6 @@ ...@@ -25,9 +25,6 @@
package javax.swing.plaf.basic; 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.*;
import java.awt.event.*; import java.awt.event.*;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
...@@ -39,8 +36,7 @@ import javax.swing.border.*; ...@@ -39,8 +36,7 @@ import javax.swing.border.*;
import javax.swing.plaf.*; import javax.swing.plaf.*;
import javax.swing.text.View; import javax.swing.text.View;
import sun.swing.UIAction; import sun.swing.*;
import sun.swing.StringUIClientPropertyKey;
/** /**
* BasicMenuItem implementation * BasicMenuItem implementation
...@@ -91,24 +87,6 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -91,24 +87,6 @@ public class BasicMenuItemUI extends MenuItemUI
private static final boolean VERBOSE = false; // show reuse hits/misses private static final boolean VERBOSE = false; // show reuse hits/misses
private static final boolean DEBUG = false; // show bad params, misc. 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) { static void loadActionMap(LazyActionMap map) {
// NOTE: BasicMenuUI also calls into this method. // NOTE: BasicMenuUI also calls into this method.
map.put(new Actions(Actions.CLICK)); map.put(new Actions(Actions.CLICK));
...@@ -199,13 +177,14 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -199,13 +177,14 @@ public class BasicMenuItemUI extends MenuItemUI
//In case of column layout, .checkIconFactory is defined for this UI, //In case of column layout, .checkIconFactory is defined for this UI,
//the icon is compatible with it and useCheckAndArrow() is true, //the icon is compatible with it and useCheckAndArrow() is true,
//then the icon is handled by the checkIcon. //then the icon is handled by the checkIcon.
boolean isColumnLayout = LayoutInfo.isColumnLayout( boolean isColumnLayout = MenuItemLayoutHelper.isColumnLayout(
BasicGraphicsUtils.isLeftToRight(menuItem), menuItem); BasicGraphicsUtils.isLeftToRight(menuItem), menuItem);
if (isColumnLayout) { if (isColumnLayout) {
MenuItemCheckIconFactory iconFactory = MenuItemCheckIconFactory iconFactory =
(MenuItemCheckIconFactory) UIManager.get(prefix (MenuItemCheckIconFactory) UIManager.get(prefix
+ ".checkIconFactory"); + ".checkIconFactory");
if (iconFactory != null && useCheckAndArrow() if (iconFactory != null
&& MenuItemLayoutHelper.useCheckAndArrow(menuItem)
&& iconFactory.isCompatible(checkIcon, prefix)) { && iconFactory.isCompatible(checkIcon, prefix)) {
checkIcon = iconFactory.getIcon(menuItem); checkIcon = iconFactory.getIcon(menuItem);
} }
...@@ -256,20 +235,7 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -256,20 +235,7 @@ public class BasicMenuItemUI extends MenuItemUI
uninstallComponents(menuItem); uninstallComponents(menuItem);
uninstallListeners(); uninstallListeners();
uninstallKeyboardActions(); uninstallKeyboardActions();
MenuItemLayoutHelper.clearUsedParentClientProperties(menuItem);
// 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 );
}
menuItem = null; menuItem = null;
} }
...@@ -405,19 +371,6 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -405,19 +371,6 @@ public class BasicMenuItemUI extends MenuItemUI
return d; 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, protected Dimension getPreferredMenuItemSize(JComponent c,
Icon checkIcon, Icon checkIcon,
Icon arrowIcon, Icon arrowIcon,
...@@ -447,32 +400,36 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -447,32 +400,36 @@ public class BasicMenuItemUI extends MenuItemUI
// the icon and text when user points a menu item by mouse. // the icon and text when user points a menu item by mouse.
JMenuItem mi = (JMenuItem) c; JMenuItem mi = (JMenuItem) c;
LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon, MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
createMaxViewRect(), defaultTextIconGap, acceleratorDelimiter, arrowIcon, MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap,
BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont, acceleratorDelimiter, BasicGraphicsUtils.isLeftToRight(mi),
useCheckAndArrow(), getPropertyPrefix()); mi.getFont(), acceleratorFont,
MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
Dimension result = new Dimension(); Dimension result = new Dimension();
// Calculate the result width // Calculate the result width
result.width = li.leadingGap; result.width = lh.getLeadingGap();
addWidth(li.maxCheckWidth, li.afterCheckIconGap, result); MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(),
lh.getAfterCheckIconGap(), result);
// Take into account mimimal text offset. // Take into account mimimal text offset.
if ((!li.isTopLevelMenu) if ((!lh.isTopLevelMenu())
&& (li.minTextOffset > 0) && (lh.getMinTextOffset() > 0)
&& (result.width < li.minTextOffset)) { && (result.width < lh.getMinTextOffset())) {
result.width = li.minTextOffset; result.width = lh.getMinTextOffset();
} }
addWidth(li.maxLabelWidth, li.gap, result); MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), lh.getGap(), result);
addWidth(li.maxAccWidth, li.gap, result); MenuItemLayoutHelper.addMaxWidth(lh.getAccSize(), lh.getGap(), result);
addWidth(li.maxArrowWidth, li.gap, result); MenuItemLayoutHelper.addMaxWidth(lh.getArrowSize(), lh.getGap(), result);
// Calculate the result height // Calculate the result height
result.height = max(li.checkRect.height, li.labelRect.height, result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(),
li.accRect.height, li.arrowRect.height); lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(),
lh.getArrowSize().getHeight());
// Take into account menu item insets // Take into account menu item insets
Insets insets = li.mi.getInsets(); Insets insets = lh.getMenuItem().getInsets();
if(insets != null) { if(insets != null) {
result.width += insets.left + insets.right; result.width += insets.left + insets.right;
result.height += insets.top + insets.bottom; result.height += insets.top + insets.bottom;
...@@ -492,500 +449,9 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -492,500 +449,9 @@ public class BasicMenuItemUI extends MenuItemUI
result.height++; result.height++;
} }
li.clear();
return result; 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() * We draw the background in paintMenuItem()
* so override update (which fills the background of opaque * so override update (which fills the background of opaque
...@@ -1016,122 +482,132 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -1016,122 +482,132 @@ public class BasicMenuItemUI extends MenuItemUI
Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
applyInsets(viewRect, mi.getInsets()); applyInsets(viewRect, mi.getInsets());
LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon, MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
viewRect, defaultTextIconGap, acceleratorDelimiter, arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter,
BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont, BasicGraphicsUtils.isLeftToRight(mi), mi.getFont(),
useCheckAndArrow(), getPropertyPrefix()); acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem),
layoutMenuItem(li); getPropertyPrefix());
MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
paintBackground(g, mi, background); paintBackground(g, mi, background);
paintCheckIcon(g, li, holdc, foreground); paintCheckIcon(g, lh, lr, holdc, foreground);
paintIcon(g, li, holdc); paintIcon(g, lh, lr, holdc);
paintText(g, li); paintText(g, lh, lr);
paintAccText(g, li); paintAccText(g, lh, lr);
paintArrowIcon(g, li, foreground); paintArrowIcon(g, lh, lr, foreground);
// Restore original graphics font and color // Restore original graphics font and color
g.setColor(holdc); g.setColor(holdc);
g.setFont(holdf); g.setFont(holdf);
li.clear();
} }
private void paintIcon(Graphics g, LayoutInfo li, Color holdc) { private void paintIcon(Graphics g, MenuItemLayoutHelper lh,
if (li.icon != null) { MenuItemLayoutHelper.LayoutResult lr, Color holdc) {
if (lh.getIcon() != null) {
Icon icon; Icon icon;
ButtonModel model = li.mi.getModel(); ButtonModel model = lh.getMenuItem().getModel();
if (!model.isEnabled()) { if (!model.isEnabled()) {
icon = li.mi.getDisabledIcon(); icon = lh.getMenuItem().getDisabledIcon();
} else if (model.isPressed() && model.isArmed()) { } else if (model.isPressed() && model.isArmed()) {
icon = li.mi.getPressedIcon(); icon = lh.getMenuItem().getPressedIcon();
if (icon == null) { if (icon == null) {
// Use default icon // Use default icon
icon = li.mi.getIcon(); icon = lh.getMenuItem().getIcon();
} }
} else { } else {
icon = li.mi.getIcon(); icon = lh.getMenuItem().getIcon();
} }
if (icon != null) { 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); g.setColor(holdc);
} }
} }
} }
private void paintCheckIcon(Graphics g, LayoutInfo li, private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh,
MenuItemLayoutHelper.LayoutResult lr,
Color holdc, Color foreground) { Color holdc, Color foreground) {
if (li.checkIcon != null) { if (lh.getCheckIcon() != null) {
ButtonModel model = li.mi.getModel(); ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
|| (li.mi instanceof JMenu && model.isSelected())) { && model.isSelected())) {
g.setColor(foreground); g.setColor(foreground);
} else { } else {
g.setColor(holdc); g.setColor(holdc);
} }
if (li.useCheckAndArrow) { if (lh.useCheckAndArrow()) {
li.checkIcon.paintIcon(li.mi, g, li.checkRect.x, lh.getCheckIcon().paintIcon(lh.getMenuItem(), g,
li.checkRect.y); lr.getCheckRect().x, lr.getCheckRect().y);
} }
g.setColor(holdc); g.setColor(holdc);
} }
} }
private void paintAccText(Graphics g, LayoutInfo li) { private void paintAccText(Graphics g, MenuItemLayoutHelper lh,
if (!li.accText.equals("")) { MenuItemLayoutHelper.LayoutResult lr) {
ButtonModel model = li.mi.getModel(); if (!lh.getAccText().equals("")) {
g.setFont(acceleratorFont); ButtonModel model = lh.getMenuItem().getModel();
g.setFont(lh.getAccFontMetrics().getFont());
if (!model.isEnabled()) { if (!model.isEnabled()) {
// *** paint the accText disabled // *** paint the accText disabled
if (disabledForeground != null) { if (disabledForeground != null) {
g.setColor(disabledForeground); g.setColor(disabledForeground);
SwingUtilities2.drawString(li.mi, g, li.accText, SwingUtilities2.drawString(lh.getMenuItem(), g,
li.accRect.x, lh.getAccText(), lr.getAccRect().x,
li.accRect.y + li.accFm.getAscent()); lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
} else { } else {
g.setColor(li.mi.getBackground().brighter()); g.setColor(lh.getMenuItem().getBackground().brighter());
SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x, SwingUtilities2.drawString(lh.getMenuItem(), g,
li.accRect.y + li.accFm.getAscent()); lh.getAccText(), lr.getAccRect().x,
g.setColor(li.mi.getBackground().darker()); lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
SwingUtilities2.drawString(li.mi, g, li.accText, g.setColor(lh.getMenuItem().getBackground().darker());
li.accRect.x - 1, SwingUtilities2.drawString(lh.getMenuItem(), g,
li.accRect.y + li.accFm.getAscent() - 1); lh.getAccText(), lr.getAccRect().x - 1,
lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1);
} }
} else { } else {
// *** paint the accText normally // *** paint the accText normally
if (model.isArmed() || if (model.isArmed()
(li.mi instanceof JMenu && model.isSelected())) { || (lh.getMenuItem() instanceof JMenu
&& model.isSelected())) {
g.setColor(acceleratorSelectionForeground); g.setColor(acceleratorSelectionForeground);
} else { } else {
g.setColor(acceleratorForeground); g.setColor(acceleratorForeground);
} }
SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x, SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(),
li.accRect.y + li.accFm.getAscent()); lr.getAccRect().x, lr.getAccRect().y +
lh.getAccFontMetrics().getAscent());
} }
} }
} }
private void paintText(Graphics g, LayoutInfo li) { private void paintText(Graphics g, MenuItemLayoutHelper lh,
if (!li.text.equals("")) { MenuItemLayoutHelper.LayoutResult lr) {
if (li.htmlView != null) { if (!lh.getText().equals("")) {
if (lh.getHtmlView() != null) {
// Text is HTML // Text is HTML
li.htmlView.paint(g, li.textRect); lh.getHtmlView().paint(g, lr.getTextRect());
} else { } else {
// Text isn't HTML // 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) { private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh,
if (li.arrowIcon != null) { MenuItemLayoutHelper.LayoutResult lr,
ButtonModel model = li.mi.getModel(); Color foreground) {
if (model.isArmed() if (lh.getArrowIcon() != null) {
|| (li.mi instanceof JMenu && model.isSelected())) { ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
&& model.isSelected())) {
g.setColor(foreground); g.setColor(foreground);
} }
if (li.useCheckAndArrow) { if (lh.useCheckAndArrow()) {
li.arrowIcon.paintIcon(li.mi, g, li.arrowRect.x, li.arrowRect.y); lh.getArrowIcon().paintIcon(lh.getMenuItem(), g,
lr.getArrowRect().x, lr.getArrowRect().y);
} }
} }
} }
...@@ -1216,346 +692,6 @@ public class BasicMenuItemUI extends MenuItemUI ...@@ -1216,346 +692,6 @@ public class BasicMenuItemUI extends MenuItemUI
} }
} }
/**
* 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() { public MenuElement[] getPath() {
MenuSelectionManager m = MenuSelectionManager.defaultManager(); MenuSelectionManager m = MenuSelectionManager.defaultManager();
MenuElement oldPath[] = m.getSelectedPath(); MenuElement oldPath[] = m.getSelectedPath();
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,7 +30,6 @@ import javax.swing.plaf.UIResource; ...@@ -30,7 +30,6 @@ import javax.swing.plaf.UIResource;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
/** /**
* The default layout manager for Popup menus and menubars. This * The default layout manager for Popup menus and menubars. This
...@@ -49,18 +48,7 @@ public class DefaultMenuLayout extends BoxLayout implements UIResource { ...@@ -49,18 +48,7 @@ public class DefaultMenuLayout extends BoxLayout implements UIResource {
public Dimension preferredLayoutSize(Container target) { public Dimension preferredLayoutSize(Container target) {
if (target instanceof JPopupMenu) { if (target instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu) target; JPopupMenu popupMenu = (JPopupMenu) target;
sun.swing.MenuItemLayoutHelper.clearUsedClientProperties(popupMenu);
// 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);
if (popupMenu.getComponentCount() == 0) { if (popupMenu.getComponentCount() == 0) {
return new Dimension(0, 0); return new Dimension(0, 0);
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -47,19 +47,22 @@ class DefaultMenuLayout extends BoxLayout implements UIResource { ...@@ -47,19 +47,22 @@ class DefaultMenuLayout extends BoxLayout implements UIResource {
super(target, axis); super(target, axis);
} }
public void invalidateLayout(Container target) { public Dimension preferredLayoutSize(Container target) {
if (target instanceof JPopupMenu) { if (target instanceof JPopupMenu) {
SynthPopupMenuUI popupUI = (SynthPopupMenuUI)((JPopupMenu)target). JPopupMenu popupMenu = (JPopupMenu) target;
getUI();
popupUI.resetAlignmentHints(); 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); super.invalidateLayout(target);
}
public Dimension preferredLayoutSize(Container target) {
if (target instanceof JPopupMenu && target.getComponentCount() == 0) {
return new Dimension(0, 0);
}
return super.preferredLayoutSize(target); return super.preferredLayoutSize(target);
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
package javax.swing.plaf.synth; package javax.swing.plaf.synth;
import sun.swing.SwingUtilities2; import sun.swing.SwingUtilities2;
import sun.swing.MenuItemLayoutHelper;
import java.awt.*; import java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
...@@ -411,6 +413,198 @@ public class SynthGraphicsUtils { ...@@ -411,6 +413,198 @@ public class SynthGraphicsUtils {
} }
/**
* 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 * Wraps a SynthIcon around the Icon interface, forwarding calls to
* the SynthIcon with a given SynthContext. * the SynthIcon with a given SynthContext.
......
/*
* 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());
}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -37,7 +37,7 @@ import javax.swing.plaf.*; ...@@ -37,7 +37,7 @@ import javax.swing.plaf.*;
import javax.swing.plaf.basic.*; import javax.swing.plaf.basic.*;
import javax.swing.text.View; import javax.swing.text.View;
import sun.swing.plaf.synth.*; import sun.swing.plaf.synth.*;
import sun.swing.SwingUtilities2; import sun.swing.MenuItemLayoutHelper;
/** /**
...@@ -59,542 +59,16 @@ class SynthMenuItemUI extends BasicMenuItemUI implements ...@@ -59,542 +59,16 @@ class SynthMenuItemUI extends BasicMenuItemUI implements
return new SynthMenuItemUI(); return new SynthMenuItemUI();
} }
// public void uninstallUI(JComponent c) {
// The next handful of static methods are used by both SynthMenuUI super.uninstallUI(c);
// and SynthMenuItemUI. This is necessitated by SynthMenuUI not // Remove values from the parent's Client Properties.
// extending SynthMenuItemUI. JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c);
// if (p != null) {
p.putClientProperty(
/* SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null);
* 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);
}
}
/**
* 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() { protected void installDefaults() {
updateStyle(menuItem); updateStyle(menuItem);
} }
...@@ -718,9 +192,11 @@ class SynthMenuItemUI extends BasicMenuItemUI implements ...@@ -718,9 +192,11 @@ class SynthMenuItemUI extends BasicMenuItemUI implements
int defaultTextIconGap) { int defaultTextIconGap) {
SynthContext context = getContext(c); SynthContext context = getContext(c);
SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR); SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
Dimension value = getPreferredMenuItemSize(context, accContext, Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize(
c, checkIcon, arrowIcon, defaultTextIconGap, context, accContext, c, checkIcon, arrowIcon,
acceleratorDelimiter); defaultTextIconGap, acceleratorDelimiter,
MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
context.dispose(); context.dispose();
accContext.dispose(); accContext.dispose();
return value; return value;
...@@ -751,14 +227,13 @@ class SynthMenuItemUI extends BasicMenuItemUI implements ...@@ -751,14 +227,13 @@ class SynthMenuItemUI extends BasicMenuItemUI implements
String prefix = getPropertyPrefix(); String prefix = getPropertyPrefix();
Icon checkIcon = style.getIcon(context, prefix + ".checkIcon"); Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
paint(context, accContext, g, checkIcon, arrowIcon, SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon,
acceleratorDelimiter, defaultTextIconGap); acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix());
accContext.dispose(); accContext.dispose();
} }
void paintBackground(SynthContext context, Graphics g, JComponent c) { void paintBackground(SynthContext context, Graphics g, JComponent c) {
context.getPainter().paintMenuItemBackground(context, g, 0, 0, SynthGraphicsUtils.paintBackground(context, g, c);
c.getWidth(), c.getHeight());
} }
public void paintBorder(SynthContext context, Graphics g, int x, public void paintBorder(SynthContext context, Graphics g, int x,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -35,7 +35,7 @@ import javax.swing.border.*; ...@@ -35,7 +35,7 @@ import javax.swing.border.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import sun.swing.plaf.synth.SynthUI; import sun.swing.plaf.synth.SynthUI;
import sun.swing.MenuItemLayoutHelper;
/** /**
* Synth's MenuUI. * Synth's MenuUI.
...@@ -86,7 +86,7 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, ...@@ -86,7 +86,7 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener,
acceleratorDelimiter = style.getString(context, prefix + acceleratorDelimiter = style.getString(context, prefix +
".acceleratorDelimiter", "+"); ".acceleratorDelimiter", "+");
if (useCheckAndArrow()) { if (MenuItemLayoutHelper.useCheckAndArrow(menuItem)) {
checkIcon = style.getIcon(context, prefix + ".checkIcon"); checkIcon = style.getIcon(context, prefix + ".checkIcon");
arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
} else { } else {
...@@ -111,6 +111,16 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, ...@@ -111,6 +111,16 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener,
accContext.dispose(); 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() { protected void uninstallDefaults() {
SynthContext context = getContext(menuItem, ENABLED); SynthContext context = getContext(menuItem, ENABLED);
style.uninstallDefaults(context); style.uninstallDefaults(context);
...@@ -182,9 +192,11 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, ...@@ -182,9 +192,11 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener,
int defaultTextIconGap) { int defaultTextIconGap) {
SynthContext context = getContext(c); SynthContext context = getContext(c);
SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR); SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
Dimension value = SynthMenuItemUI.getPreferredMenuItemSize( Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize(
context, accContext, c, checkIcon, arrowIcon, context, accContext, c, checkIcon, arrowIcon,
defaultTextIconGap, acceleratorDelimiter); defaultTextIconGap, acceleratorDelimiter,
MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
context.dispose(); context.dispose();
accContext.dispose(); accContext.dispose();
return value; return value;
...@@ -211,21 +223,12 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, ...@@ -211,21 +223,12 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener,
protected void paint(SynthContext context, Graphics g) { protected void paint(SynthContext context, Graphics g) {
SynthContext accContext = getContext(menuItem, SynthContext accContext = getContext(menuItem,
Region.MENU_ITEM_ACCELERATOR); Region.MENU_ITEM_ACCELERATOR);
SynthStyle style = context.getStyle(); // Refetch the appropriate check indicator for the current state
Icon checkIcon; String prefix = getPropertyPrefix();
Icon arrowIcon; Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
if (useCheckAndArrow()) { Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
// Refetch the appropriate icons for the current state SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon,
String prefix = getPropertyPrefix(); acceleratorDelimiter, defaultTextIconGap, 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);
accContext.dispose(); accContext.dispose();
} }
...@@ -239,8 +242,4 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, ...@@ -239,8 +242,4 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener,
updateStyle((JMenu)e.getSource()); updateStyle((JMenu)e.getSource());
} }
} }
private boolean useCheckAndArrow() {
return !((JMenu)menuItem).isTopLevelMenu();
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -58,34 +58,6 @@ import sun.swing.plaf.synth.SynthUI; ...@@ -58,34 +58,6 @@ import sun.swing.plaf.synth.SynthUI;
*/ */
class SynthPopupMenuUI extends BasicPopupMenuUI implements class SynthPopupMenuUI extends BasicPopupMenuUI implements
PropertyChangeListener, SynthUI { 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; private SynthStyle style;
public static ComponentUI createUI(JComponent x) { public static ComponentUI createUI(JComponent x) {
...@@ -153,90 +125,6 @@ class SynthPopupMenuUI extends BasicPopupMenuUI implements ...@@ -153,90 +125,6 @@ class SynthPopupMenuUI extends BasicPopupMenuUI implements
return SynthLookAndFeel.getComponentState(c); 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) { public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c); SynthContext context = getContext(c);
......
/*
* 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 + "]";
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册