changeset 1637:281fbd82a971

6797139: JButton title is truncating for some strings irrespective of preferred size. Reviewed-by: peterz
author alexp
date Wed, 02 Sep 2009 17:47:19 +0400
parents 935814bd43a6
children ff468ef27959
files src/share/classes/javax/swing/SwingUtilities.java src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java src/share/classes/sun/swing/MenuItemLayoutHelper.java src/share/classes/sun/swing/SwingUtilities2.java test/javax/swing/SwingUtilities/6797139/bug6797139.java
diffstat 5 files changed, 195 insertions(+), 242 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/javax/swing/SwingUtilities.java	Tue Sep 01 18:51:15 2009 +0400
+++ b/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/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java	Tue Sep 01 18:51:15 2009 +0400
+++ b/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/src/share/classes/sun/swing/MenuItemLayoutHelper.java	Tue Sep 01 18:51:15 2009 +0400
+++ b/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/src/share/classes/sun/swing/SwingUtilities2.java	Tue Sep 01 18:51:15 2009 +0400
+++ b/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);
-    }
+        int charIndex = (int) firstChar;
+        if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) {
+            byte[] lsbs = null;
 
-    /**
-     * 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);
-    }
-
-    /* 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 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 LSBCacheEntry(FontRenderContext frc, Font font) {
+            lsbCache = new byte[MAX_CHAR_INDEX - MIN_CHAR_INDEX];
+            reset(frc, font);
+
+        }
+
+        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);
+            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 ((0xFF00 & bearing) >>> 8) - 127;
-        }
+            return lsb;
 
-        public int getRightSideBearing(char aChar) {
-            Short bearing = cache.get(aChar);
-            if (bearing == null) {
-                bearing = calcBearing(aChar);
-                cache.put(aChar, bearing);
-            }
-            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);
-
-            /* 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/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();
+            }
+        });
+    }
+}