6797139: JButton title is truncating for some strings irrespective of preferred size.
Reviewed-by: peterz
--- a/jdk/src/share/classes/javax/swing/SwingUtilities.java Tue Sep 01 18:51:15 2009 +0400
+++ b/jdk/src/share/classes/javax/swing/SwingUtilities.java Wed Sep 02 17:47:19 2009 +0400
@@ -999,24 +999,20 @@
textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
} else {
textR.width = SwingUtilities2.stringWidth(c, fm, text);
-
- // Take into account the left and right side bearings.
- // This gives more space than it is actually needed,
- // but there are two reasons:
- // 1. If we set the width to the actual bounds,
- // all callers would have to account for the bearings
- // themselves. NOTE: all pref size calculations don't do it.
- // 2. You can do a drawString at the returned location
- // and the text won't be clipped.
lsb = SwingUtilities2.getLeftSideBearing(c, fm, text);
if (lsb < 0) {
+ // If lsb is negative, add it to the width and later
+ // adjust the x location. This gives more space than is
+ // actually needed.
+ // This is done like this for two reasons:
+ // 1. If we set the width to the actual bounds all
+ // callers would have to account for negative lsb
+ // (pref size calculations ONLY look at width of
+ // textR)
+ // 2. You can do a drawString at the returned location
+ // and the text won't be clipped.
textR.width -= lsb;
}
- rsb = SwingUtilities2.getRightSideBearing(c, fm, text);
- if (rsb > 0) {
- textR.width += rsb;
- }
-
if (textR.width > availTextWidth) {
text = SwingUtilities2.clipString(c, fm, text,
availTextWidth);
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java Tue Sep 01 18:51:15 2009 +0400
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java Wed Sep 02 17:47:19 2009 +0400
@@ -195,7 +195,7 @@
getHorizontalAlignment(), getVerticalAlignment(),
getHorizontalTextPosition(), getVerticalTextPosition(),
getViewRect(), iconRect, textRect, getGap());
- textRect.width += getLeftTextExtraWidth() + getRightTextExtraWidth();
+ textRect.width += getLeftTextExtraWidth();
Rectangle labelRect = iconRect.union(textRect);
getLabelSize().setHeight(labelRect.height);
getLabelSize().setWidth(labelRect.width);
--- a/jdk/src/share/classes/sun/swing/MenuItemLayoutHelper.java Tue Sep 01 18:51:15 2009 +0400
+++ b/jdk/src/share/classes/sun/swing/MenuItemLayoutHelper.java Wed Sep 02 17:47:19 2009 +0400
@@ -84,7 +84,6 @@
private int minTextOffset;
private int leftTextExtraWidth;
- private int rightTextExtraWidth;
private Rectangle viewRect;
@@ -157,7 +156,6 @@
private void calcExtraWidths() {
leftTextExtraWidth = getLeftExtraWidth(text);
- rightTextExtraWidth = getRightExtraWidth(text);
}
private int getLeftExtraWidth(String str) {
@@ -169,15 +167,6 @@
}
}
- private int getRightExtraWidth(String str) {
- int rsb = SwingUtilities2.getRightSideBearing(mi, fm, str);
- if (rsb > 0) {
- return rsb;
- } else {
- return 0;
- }
- }
-
private void setOriginalWidths() {
iconSize.origWidth = iconSize.width;
textSize.origWidth = textSize.width;
@@ -313,7 +302,7 @@
verticalAlignment, horizontalAlignment,
verticalTextPosition, horizontalTextPosition,
viewRect, iconRect, textRect, gap);
- textRect.width += leftTextExtraWidth + rightTextExtraWidth;
+ textRect.width += leftTextExtraWidth;
Rectangle labelRect = iconRect.union(textRect);
labelSize.height = labelRect.height;
labelSize.width = labelRect.width;
@@ -1121,10 +1110,6 @@
return leftTextExtraWidth;
}
- public int getRightTextExtraWidth() {
- return rightTextExtraWidth;
- }
-
/**
* Returns false if the component is a JMenu and it is a top
* level menu (on the menubar).
--- a/jdk/src/share/classes/sun/swing/SwingUtilities2.java Tue Sep 01 18:51:15 2009 +0400
+++ b/jdk/src/share/classes/sun/swing/SwingUtilities2.java Wed Sep 02 17:47:19 2009 +0400
@@ -27,7 +27,6 @@
import java.security.*;
import java.lang.reflect.*;
-import java.lang.ref.SoftReference;
import java.awt.*;
import static java.awt.RenderingHints.*;
import java.awt.event.*;
@@ -78,17 +77,23 @@
public static final Object LAF_STATE_KEY =
new StringBuffer("LookAndFeel State");
- // Most of applications use 10 or less fonts simultaneously
- private static final int STRONG_BEARING_CACHE_SIZE = 10;
- // Strong cache for the left and right side bearings
- // for STRONG_BEARING_CACHE_SIZE most recently used fonts.
- private static BearingCacheEntry[] strongBearingCache =
- new BearingCacheEntry[STRONG_BEARING_CACHE_SIZE];
- // Next index to insert an entry into the strong bearing cache
- private static int strongBearingCacheNextIndex = 0;
- // Soft cache for the left and right side bearings
- private static Set<SoftReference<BearingCacheEntry>> softBearingCache =
- new HashSet<SoftReference<BearingCacheEntry>>();
+ // Maintain a cache of CACHE_SIZE fonts and the left side bearing
+ // of the characters falling into the range MIN_CHAR_INDEX to
+ // MAX_CHAR_INDEX. The values in fontCache are created as needed.
+ private static LSBCacheEntry[] fontCache;
+ // Windows defines 6 font desktop properties, we will therefore only
+ // cache the metrics for 6 fonts.
+ private static final int CACHE_SIZE = 6;
+ // nextIndex in fontCache to insert a font into.
+ private static int nextIndex;
+ // LSBCacheEntry used to search in fontCache to see if we already
+ // have an entry for a particular font
+ private static LSBCacheEntry searchKey;
+
+ // getLeftSideBearing will consult all characters that fall in the
+ // range MIN_CHAR_INDEX to MAX_CHAR_INDEX.
+ private static final int MIN_CHAR_INDEX = (int)'W';
+ private static final int MAX_CHAR_INDEX = (int)'W' + 1;
public static final FontRenderContext DEFAULT_FRC =
new FontRenderContext(null, false, false);
@@ -183,6 +188,10 @@
private static final Object charsBufferLock = new Object();
private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE];
+ static {
+ fontCache = new LSBCacheEntry[CACHE_SIZE];
+ }
+
/**
* checks whether TextLayout is required to handle characters.
*
@@ -226,7 +235,9 @@
/**
* Returns the left side bearing of the first character of string. The
- * left side bearing is calculated from the passed in FontMetrics.
+ * left side bearing is calculated from the passed in
+ * FontMetrics. If the passed in String is less than one
+ * character, this will throw a StringIndexOutOfBoundsException exception.
*
* @param c JComponent that will display the string
* @param fm FontMetrics used to measure the String width
@@ -234,14 +245,11 @@
*/
public static int getLeftSideBearing(JComponent c, FontMetrics fm,
String string) {
- if ((string == null) || (string.length() == 0)) {
- return 0;
- }
return getLeftSideBearing(c, fm, string.charAt(0));
}
/**
- * Returns the left side bearing of the specified character. The
+ * Returns the left side bearing of the first character of string. The
* left side bearing is calculated from the passed in FontMetrics.
*
* @param c JComponent that will display the string
@@ -250,105 +258,37 @@
*/
public static int getLeftSideBearing(JComponent c, FontMetrics fm,
char firstChar) {
- return getBearing(c, fm, firstChar, true);
- }
-
- /**
- * Returns the right side bearing of the last character of string. The
- * right side bearing is calculated from the passed in FontMetrics.
- *
- * @param c JComponent that will display the string
- * @param fm FontMetrics used to measure the String width
- * @param string String to get the right side bearing for.
- */
- public static int getRightSideBearing(JComponent c, FontMetrics fm,
- String string) {
- if ((string == null) || (string.length() == 0)) {
- return 0;
- }
- return getRightSideBearing(c, fm, string.charAt(string.length() - 1));
- }
-
- /**
- * Returns the right side bearing of the specified character. The
- * right side bearing is calculated from the passed in FontMetrics.
- *
- * @param c JComponent that will display the string
- * @param fm FontMetrics used to measure the String width
- * @param lastChar Character to get the right side bearing for.
- */
- public static int getRightSideBearing(JComponent c, FontMetrics fm,
- char lastChar) {
- return getBearing(c, fm, lastChar, false);
- }
+ int charIndex = (int) firstChar;
+ if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) {
+ byte[] lsbs = null;
- /* Calculates the left and right side bearing for a character.
- * Strongly caches bearings for STRONG_BEARING_CACHE_SIZE
- * most recently used Fonts and softly caches as many as GC allows.
- */
- private static int getBearing(JComponent comp, FontMetrics fm, char c,
- boolean isLeftBearing) {
- if (fm == null) {
- if (comp == null) {
- return 0;
- } else {
- fm = comp.getFontMetrics(comp.getFont());
- }
- }
- synchronized (SwingUtilities2.class) {
- BearingCacheEntry entry = null;
- BearingCacheEntry searchKey = new BearingCacheEntry(fm);
- // See if we already have an entry in the strong cache
- for (BearingCacheEntry cacheEntry : strongBearingCache) {
- if (searchKey.equals(cacheEntry)) {
- entry = cacheEntry;
- break;
+ FontRenderContext frc = getFontRenderContext(c, fm);
+ Font font = fm.getFont();
+ synchronized (SwingUtilities2.class) {
+ LSBCacheEntry entry = null;
+ if (searchKey == null) {
+ searchKey = new LSBCacheEntry(frc, font);
+ } else {
+ searchKey.reset(frc, font);
}
- }
- // See if we already have an entry in the soft cache
- if (entry == null) {
- Iterator<SoftReference<BearingCacheEntry>> iter =
- softBearingCache.iterator();
- while (iter.hasNext()) {
- BearingCacheEntry cacheEntry = iter.next().get();
- if (cacheEntry == null) {
- // Remove discarded soft reference from the cache
- iter.remove();
- continue;
- }
+ // See if we already have an entry for this pair
+ for (LSBCacheEntry cacheEntry : fontCache) {
if (searchKey.equals(cacheEntry)) {
entry = cacheEntry;
- putEntryInStrongCache(entry);
break;
}
}
+ if (entry == null) {
+ // No entry for this pair, add it.
+ entry = searchKey;
+ fontCache[nextIndex] = searchKey;
+ searchKey = null;
+ nextIndex = (nextIndex + 1) % CACHE_SIZE;
+ }
+ return entry.getLeftSideBearing(firstChar);
}
- if (entry == null) {
- // No entry, add it
- entry = searchKey;
- cacheEntry(entry);
- }
- return (isLeftBearing)
- ? entry.getLeftSideBearing(c)
- : entry.getRightSideBearing(c);
}
- }
-
- private synchronized static void cacheEntry(BearingCacheEntry entry) {
- // Move the oldest entry from the strong cache into the soft cache
- BearingCacheEntry oldestEntry =
- strongBearingCache[strongBearingCacheNextIndex];
- if (oldestEntry != null) {
- softBearingCache.add(new SoftReference<BearingCacheEntry>(oldestEntry));
- }
- // Put entry in the strong cache
- putEntryInStrongCache(entry);
- }
-
- private synchronized static void putEntryInStrongCache(BearingCacheEntry entry) {
- strongBearingCache[strongBearingCacheNextIndex] = entry;
- strongBearingCacheNextIndex = (strongBearingCacheNextIndex + 1)
- % STRONG_BEARING_CACHE_SIZE;
+ return 0;
}
/**
@@ -1063,99 +1003,72 @@
}
/**
- * BearingCacheEntry is used to cache left and right character bearings
- * for a particular <code>Font</code> and <code>FontRenderContext</code>.
+ * LSBCacheEntry is used to cache the left side bearing (lsb) for
+ * a particular <code>Font</code> and <code>FontRenderContext</code>.
+ * This only caches characters that fall in the range
+ * <code>MIN_CHAR_INDEX</code> to <code>MAX_CHAR_INDEX</code>.
*/
- private static class BearingCacheEntry {
- private FontMetrics fontMetrics;
+ private static class LSBCacheEntry {
+ // Used to indicate a particular entry in lsb has not been set.
+ private static final byte UNSET = Byte.MAX_VALUE;
+ // Used in creating a GlyphVector to get the lsb
+ private static final char[] oneChar = new char[1];
+
+ private byte[] lsbCache;
private Font font;
private FontRenderContext frc;
- private Map<Character, Short> cache;
- // Used for the creation of a GlyphVector
- private static final char[] oneChar = new char[1];
+
+
+ public LSBCacheEntry(FontRenderContext frc, Font font) {
+ lsbCache = new byte[MAX_CHAR_INDEX - MIN_CHAR_INDEX];
+ reset(frc, font);
- public BearingCacheEntry(FontMetrics fontMetrics) {
- this.fontMetrics = fontMetrics;
- this.font = fontMetrics.getFont();
- this.frc = fontMetrics.getFontRenderContext();
- this.cache = new HashMap<Character, Short>();
- assert (font != null && frc != null);
+ }
+
+ public void reset(FontRenderContext frc, Font font) {
+ this.font = font;
+ this.frc = frc;
+ for (int counter = lsbCache.length - 1; counter >= 0; counter--) {
+ lsbCache[counter] = UNSET;
+ }
}
public int getLeftSideBearing(char aChar) {
- Short bearing = cache.get(aChar);
- if (bearing == null) {
- bearing = calcBearing(aChar);
- cache.put(aChar, bearing);
- }
- return ((0xFF00 & bearing) >>> 8) - 127;
- }
-
- public int getRightSideBearing(char aChar) {
- Short bearing = cache.get(aChar);
- if (bearing == null) {
- bearing = calcBearing(aChar);
- cache.put(aChar, bearing);
+ int index = aChar - MIN_CHAR_INDEX;
+ assert (index >= 0 && index < (MAX_CHAR_INDEX - MIN_CHAR_INDEX));
+ byte lsb = lsbCache[index];
+ if (lsb == UNSET) {
+ oneChar[0] = aChar;
+ GlyphVector gv = font.createGlyphVector(frc, oneChar);
+ lsb = (byte) gv.getGlyphPixelBounds(0, frc, 0f, 0f).x;
+ if (lsb < 0) {
+ /* HRGB/HBGR LCD glyph images will always have a pixel
+ * on the left used in colour fringe reduction.
+ * Text rendering positions this correctly but here
+ * we are using the glyph image to adjust that position
+ * so must account for it.
+ */
+ Object aaHint = frc.getAntiAliasingHint();
+ if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
+ aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
+ lsb++;
+ }
+ }
+ lsbCache[index] = lsb;
}
- return (0xFF & bearing) - 127;
- }
-
- /* Calculates left and right side bearings for a character.
- * Makes an assumption that bearing is a value between -127 and +127.
- * Stores LSB and RSB as single two-byte number (short):
- * LSB is the high byte, RSB is the low byte.
- */
- private short calcBearing(char aChar) {
- oneChar[0] = aChar;
- GlyphVector gv = font.createGlyphVector(frc, oneChar);
- Rectangle pixelBounds = gv.getGlyphPixelBounds(0, frc, 0f, 0f);
-
- // Get bearings
- int lsb = pixelBounds.x;
- int rsb = pixelBounds.width - fontMetrics.charWidth(aChar);
+ return lsb;
- /* HRGB/HBGR LCD glyph images will always have a pixel
- * on the left and a pixel on the right
- * used in colour fringe reduction.
- * Text rendering positions this correctly but here
- * we are using the glyph image to adjust that position
- * so must account for it.
- */
- if (lsb < 0) {
- Object aaHint = frc.getAntiAliasingHint();
- if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
- aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
- lsb++;
- }
- }
- if (rsb > 0) {
- Object aaHint = frc.getAntiAliasingHint();
- if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
- aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
- rsb--;
- }
- }
- // Make sure that LSB and RSB are valid (see 6472972)
- if (lsb < -127 || lsb > 127) {
- lsb = 0;
- }
- if (rsb < -127 || rsb > 127) {
- rsb = 0;
- }
-
- int bearing = ((lsb + 127) << 8) + (rsb + 127);
- return (short)bearing;
}
public boolean equals(Object entry) {
if (entry == this) {
return true;
}
- if (!(entry instanceof BearingCacheEntry)) {
+ if (!(entry instanceof LSBCacheEntry)) {
return false;
}
- BearingCacheEntry oEntry = (BearingCacheEntry)entry;
+ LSBCacheEntry oEntry = (LSBCacheEntry) entry;
return (font.equals(oEntry.font) &&
frc.equals(oEntry.frc));
}
@@ -1172,7 +1085,6 @@
}
}
-
/*
* here goes the fix for 4856343 [Problem with applet interaction
* with system selection clipboard]
@@ -1181,36 +1093,34 @@
* are to be performed
*/
-
/**
- * checks the security permissions for accessing system clipboard
- *
- * for untrusted context (see isTrustedContext) checks the
- * permissions for the current event being handled
- *
- */
- public static boolean canAccessSystemClipboard() {
- boolean canAccess = false;
- if (!GraphicsEnvironment.isHeadless()) {
- SecurityManager sm = System.getSecurityManager();
- if (sm == null) {
- canAccess = true;
- } else {
- try {
- sm.checkSystemClipboardAccess();
- canAccess = true;
- } catch (SecurityException e) {
- }
- if (canAccess && ! isTrustedContext()) {
- canAccess = canCurrentEventAccessSystemClipboard(true);
- }
- }
- }
- return canAccess;
- }
-
+ * checks the security permissions for accessing system clipboard
+ *
+ * for untrusted context (see isTrustedContext) checks the
+ * permissions for the current event being handled
+ *
+ */
+ public static boolean canAccessSystemClipboard() {
+ boolean canAccess = false;
+ if (!GraphicsEnvironment.isHeadless()) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null) {
+ canAccess = true;
+ } else {
+ try {
+ sm.checkSystemClipboardAccess();
+ canAccess = true;
+ } catch (SecurityException e) {
+ }
+ if (canAccess && ! isTrustedContext()) {
+ canAccess = canCurrentEventAccessSystemClipboard(true);
+ }
+ }
+ }
+ return canAccess;
+ }
/**
- * Returns true if EventQueue.getCurrentEvent() has the permissions to
+ * Returns true if EventQueue.getCurrentEvent() has the permissions to
* access the system clipboard
*/
public static boolean canCurrentEventAccessSystemClipboard() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/SwingUtilities/6797139/bug6797139.java Wed Sep 02 17:47:19 2009 +0400
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2009 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.
+ *
+ * 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.
+ */
+
+/* @test
+ *
+ * @bug 6797139
+ * @author Alexander Potochkin
+ * @summary tests that JButton's text is not incorrectly truncated
+ */
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicButtonUI;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class bug6797139 {
+
+ private static void createGui() {
+ JButton b = new JButton("Probably");
+ b.setUI(new BasicButtonUI() {
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
+ super.paintText(g, b, textRect, text);
+ if (text.endsWith("...")) {
+ throw new RuntimeException("Text is truncated!");
+ }
+ }
+ });
+ b.setSize(b.getPreferredSize());
+ BufferedImage image = new BufferedImage(b.getWidth(), b.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics g = image.getGraphics();
+ b.paint(g);
+ g.dispose();
+ }
+
+ public static void main(String[] args) throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ createGui();
+ }
+ });
+ }
+}