/* * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.swing; import java.security.*;import java.lang.reflect.*;import java.awt.*;import static java.awt.RenderingHints.*;import java.awt.event.*;import java.awt.font.*;import java.awt.geom.*;import java.awt.print.PrinterGraphics;import java.text.Bidi;import java.text.AttributedCharacterIterator;import java.text.AttributedString; import javax.swing.*;import javax.swing.plaf.*;import javax.swing.text.Highlighter;import javax.swing.text.JTextComponent;import javax.swing.text.DefaultHighlighter;import javax.swing.text.DefaultCaret;import javax.swing.table.TableCellRenderer;import javax.swing.table.TableColumnModel; import sun.swing.PrintColorUIResource;import sun.swing.ImageIconUIResource;import sun.print.ProxyPrintGraphics;import sun.awt.*;import sun.security.action.GetPropertyAction;import sun.security.util.SecurityConstants;import java.io.*;import java.util.*;import sun.font.FontDesignMetrics;import sun.font.FontUtilities;import sun.java2d.SunGraphicsEnvironment; import java.util.concurrent.Callable;import java.util.concurrent.Future;import java.util.concurrent.FutureTask; /** * A collection of utility methods for Swing. * <p> * <b>WARNING:</b> While this class is public, it should not be treated as * public API and its API may change in incompatable ways between dot dot * releases and even patch releases. You should not rely on this class even * existing. * */public class SwingUtilities2 { /** * The <code>AppContext</code> key for our one <code>LAFState</code> * instance. */ public static final Object LAF_STATE_KEY = new StringBuffer("LookAndFeel State"); // 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); /** * A JComponent client property is used to determine text aa settings. * To avoid having this property persist between look and feels changes * the value of the property is set to null in JComponent.setUI */ public static final Object AA_TEXT_PROPERTY_KEY = new StringBuffer("AATextInfoPropertyKey"); /** * Attribute key for the content elements. If it is set on an element, the * element is considered to be a line break. */ public static final String IMPLIED_CR = "CR"; /** * Used to tell a text component, being used as an editor for table * or tree, how many clicks it took to start editing. */ private static final StringBuilder SKIP_CLICK_COUNT = new StringBuilder("skipClickCount"); /* Presently this class assumes default fractional metrics. * This may need to change to emulate future platform L&Fs. */ public static class AATextInfo { private static AATextInfo getAATextInfoFromMap(Map hints) { Object aaHint = hints.get(KEY_TEXT_ANTIALIASING); Object contHint = hints.get(KEY_TEXT_LCD_CONTRAST); if (aaHint == null || aaHint == VALUE_TEXT_ANTIALIAS_OFF || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { return null; } else { return new AATextInfo(aaHint, (Integer)contHint); } } public static AATextInfo getAATextInfo(boolean lafCondition) { SunToolkit.setAAFontSettingsCondition(lafCondition); Toolkit tk = Toolkit.getDefaultToolkit(); Object map = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS); if (map instanceof Map) { return getAATextInfoFromMap((Map)map); } else { return null; } } Object aaHint; Integer lcdContrastHint; FontRenderContext frc; /* These are rarely constructed objects, and only when a complete * UI is being updated, so the cost of the tests here is minimal * and saves tests elsewhere. * We test that the values are ones we support/expect. */ public AATextInfo(Object aaHint, Integer lcdContrastHint) { if (aaHint == null) { throw new InternalError("null not allowed here"); } if (aaHint == VALUE_TEXT_ANTIALIAS_OFF || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { throw new InternalError("AA must be on"); } this.aaHint = aaHint; this.lcdContrastHint = lcdContrastHint; this.frc = new FontRenderContext(null, aaHint, VALUE_FRACTIONALMETRICS_DEFAULT); } } /** * Key used in client properties used to indicate that the * <code>ComponentUI</code> of the JComponent instance should be returned. */ public static final Object COMPONENT_UI_PROPERTY_KEY = new StringBuffer("ComponentUIPropertyKey"); /** Client Property key for the text maximal offsets for BasicMenuItemUI */ public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET = new StringUIClientPropertyKey ("maxTextOffset"); // security stuff private static Field inputEvent_CanAccessSystemClipboard_Field = null; private static final String UntrustedClipboardAccess = "UNTRUSTED_CLIPBOARD_ACCESS_KEY"; //all access to charsBuffer is to be synchronized on charsBufferLock private static final int CHAR_BUFFER_SIZE = 100; private static final Object charsBufferLock = new Object(); private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE]; static { fontCache = new LSBCacheEntry[CACHE_SIZE]; } /** * Fill the character buffer cache. Return the buffer length. */ private static int syncCharsBuffer(String s) { int length = s.length(); if ((charsBuffer == null) || (charsBuffer.length < length)) { charsBuffer = s.toCharArray(); } else { s.getChars(0, length, charsBuffer, 0); } return length; } /** * checks whether TextLayout is required to handle characters. * * @param text characters to be tested * @param start start * @param limit limit * @return <tt>true</tt> if TextLayout is required * <tt>false</tt> if TextLayout is not required */ public static final boolean isComplexLayout(char[] text, int start, int limit) { return FontUtilities.isComplexText(text, start, limit); } // // WARNING WARNING WARNING WARNING WARNING WARNING // Many of the following methods are invoked from older API. // As this older API was not passed a Component, a null Component may // now be passsed in. For example, SwingUtilities.computeStringWidth // is implemented to call SwingUtilities2.stringWidth, the // SwingUtilities variant does not take a JComponent, as such // SwingUtilities2.stringWidth can be passed a null Component. // In other words, if you add new functionality to these methods you // need to gracefully handle null. // /** * Returns whether or not text should be drawn antialiased. * * @param c JComponent to test. * @return Whether or not text should be drawn antialiased for the * specified component. */ public static AATextInfo drawTextAntialiased(JComponent c) { if (c != null) { /* a non-null property implies some form of AA requested */ return (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY); } // No component, assume aa is off return null; } /** * Returns the left side bearing of the first character of string. The * left side bearing is calculated from the passed in * FontMetrics. If the passed in String is less than one * character {@code 0} is returned. * * @param c JComponent that will display the string * @param fm FontMetrics used to measure the String width * @param string String to get the left side bearing for. * @throws NullPointerException if {@code string} is {@code null} * * @return the left side bearing of the first character of string * or {@code 0} if the string is empty */ 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 first character of string. The * left 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 firstChar Character to get the left side bearing for. */ public static int getLeftSideBearing(JComponent c, FontMetrics fm, char firstChar) { int charIndex = (int) firstChar; if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) { byte[] lsbs = null; 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 for this pair for (LSBCacheEntry cacheEntry : fontCache) { if (searchKey.equals(cacheEntry)) { entry = cacheEntry; 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); } } return 0; } /** * Returns the FontMetrics for the current Font of the passed * in Graphics. This method is used when a Graphics * is available, typically when painting. If a Graphics is not * available the JComponent method of the same name should be used. * <p> * Callers should pass in a non-null JComponent, the exception * to this is if a JComponent is not readily available at the time of * painting. * <p> * This does not necessarily return the FontMetrics from the * Graphics. * * @param c JComponent requesting FontMetrics, may be null * @param g Graphics Graphics */ public static FontMetrics getFontMetrics(JComponent c, Graphics g) { return getFontMetrics(c, g, g.getFont()); } /** * Returns the FontMetrics for the specified Font. * This method is used when a Graphics is available, typically when * painting. If a Graphics is not available the JComponent method of * the same name should be used. * <p> * Callers should pass in a non-null JComonent, the exception * to this is if a JComponent is not readily available at the time of * painting. * <p> * This does not necessarily return the FontMetrics from the * Graphics. * * @param c JComponent requesting FontMetrics, may be null * @param c Graphics Graphics * @param font Font to get FontMetrics for */ public static FontMetrics getFontMetrics(JComponent c, Graphics g, Font font) { if (c != null) { // Note: We assume that we're using the FontMetrics // from the widget to layout out text, otherwise we can get // mismatches when printing. return c.getFontMetrics(font); } return Toolkit.getDefaultToolkit().getFontMetrics(font); } /** * Returns the width of the passed in String. * If the passed String is <code>null</code>, returns zero. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to get the width of */ public static int stringWidth(JComponent c, FontMetrics fm, String string){ if (string == null || string.equals("")) { return 0; } boolean needsTextLayout = ((c != null) && (c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null)); if (needsTextLayout) { synchronized(charsBufferLock) { int length = syncCharsBuffer(string); needsTextLayout = isComplexLayout(charsBuffer, 0, length); } } if (needsTextLayout) { TextLayout layout = createTextLayout(c, string, fm.getFont(), fm.getFontRenderContext()); return (int) layout.getAdvance(); } else { return fm.stringWidth(string); } } /** * Clips the passed in String to the space provided. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to display * @param availTextWidth Amount of space that the string can be drawn in * @return Clipped string that can fit in the provided space. */ public static String clipStringIfNecessary(JComponent c, FontMetrics fm, String string, int availTextWidth) { if ((string == null) || (string.equals(""))) { return ""; } int textWidth = SwingUtilities2.stringWidth(c, fm, string); if (textWidth > availTextWidth) { return SwingUtilities2.clipString(c, fm, string, availTextWidth); } return string; } /** * Clips the passed in String to the space provided. NOTE: this assumes * the string does not fit in the available space. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to display * @param availTextWidth Amount of space that the string can be drawn in * @return Clipped string that can fit in the provided space. */ public static String clipString(JComponent c, FontMetrics fm, String string, int availTextWidth) { // c may be null here. String clipString = "..."; availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString); if (availTextWidth <= 0) { //can not fit any characters return clipString; } boolean needsTextLayout; synchronized (charsBufferLock) { int stringLength = syncCharsBuffer(string); needsTextLayout = isComplexLayout(charsBuffer, 0, stringLength); if (!needsTextLayout) { int width = 0; for (int nChars = 0; nChars < stringLength; nChars++) { width += fm.charWidth(charsBuffer[nChars]); if (width > availTextWidth) { string = string.substring(0, nChars); break; } } } } if (needsTextLayout) { FontRenderContext frc = getFontRenderContext(c, fm); AttributedString aString = new AttributedString(string); if (c != null) { aString.addAttribute(TextAttribute.NUMERIC_SHAPING, c.getClientProperty(TextAttribute.NUMERIC_SHAPING)); } LineBreakMeasurer measurer = new LineBreakMeasurer(aString.getIterator(), frc); int nChars = measurer.nextOffset(availTextWidth); string = string.substring(0, nChars); } return string + clipString; } /** * Draws the string at the specified location. * * @param c JComponent that will display the string, may be null * @param g Graphics to draw the text to * @param text String to display * @param x X coordinate to draw the text at * @param y Y coordinate to draw the text at */ public static void drawString(JComponent c, Graphics g, String text, int x, int y) { // c may be null // All non-editable widgets that draw strings call into this // methods. By non-editable that means widgets like JLabel, JButton // but NOT JTextComponents. if ( text == null || text.length() <= 0 ) { //no need to paint empty strings return; } if (isPrinting(g)) { Graphics2D g2d = getGraphics2D(g); if (g2d != null) { /* The printed text must scale linearly with the UI.Lines 1–500 of 1871. Load more
/* * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.swing; import java.security.*; import java.lang.reflect.*; import java.awt.*; import static java.awt.RenderingHints.*; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.PrinterGraphics; import java.text.Bidi; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import javax.swing.*; import javax.swing.plaf.*; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.DefaultHighlighter; import javax.swing.text.DefaultCaret; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; import sun.swing.PrintColorUIResource; import sun.swing.ImageIconUIResource; import sun.print.ProxyPrintGraphics; import sun.awt.*; import sun.security.action.GetPropertyAction; import sun.security.util.SecurityConstants; import java.io.*; import java.util.*; import sun.font.FontDesignMetrics; import sun.font.FontUtilities; import sun.java2d.SunGraphicsEnvironment; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; /** * A collection of utility methods for Swing. * <p> * <b>WARNING:</b> While this class is public, it should not be treated as * public API and its API may change in incompatable ways between dot dot * releases and even patch releases. You should not rely on this class even * existing. * */ public class SwingUtilities2 { /** * The <code>AppContext</code> key for our one <code>LAFState</code> * instance. */ public static final Object LAF_STATE_KEY = new StringBuffer("LookAndFeel State"); // 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); /** * A JComponent client property is used to determine text aa settings. * To avoid having this property persist between look and feels changes * the value of the property is set to null in JComponent.setUI */ public static final Object AA_TEXT_PROPERTY_KEY = new StringBuffer("AATextInfoPropertyKey"); /** * Attribute key for the content elements. If it is set on an element, the * element is considered to be a line break. */ public static final String IMPLIED_CR = "CR"; /** * Used to tell a text component, being used as an editor for table * or tree, how many clicks it took to start editing. */ private static final StringBuilder SKIP_CLICK_COUNT = new StringBuilder("skipClickCount"); /* Presently this class assumes default fractional metrics. * This may need to change to emulate future platform L&Fs. */ public static class AATextInfo { private static AATextInfo getAATextInfoFromMap(Map hints) { Object aaHint = hints.get(KEY_TEXT_ANTIALIASING); Object contHint = hints.get(KEY_TEXT_LCD_CONTRAST); if (aaHint == null || aaHint == VALUE_TEXT_ANTIALIAS_OFF || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { return null; } else { return new AATextInfo(aaHint, (Integer)contHint); } } public static AATextInfo getAATextInfo(boolean lafCondition) { SunToolkit.setAAFontSettingsCondition(lafCondition); Toolkit tk = Toolkit.getDefaultToolkit(); Object map = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS); if (map instanceof Map) { return getAATextInfoFromMap((Map)map); } else { return null; } } Object aaHint; Integer lcdContrastHint; FontRenderContext frc; /* These are rarely constructed objects, and only when a complete * UI is being updated, so the cost of the tests here is minimal * and saves tests elsewhere. * We test that the values are ones we support/expect. */ public AATextInfo(Object aaHint, Integer lcdContrastHint) { if (aaHint == null) { throw new InternalError("null not allowed here"); } if (aaHint == VALUE_TEXT_ANTIALIAS_OFF || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) { throw new InternalError("AA must be on"); } this.aaHint = aaHint; this.lcdContrastHint = lcdContrastHint; this.frc = new FontRenderContext(null, aaHint, VALUE_FRACTIONALMETRICS_DEFAULT); } } /** * Key used in client properties used to indicate that the * <code>ComponentUI</code> of the JComponent instance should be returned. */ public static final Object COMPONENT_UI_PROPERTY_KEY = new StringBuffer("ComponentUIPropertyKey"); /** Client Property key for the text maximal offsets for BasicMenuItemUI */ public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET = new StringUIClientPropertyKey ("maxTextOffset"); // security stuff private static Field inputEvent_CanAccessSystemClipboard_Field = null; private static final String UntrustedClipboardAccess = "UNTRUSTED_CLIPBOARD_ACCESS_KEY"; //all access to charsBuffer is to be synchronized on charsBufferLock private static final int CHAR_BUFFER_SIZE = 100; private static final Object charsBufferLock = new Object(); private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE]; static { fontCache = new LSBCacheEntry[CACHE_SIZE]; } /** * Fill the character buffer cache. Return the buffer length. */ private static int syncCharsBuffer(String s) { int length = s.length(); if ((charsBuffer == null) || (charsBuffer.length < length)) { charsBuffer = s.toCharArray(); } else { s.getChars(0, length, charsBuffer, 0); } return length; } /** * checks whether TextLayout is required to handle characters. * * @param text characters to be tested * @param start start * @param limit limit * @return <tt>true</tt> if TextLayout is required * <tt>false</tt> if TextLayout is not required */ public static final boolean isComplexLayout(char[] text, int start, int limit) { return FontUtilities.isComplexText(text, start, limit); } // // WARNING WARNING WARNING WARNING WARNING WARNING // Many of the following methods are invoked from older API. // As this older API was not passed a Component, a null Component may // now be passsed in. For example, SwingUtilities.computeStringWidth // is implemented to call SwingUtilities2.stringWidth, the // SwingUtilities variant does not take a JComponent, as such // SwingUtilities2.stringWidth can be passed a null Component. // In other words, if you add new functionality to these methods you // need to gracefully handle null. // /** * Returns whether or not text should be drawn antialiased. * * @param c JComponent to test. * @return Whether or not text should be drawn antialiased for the * specified component. */ public static AATextInfo drawTextAntialiased(JComponent c) { if (c != null) { /* a non-null property implies some form of AA requested */ return (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY); } // No component, assume aa is off return null; } /** * Returns the left side bearing of the first character of string. The * left side bearing is calculated from the passed in * FontMetrics. If the passed in String is less than one * character {@code 0} is returned. * * @param c JComponent that will display the string * @param fm FontMetrics used to measure the String width * @param string String to get the left side bearing for. * @throws NullPointerException if {@code string} is {@code null} * * @return the left side bearing of the first character of string * or {@code 0} if the string is empty */ 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 first character of string. The * left 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 firstChar Character to get the left side bearing for. */ public static int getLeftSideBearing(JComponent c, FontMetrics fm, char firstChar) { int charIndex = (int) firstChar; if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) { byte[] lsbs = null; 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 for this pair for (LSBCacheEntry cacheEntry : fontCache) { if (searchKey.equals(cacheEntry)) { entry = cacheEntry; 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); } } return 0; } /** * Returns the FontMetrics for the current Font of the passed * in Graphics. This method is used when a Graphics * is available, typically when painting. If a Graphics is not * available the JComponent method of the same name should be used. * <p> * Callers should pass in a non-null JComponent, the exception * to this is if a JComponent is not readily available at the time of * painting. * <p> * This does not necessarily return the FontMetrics from the * Graphics. * * @param c JComponent requesting FontMetrics, may be null * @param g Graphics Graphics */ public static FontMetrics getFontMetrics(JComponent c, Graphics g) { return getFontMetrics(c, g, g.getFont()); } /** * Returns the FontMetrics for the specified Font. * This method is used when a Graphics is available, typically when * painting. If a Graphics is not available the JComponent method of * the same name should be used. * <p> * Callers should pass in a non-null JComonent, the exception * to this is if a JComponent is not readily available at the time of * painting. * <p> * This does not necessarily return the FontMetrics from the * Graphics. * * @param c JComponent requesting FontMetrics, may be null * @param c Graphics Graphics * @param font Font to get FontMetrics for */ public static FontMetrics getFontMetrics(JComponent c, Graphics g, Font font) { if (c != null) { // Note: We assume that we're using the FontMetrics // from the widget to layout out text, otherwise we can get // mismatches when printing. return c.getFontMetrics(font); } return Toolkit.getDefaultToolkit().getFontMetrics(font); } /** * Returns the width of the passed in String. * If the passed String is <code>null</code>, returns zero. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to get the width of */ public static int stringWidth(JComponent c, FontMetrics fm, String string){ if (string == null || string.equals("")) { return 0; } boolean needsTextLayout = ((c != null) && (c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null)); if (needsTextLayout) { synchronized(charsBufferLock) { int length = syncCharsBuffer(string); needsTextLayout = isComplexLayout(charsBuffer, 0, length); } } if (needsTextLayout) { TextLayout layout = createTextLayout(c, string, fm.getFont(), fm.getFontRenderContext()); return (int) layout.getAdvance(); } else { return fm.stringWidth(string); } } /** * Clips the passed in String to the space provided. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to display * @param availTextWidth Amount of space that the string can be drawn in * @return Clipped string that can fit in the provided space. */ public static String clipStringIfNecessary(JComponent c, FontMetrics fm, String string, int availTextWidth) { if ((string == null) || (string.equals(""))) { return ""; } int textWidth = SwingUtilities2.stringWidth(c, fm, string); if (textWidth > availTextWidth) { return SwingUtilities2.clipString(c, fm, string, availTextWidth); } return string; } /** * Clips the passed in String to the space provided. NOTE: this assumes * the string does not fit in the available space. * * @param c JComponent that will display the string, may be null * @param fm FontMetrics used to measure the String width * @param string String to display * @param availTextWidth Amount of space that the string can be drawn in * @return Clipped string that can fit in the provided space. */ public static String clipString(JComponent c, FontMetrics fm, String string, int availTextWidth) { // c may be null here. String clipString = "..."; availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString); if (availTextWidth <= 0) { //can not fit any characters return clipString; } boolean needsTextLayout; synchronized (charsBufferLock) { int stringLength = syncCharsBuffer(string); needsTextLayout = isComplexLayout(charsBuffer, 0, stringLength); if (!needsTextLayout) { int width = 0; for (int nChars = 0; nChars < stringLength; nChars++) { width += fm.charWidth(charsBuffer[nChars]); if (width > availTextWidth) { string = string.substring(0, nChars); break; } } } } if (needsTextLayout) { FontRenderContext frc = getFontRenderContext(c, fm); AttributedString aString = new AttributedString(string); if (c != null) { aString.addAttribute(TextAttribute.NUMERIC_SHAPING, c.getClientProperty(TextAttribute.NUMERIC_SHAPING)); } LineBreakMeasurer measurer = new LineBreakMeasurer(aString.getIterator(), frc); int nChars = measurer.nextOffset(availTextWidth); string = string.substring(0, nChars); } return string + clipString; } /** * Draws the string at the specified location. * * @param c JComponent that will display the string, may be null * @param g Graphics to draw the text to * @param text String to display * @param x X coordinate to draw the text at * @param y Y coordinate to draw the text at */ public static void drawString(JComponent c, Graphics g, String text, int x, int y) { // c may be null // All non-editable widgets that draw strings call into this // methods. By non-editable that means widgets like JLabel, JButton // but NOT JTextComponents. if ( text == null || text.length() <= 0 ) { //no need to paint empty strings return; } if (isPrinting(g)) { Graphics2D g2d = getGraphics2D(g); if (g2d != null) { /* The printed text must scale linearly with the UI.