Path: blob/master/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java
41153 views
/*1* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.swing;2627import java.awt.AWTEvent;28import java.awt.Color;29import java.awt.Component;30import java.awt.Container;31import java.awt.Dimension;32import java.awt.EventQueue;33import java.awt.FocusTraversalPolicy;34import java.awt.Font;35import java.awt.FontMetrics;36import java.awt.Graphics;37import java.awt.Graphics2D;38import java.awt.GraphicsConfiguration;39import java.awt.GraphicsEnvironment;40import java.awt.Point;41import java.awt.PrintGraphics;42import java.awt.Rectangle;43import java.awt.RenderingHints;44import java.awt.Shape;45import java.awt.Toolkit;46import java.awt.event.InputEvent;47import java.awt.event.KeyEvent;48import java.awt.event.MouseEvent;49import java.awt.font.FontRenderContext;50import java.awt.font.GlyphVector;51import java.awt.font.LineBreakMeasurer;52import java.awt.font.TextAttribute;53import java.awt.font.TextHitInfo;54import java.awt.font.TextLayout;55import java.awt.geom.AffineTransform;56import java.awt.geom.Rectangle2D;57import java.awt.print.PrinterGraphics;58import java.beans.PropertyChangeEvent;59import java.io.BufferedInputStream;60import java.io.IOException;61import java.io.InputStream;62import java.lang.reflect.Modifier;63import java.security.AccessController;64import java.security.PrivilegedAction;65import java.text.AttributedCharacterIterator;66import java.text.AttributedString;67import java.text.BreakIterator;68import java.text.CharacterIterator;69import java.util.HashMap;70import java.util.Locale;71import java.util.Map;72import java.util.Objects;73import java.util.concurrent.Callable;74import java.util.concurrent.Future;75import java.util.concurrent.FutureTask;7677import javax.swing.JComponent;78import javax.swing.JList;79import javax.swing.JTable;80import javax.swing.ListCellRenderer;81import javax.swing.ListSelectionModel;82import javax.swing.SwingUtilities;83import javax.swing.UIDefaults;84import javax.swing.UIManager;85import javax.swing.event.TreeModelEvent;86import javax.swing.table.TableCellRenderer;87import javax.swing.table.TableColumnModel;88import javax.swing.text.DefaultCaret;89import javax.swing.text.DefaultHighlighter;90import javax.swing.text.Highlighter;91import javax.swing.text.JTextComponent;92import javax.swing.tree.TreeModel;93import javax.swing.tree.TreePath;9495import sun.awt.AWTAccessor;96import sun.awt.AWTPermissions;97import sun.awt.AppContext;98import sun.awt.SunToolkit;99import sun.font.FontDesignMetrics;100import sun.font.FontUtilities;101import sun.java2d.SunGraphicsEnvironment;102import sun.print.ProxyPrintGraphics;103104import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;105import static java.awt.RenderingHints.KEY_TEXT_LCD_CONTRAST;106import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT;107import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;108import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;109import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;110import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;111import static java.awt.geom.AffineTransform.TYPE_FLIP;112import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;113import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;114115/**116* A collection of utility methods for Swing.117* <p>118* <b>WARNING:</b> While this class is public, it should not be treated as119* public API and its API may change in incompatable ways between dot dot120* releases and even patch releases. You should not rely on this class even121* existing.122*123*/124public class SwingUtilities2 {125/**126* The {@code AppContext} key for our one {@code LAFState}127* instance.128*/129public static final Object LAF_STATE_KEY =130new StringBuffer("LookAndFeel State");131132public static final Object MENU_SELECTION_MANAGER_LISTENER_KEY =133new StringBuffer("MenuSelectionManager listener key");134135// Maintain a cache of CACHE_SIZE fonts and the left side bearing136// of the characters falling into the range MIN_CHAR_INDEX to137// MAX_CHAR_INDEX. The values in fontCache are created as needed.138private static LSBCacheEntry[] fontCache;139// Windows defines 6 font desktop properties, we will therefore only140// cache the metrics for 6 fonts.141private static final int CACHE_SIZE = 6;142// nextIndex in fontCache to insert a font into.143private static int nextIndex;144// LSBCacheEntry used to search in fontCache to see if we already145// have an entry for a particular font146private static LSBCacheEntry searchKey;147148// getLeftSideBearing will consult all characters that fall in the149// range MIN_CHAR_INDEX to MAX_CHAR_INDEX.150private static final int MIN_CHAR_INDEX = (int)'W';151private static final int MAX_CHAR_INDEX = (int)'W' + 1;152153public static final FontRenderContext DEFAULT_FRC =154new FontRenderContext(null, false, false);155156/**157* Attribute key for the content elements. If it is set on an element, the158* element is considered to be a line break.159*/160public static final String IMPLIED_CR = "CR";161162/**163* Used to tell a text component, being used as an editor for table164* or tree, how many clicks it took to start editing.165*/166private static final StringBuilder SKIP_CLICK_COUNT =167new StringBuilder("skipClickCount");168169@SuppressWarnings("unchecked")170public static void putAATextInfo(boolean lafCondition,171Map<Object, Object> map) {172SunToolkit.setAAFontSettingsCondition(lafCondition);173Toolkit tk = Toolkit.getDefaultToolkit();174Object desktopHints = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS);175176if (desktopHints instanceof Map) {177Map<Object, Object> hints = (Map<Object, Object>) desktopHints;178Object aaHint = hints.get(KEY_TEXT_ANTIALIASING);179if (aaHint == null180|| aaHint == VALUE_TEXT_ANTIALIAS_OFF181|| aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) {182return;183}184map.put(KEY_TEXT_ANTIALIASING, aaHint);185map.put(KEY_TEXT_LCD_CONTRAST, hints.get(KEY_TEXT_LCD_CONTRAST));186}187}188189/** Client Property key for the text maximal offsets for BasicMenuItemUI */190public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET =191new StringUIClientPropertyKey ("maxTextOffset");192193// security stuff194private static final String UntrustedClipboardAccess =195"UNTRUSTED_CLIPBOARD_ACCESS_KEY";196197//all access to charsBuffer is to be synchronized on charsBufferLock198private static final int CHAR_BUFFER_SIZE = 100;199private static final Object charsBufferLock = new Object();200private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE];201202static {203fontCache = new LSBCacheEntry[CACHE_SIZE];204}205206/**207* Fill the character buffer cache. Return the buffer length.208*/209private static int syncCharsBuffer(String s) {210int length = s.length();211if ((charsBuffer == null) || (charsBuffer.length < length)) {212charsBuffer = s.toCharArray();213} else {214s.getChars(0, length, charsBuffer, 0);215}216return length;217}218219/**220* checks whether TextLayout is required to handle characters.221*222* @param text characters to be tested223* @param start start224* @param limit limit225* @return {@code true} if TextLayout is required226* {@code false} if TextLayout is not required227*/228public static final boolean isComplexLayout(char[] text, int start, int limit) {229return FontUtilities.isComplexText(text, start, limit);230}231232//233// WARNING WARNING WARNING WARNING WARNING WARNING234// Many of the following methods are invoked from older API.235// As this older API was not passed a Component, a null Component may236// now be passsed in. For example, SwingUtilities.computeStringWidth237// is implemented to call SwingUtilities2.stringWidth, the238// SwingUtilities variant does not take a JComponent, as such239// SwingUtilities2.stringWidth can be passed a null Component.240// In other words, if you add new functionality to these methods you241// need to gracefully handle null.242//243244/**245* Returns the left side bearing of the first character of string. The246* left side bearing is calculated from the passed in247* FontMetrics. If the passed in String is less than one248* character {@code 0} is returned.249*250* @param c JComponent that will display the string251* @param fm FontMetrics used to measure the String width252* @param string String to get the left side bearing for.253* @throws NullPointerException if {@code string} is {@code null}254*255* @return the left side bearing of the first character of string256* or {@code 0} if the string is empty257*/258public static int getLeftSideBearing(JComponent c, FontMetrics fm,259String string) {260if ((string == null) || (string.length() == 0)) {261return 0;262}263return getLeftSideBearing(c, fm, string.charAt(0));264}265266/**267* Returns the left side bearing of the first character of string. The268* left side bearing is calculated from the passed in FontMetrics.269*270* @param c JComponent that will display the string271* @param fm FontMetrics used to measure the String width272* @param firstChar Character to get the left side bearing for.273*/274public static int getLeftSideBearing(JComponent c, FontMetrics fm,275char firstChar) {276int charIndex = (int) firstChar;277if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) {278byte[] lsbs = null;279280FontRenderContext frc = getFontRenderContext(c, fm);281Font font = fm.getFont();282synchronized (SwingUtilities2.class) {283LSBCacheEntry entry = null;284if (searchKey == null) {285searchKey = new LSBCacheEntry(frc, font);286} else {287searchKey.reset(frc, font);288}289// See if we already have an entry for this pair290for (LSBCacheEntry cacheEntry : fontCache) {291if (searchKey.equals(cacheEntry)) {292entry = cacheEntry;293break;294}295}296if (entry == null) {297// No entry for this pair, add it.298entry = searchKey;299fontCache[nextIndex] = searchKey;300searchKey = null;301nextIndex = (nextIndex + 1) % CACHE_SIZE;302}303return entry.getLeftSideBearing(firstChar);304}305}306return 0;307}308309/**310* Returns the FontMetrics for the current Font of the passed311* in Graphics. This method is used when a Graphics312* is available, typically when painting. If a Graphics is not313* available the JComponent method of the same name should be used.314* <p>315* Callers should pass in a non-null JComponent, the exception316* to this is if a JComponent is not readily available at the time of317* painting.318* <p>319* This does not necessarily return the FontMetrics from the320* Graphics.321*322* @param c JComponent requesting FontMetrics, may be null323* @param g Graphics Graphics324*/325public static FontMetrics getFontMetrics(JComponent c, Graphics g) {326return getFontMetrics(c, g, g.getFont());327}328329330/**331* Returns the FontMetrics for the specified Font.332* This method is used when a Graphics is available, typically when333* painting. If a Graphics is not available the JComponent method of334* the same name should be used.335* <p>336* Callers should pass in a non-null JComonent, the exception337* to this is if a JComponent is not readily available at the time of338* painting.339* <p>340* This does not necessarily return the FontMetrics from the341* Graphics.342*343* @param c JComponent requesting FontMetrics, may be null344* @param c Graphics Graphics345* @param font Font to get FontMetrics for346*/347@SuppressWarnings("deprecation")348public static FontMetrics getFontMetrics(JComponent c, Graphics g,349Font font) {350if (c != null) {351// Note: We assume that we're using the FontMetrics352// from the widget to layout out text, otherwise we can get353// mismatches when printing.354return c.getFontMetrics(font);355}356return Toolkit.getDefaultToolkit().getFontMetrics(font);357}358359360/**361* Returns the width of the passed in String.362* If the passed String is {@code null}, returns zero.363*364* @param c JComponent that will display the string, may be null365* @param fm FontMetrics used to measure the String width366* @param string String to get the width of367*/368public static int stringWidth(JComponent c, FontMetrics fm, String string) {369return (int) stringWidth(c, fm, string, false);370}371372/**373* Returns the width of the passed in String.374* If the passed String is {@code null}, returns zero.375*376* @param c JComponent that will display the string, may be null377* @param fm FontMetrics used to measure the String width378* @param string String to get the width of379* @param useFPAPI use floating point API380*/381public static float stringWidth(JComponent c, FontMetrics fm, String string,382boolean useFPAPI){383if (string == null || string.isEmpty()) {384return 0;385}386boolean needsTextLayout = ((c != null) &&387(c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));388if (needsTextLayout) {389synchronized(charsBufferLock) {390int length = syncCharsBuffer(string);391needsTextLayout = isComplexLayout(charsBuffer, 0, length);392}393}394if (needsTextLayout) {395TextLayout layout = createTextLayout(c, string,396fm.getFont(), fm.getFontRenderContext());397return layout.getAdvance();398} else {399return getFontStringWidth(string, fm, useFPAPI);400}401}402403404/**405* Clips the passed in String to the space provided.406*407* @param c JComponent that will display the string, may be null408* @param fm FontMetrics used to measure the String width409* @param string String to display410* @param availTextWidth Amount of space that the string can be drawn in411* @return Clipped string that can fit in the provided space.412*/413public static String clipStringIfNecessary(JComponent c, FontMetrics fm,414String string,415int availTextWidth) {416if (string == null || string.isEmpty()) {417return "";418}419int textWidth = SwingUtilities2.stringWidth(c, fm, string);420if (textWidth > availTextWidth) {421return SwingUtilities2.clipString(c, fm, string, availTextWidth);422}423return string;424}425426427/**428* Clips the passed in String to the space provided. NOTE: this assumes429* the string does not fit in the available space.430*431* @param c JComponent that will display the string, may be null432* @param fm FontMetrics used to measure the String width433* @param string String to display434* @param availTextWidth Amount of space that the string can be drawn in435* @return Clipped string that can fit in the provided space.436*/437public static String clipString(JComponent c, FontMetrics fm,438String string, int availTextWidth) {439// c may be null here.440String clipString = "...";441availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString);442if (availTextWidth <= 0) {443//can not fit any characters444return clipString;445}446447boolean needsTextLayout;448synchronized (charsBufferLock) {449int stringLength = syncCharsBuffer(string);450needsTextLayout =451isComplexLayout(charsBuffer, 0, stringLength);452if (!needsTextLayout) {453int width = 0;454for (int nChars = 0; nChars < stringLength; nChars++) {455width += fm.charWidth(charsBuffer[nChars]);456if (width > availTextWidth) {457string = string.substring(0, nChars);458break;459}460}461}462}463if (needsTextLayout) {464AttributedString aString = new AttributedString(string);465if (c != null) {466aString.addAttribute(TextAttribute.NUMERIC_SHAPING,467c.getClientProperty(TextAttribute.NUMERIC_SHAPING));468}469LineBreakMeasurer measurer = new LineBreakMeasurer(470aString.getIterator(), BreakIterator.getCharacterInstance(),471getFontRenderContext(c, fm));472string = string.substring(0, measurer.nextOffset(availTextWidth));473474}475return string + clipString;476}477478479/**480* Draws the string at the specified location.481*482* @param c JComponent that will display the string, may be null483* @param g Graphics to draw the text to484* @param text String to display485* @param x X coordinate to draw the text at486* @param y Y coordinate to draw the text at487*/488public static void drawString(JComponent c, Graphics g, String text,489int x, int y) {490drawString(c, g, text, x, y, false);491}492493/**494* Draws the string at the specified location.495*496* @param c JComponent that will display the string, may be null497* @param g Graphics to draw the text to498* @param text String to display499* @param x X coordinate to draw the text at500* @param y Y coordinate to draw the text at501* @param useFPAPI use floating point API502*/503public static void drawString(JComponent c, Graphics g, String text,504float x, float y, boolean useFPAPI) {505// c may be null506507// All non-editable widgets that draw strings call into this508// methods. By non-editable that means widgets like JLabel, JButton509// but NOT JTextComponents.510if ( text == null || text.length() <= 0 ) { //no need to paint empty strings511return;512}513if (isPrinting(g)) {514Graphics2D g2d = getGraphics2D(g);515if (g2d != null) {516/* The printed text must scale linearly with the UI.517* Calculate the width on screen, obtain a TextLayout with518* advances for the printer graphics FRC, and then justify519* it to fit in the screen width. This distributes the spacing520* more evenly than directly laying out to the screen advances.521*/522String trimmedText = trimTrailingSpaces(text);523if (!trimmedText.isEmpty()) {524float screenWidth = (float) g2d.getFont().getStringBounds525(trimmedText, getFontRenderContext(c)).getWidth();526TextLayout layout = createTextLayout(c, text, g2d.getFont(),527g2d.getFontRenderContext());528529// If text fits the screenWidth, then do not need to justify530if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),531trimmedText) > screenWidth) {532layout = layout.getJustifiedLayout(screenWidth);533}534/* Use alternate print color if specified */535Color col = g2d.getColor();536if (col instanceof PrintColorUIResource) {537g2d.setColor(((PrintColorUIResource)col).getPrintColor());538}539540layout.draw(g2d, x, y);541542g2d.setColor(col);543}544545return;546}547}548549// If we get here we're not printing550if (g instanceof Graphics2D) {551Graphics2D g2 = (Graphics2D)g;552553boolean needsTextLayout = ((c != null) &&554(c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));555556if (needsTextLayout) {557synchronized(charsBufferLock) {558int length = syncCharsBuffer(text);559needsTextLayout = isComplexLayout(charsBuffer, 0, length);560}561}562563Object aaHint = (c == null)564? null565: c.getClientProperty(KEY_TEXT_ANTIALIASING);566if (aaHint != null) {567Object oldContrast = null;568Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);569if (aaHint != oldAAValue) {570g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint);571} else {572oldAAValue = null;573}574575Object lcdContrastHint = c.getClientProperty(576KEY_TEXT_LCD_CONTRAST);577if (lcdContrastHint != null) {578oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);579if (lcdContrastHint.equals(oldContrast)) {580oldContrast = null;581} else {582g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,583lcdContrastHint);584}585}586587if (needsTextLayout) {588TextLayout layout = createTextLayout(c, text, g2.getFont(),589g2.getFontRenderContext());590layout.draw(g2, x, y);591} else {592g2.drawString(text, x, y);593}594595if (oldAAValue != null) {596g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);597}598if (oldContrast != null) {599g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);600}601602return;603}604605if (needsTextLayout){606TextLayout layout = createTextLayout(c, text, g2.getFont(),607g2.getFontRenderContext());608layout.draw(g2, x, y);609return;610}611}612613g.drawString(text, (int) x, (int) y);614}615616/**617* Draws the string at the specified location underlining the specified618* character.619*620* @param c JComponent that will display the string, may be null621* @param g Graphics to draw the text to622* @param text String to display623* @param underlinedIndex Index of a character in the string to underline624* @param x X coordinate to draw the text at625* @param y Y coordinate to draw the text at626*/627628public static void drawStringUnderlineCharAt(JComponent c,Graphics g,629String text, int underlinedIndex, int x, int y) {630drawStringUnderlineCharAt(c, g, text, underlinedIndex, x, y, false);631}632/**633* Draws the string at the specified location underlining the specified634* character.635*636* @param c JComponent that will display the string, may be null637* @param g Graphics to draw the text to638* @param text String to display639* @param underlinedIndex Index of a character in the string to underline640* @param x X coordinate to draw the text at641* @param y Y coordinate to draw the text at642* @param useFPAPI use floating point API643*/644public static void drawStringUnderlineCharAt(JComponent c, Graphics g,645String text, int underlinedIndex,646float x, float y,647boolean useFPAPI) {648if (text == null || text.length() <= 0) {649return;650}651SwingUtilities2.drawString(c, g, text, x, y, useFPAPI);652int textLength = text.length();653if (underlinedIndex >= 0 && underlinedIndex < textLength ) {654float underlineRectY = y;655int underlineRectHeight = 1;656float underlineRectX = 0;657int underlineRectWidth = 0;658boolean isPrinting = isPrinting(g);659boolean needsTextLayout = isPrinting;660if (!needsTextLayout) {661synchronized (charsBufferLock) {662syncCharsBuffer(text);663needsTextLayout =664isComplexLayout(charsBuffer, 0, textLength);665}666}667if (!needsTextLayout) {668FontMetrics fm = g.getFontMetrics();669underlineRectX = x +670SwingUtilities2.stringWidth(c,fm,671text.substring(0,underlinedIndex));672underlineRectWidth = fm.charWidth(text.673charAt(underlinedIndex));674} else {675Graphics2D g2d = getGraphics2D(g);676if (g2d != null) {677TextLayout layout =678createTextLayout(c, text, g2d.getFont(),679g2d.getFontRenderContext());680if (isPrinting) {681float screenWidth = (float)g2d.getFont().682getStringBounds(text, getFontRenderContext(c)).getWidth();683// If text fits the screenWidth, then do not need to justify684if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),685text) > screenWidth) {686layout = layout.getJustifiedLayout(screenWidth);687}688}689TextHitInfo leading =690TextHitInfo.leading(underlinedIndex);691TextHitInfo trailing =692TextHitInfo.trailing(underlinedIndex);693Shape shape =694layout.getVisualHighlightShape(leading, trailing);695Rectangle rect = shape.getBounds();696underlineRectX = x + rect.x;697underlineRectWidth = rect.width;698}699}700g.fillRect((int) underlineRectX, (int) underlineRectY + 1,701underlineRectWidth, underlineRectHeight);702}703}704705706/**707* A variation of locationToIndex() which only returns an index if the708* Point is within the actual bounds of a list item (not just in the cell)709* and if the JList has the "List.isFileList" client property set.710* Otherwise, this method returns -1.711* This is used to make Windows {@literal L&F} JFileChooser act712* like native dialogs.713*/714public static int loc2IndexFileList(JList<?> list, Point point) {715int index = list.locationToIndex(point);716if (index != -1) {717Object bySize = list.getClientProperty("List.isFileList");718if (bySize instanceof Boolean && ((Boolean)bySize).booleanValue() &&719!pointIsInActualBounds(list, index, point)) {720index = -1;721}722}723return index;724}725726727/**728* Returns true if the given point is within the actual bounds of the729* JList item at index (not just inside the cell).730*/731private static <T> boolean pointIsInActualBounds(JList<T> list, int index,732Point point) {733ListCellRenderer<? super T> renderer = list.getCellRenderer();734T value = list.getModel().getElementAt(index);735Component item = renderer.getListCellRendererComponent(list,736value, index, false, false);737Dimension itemSize = item.getPreferredSize();738Rectangle cellBounds = list.getCellBounds(index, index);739if (!item.getComponentOrientation().isLeftToRight()) {740cellBounds.x += (cellBounds.width - itemSize.width);741}742cellBounds.width = itemSize.width;743744return cellBounds.contains(point);745}746747748/**749* Returns true if the given point is outside the preferredSize of the750* item at the given row of the table. (Column must be 0).751* Does not check the "Table.isFileList" property. That should be checked752* before calling this method.753* This is used to make Windows {@literal L&F} JFileChooser act754* like native dialogs.755*/756public static boolean pointOutsidePrefSize(JTable table, int row, int column, Point p) {757if (table.convertColumnIndexToModel(column) != 0 || row == -1) {758return true;759}760TableCellRenderer tcr = table.getCellRenderer(row, column);761Object value = table.getValueAt(row, column);762Component cell = tcr.getTableCellRendererComponent(table, value, false,763false, row, column);764Dimension itemSize = cell.getPreferredSize();765Rectangle cellBounds = table.getCellRect(row, column, false);766cellBounds.width = itemSize.width;767cellBounds.height = itemSize.height;768769// See if coords are inside770// ASSUME: mouse x,y will never be < cell's x,y771assert (p.x >= cellBounds.x && p.y >= cellBounds.y);772return p.x > cellBounds.x + cellBounds.width ||773p.y > cellBounds.y + cellBounds.height;774}775776/**777* Set the lead and anchor without affecting selection.778*/779public static void setLeadAnchorWithoutSelection(ListSelectionModel model,780int lead, int anchor) {781if (anchor == -1) {782anchor = lead;783}784if (lead == -1) {785model.setAnchorSelectionIndex(-1);786model.setLeadSelectionIndex(-1);787} else {788if (model.isSelectedIndex(lead)) {789model.addSelectionInterval(lead, lead);790} else {791model.removeSelectionInterval(lead, lead);792}793model.setAnchorSelectionIndex(anchor);794}795}796797/**798* Ignore mouse events if the component is null, not enabled, the event799* is not associated with the left mouse button, or the event has been800* consumed.801*/802public static boolean shouldIgnore(MouseEvent me, JComponent c) {803return c == null || !c.isEnabled()804|| !SwingUtilities.isLeftMouseButton(me)805|| me.isConsumed();806}807808/**809* Request focus on the given component if it doesn't already have it810* and {@code isRequestFocusEnabled()} returns true.811*/812public static void adjustFocus(JComponent c) {813if (!c.hasFocus() && c.isRequestFocusEnabled()) {814c.requestFocus();815}816}817818/**819* The following draw functions have the same semantic as the820* Graphics methods with the same names.821*822* this is used for printing823*/824public static int drawChars(JComponent c, Graphics g,825char[] data,826int offset,827int length,828int x,829int y) {830return (int) drawChars(c, g, data, offset, length, x, y, false);831}832833public static float drawChars(JComponent c, Graphics g,834char[] data,835int offset,836int length,837float x,838float y) {839return drawChars(c, g, data, offset, length, x, y, true);840}841842public static float drawChars(JComponent c, Graphics g,843char[] data,844int offset,845int length,846float x,847float y,848boolean useFPAPI) {849if ( length <= 0 ) { //no need to paint empty strings850return x;851}852float nextX = x + getFontCharsWidth(data, offset, length,853getFontMetrics(c, g),854useFPAPI);855if (isPrinting(g)) {856Graphics2D g2d = getGraphics2D(g);857if (g2d != null) {858FontRenderContext deviceFontRenderContext = g2d.859getFontRenderContext();860FontRenderContext frc = getFontRenderContext(c);861if (frc != null &&862!isFontRenderContextPrintCompatible863(deviceFontRenderContext, frc)) {864865String text = new String(data, offset, length);866TextLayout layout = new TextLayout(text, g2d.getFont(),867deviceFontRenderContext);868String trimmedText = trimTrailingSpaces(text);869if (!trimmedText.isEmpty()) {870float screenWidth = (float)g2d.getFont().871getStringBounds(trimmedText, frc).getWidth();872// If text fits the screenWidth, then do not need to justify873if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),874trimmedText) > screenWidth) {875layout = layout.getJustifiedLayout(screenWidth);876}877878/* Use alternate print color if specified */879Color col = g2d.getColor();880if (col instanceof PrintColorUIResource) {881g2d.setColor(((PrintColorUIResource)col).getPrintColor());882}883884layout.draw(g2d,x,y);885886g2d.setColor(col);887}888889return nextX;890}891}892}893// Assume we're not printing if we get here, or that we are invoked894// via Swing text printing which is laid out for the printer.895Object aaHint = (c == null)896? null897: c.getClientProperty(KEY_TEXT_ANTIALIASING);898899if (!(g instanceof Graphics2D)) {900g.drawChars(data, offset, length, (int) x, (int) y);901return nextX;902}903904Graphics2D g2 = (Graphics2D) g;905if (aaHint != null) {906907Object oldContrast = null;908Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);909if (aaHint != null && aaHint != oldAAValue) {910g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint);911} else {912oldAAValue = null;913}914915Object lcdContrastHint = c.getClientProperty(KEY_TEXT_LCD_CONTRAST);916if (lcdContrastHint != null) {917oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);918if (lcdContrastHint.equals(oldContrast)) {919oldContrast = null;920} else {921g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,922lcdContrastHint);923}924}925926g2.drawString(new String(data, offset, length), x, y);927928if (oldAAValue != null) {929g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);930}931if (oldContrast != null) {932g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);933}934}935else {936g2.drawString(new String(data, offset, length), x, y);937}938return nextX;939}940941public static float getFontCharWidth(char c, FontMetrics fm,942boolean useFPAPI)943{944return getFontCharsWidth(new char[]{c}, 0, 1, fm, useFPAPI);945}946947public static float getFontCharsWidth(char[] data, int offset, int len,948FontMetrics fm,949boolean useFPAPI)950{951if (len == 0) {952return 0;953}954if (useFPAPI) {955Rectangle2D bounds = fm.getFont().956getStringBounds(data, offset, offset + len,957fm.getFontRenderContext());958return (float) bounds.getWidth();959} else {960return fm.charsWidth(data, offset, len);961}962}963964public static float getFontStringWidth(String data, FontMetrics fm,965boolean useFPAPI)966{967if (useFPAPI) {968Rectangle2D bounds = fm.getFont()969.getStringBounds(data, fm.getFontRenderContext());970return (float) bounds.getWidth();971} else {972return fm.stringWidth(data);973}974}975976/*977* see documentation for drawChars978* returns the advance979*/980public static float drawString(JComponent c, Graphics g,981AttributedCharacterIterator iterator,982int x, int y)983{984return drawStringImpl(c, g, iterator, x, y);985}986987public static float drawString(JComponent c, Graphics g,988AttributedCharacterIterator iterator,989float x, float y)990{991return drawStringImpl(c, g, iterator, x, y);992}993994private static float drawStringImpl(JComponent c, Graphics g,995AttributedCharacterIterator iterator,996float x, float y)997{998999float retVal;1000boolean isPrinting = isPrinting(g);1001Color col = g.getColor();10021003if (isPrinting) {1004/* Use alternate print color if specified */1005if (col instanceof PrintColorUIResource) {1006g.setColor(((PrintColorUIResource)col).getPrintColor());1007}1008}10091010Graphics2D g2d = getGraphics2D(g);1011if (g2d == null) {1012g.drawString(iterator, (int)x, (int)y); //for the cases where advance1013//matters it should not happen1014retVal = x;10151016} else {1017FontRenderContext frc;1018if (isPrinting) {1019frc = getFontRenderContext(c);1020if (frc.isAntiAliased() || frc.usesFractionalMetrics()) {1021frc = new FontRenderContext(frc.getTransform(), false, false);1022}1023} else if ((frc = getFRCProperty(c)) != null) {1024/* frc = frc; ! */1025} else {1026frc = g2d.getFontRenderContext();1027}1028TextLayout layout;1029if (isPrinting) {1030FontRenderContext deviceFRC = g2d.getFontRenderContext();1031if (!isFontRenderContextPrintCompatible(frc, deviceFRC)) {1032layout = new TextLayout(iterator, deviceFRC);1033AttributedCharacterIterator trimmedIt =1034getTrimmedTrailingSpacesIterator(iterator);1035if (trimmedIt != null) {1036float screenWidth = new TextLayout(trimmedIt, frc).1037getAdvance();1038layout = layout.getJustifiedLayout(screenWidth);1039}1040} else {1041layout = new TextLayout(iterator, frc);1042}1043} else {1044layout = new TextLayout(iterator, frc);1045}1046layout.draw(g2d, x, y);1047retVal = layout.getAdvance();1048}10491050if (isPrinting) {1051g.setColor(col);1052}10531054return retVal;1055}10561057/**1058* This method should be used for drawing a borders over a filled rectangle.1059* Draws vertical line, using the current color, between the points {@code1060* (x, y1)} and {@code (x, y2)} in graphics context's coordinate system.1061* Note: it use {@code Graphics.fillRect()} internally.1062*1063* @param g Graphics to draw the line to.1064* @param x the <i>x</i> coordinate.1065* @param y1 the first point's <i>y</i> coordinate.1066* @param y2 the second point's <i>y</i> coordinate.1067*/1068public static void drawVLine(Graphics g, int x, int y1, int y2) {1069if (y2 < y1) {1070final int temp = y2;1071y2 = y1;1072y1 = temp;1073}1074g.fillRect(x, y1, 1, y2 - y1 + 1);1075}10761077/**1078* This method should be used for drawing a borders over a filled rectangle.1079* Draws horizontal line, using the current color, between the points {@code1080* (x1, y)} and {@code (x2, y)} in graphics context's coordinate system.1081* Note: it use {@code Graphics.fillRect()} internally.1082*1083* @param g Graphics to draw the line to.1084* @param x1 the first point's <i>x</i> coordinate.1085* @param x2 the second point's <i>x</i> coordinate.1086* @param y the <i>y</i> coordinate.1087*/1088public static void drawHLine(Graphics g, int x1, int x2, int y) {1089if (x2 < x1) {1090final int temp = x2;1091x2 = x1;1092x1 = temp;1093}1094g.fillRect(x1, y, x2 - x1 + 1, 1);1095}10961097/**1098* This method should be used for drawing a borders over a filled rectangle.1099* Draws the outline of the specified rectangle. The left and right edges of1100* the rectangle are at {@code x} and {@code x + w}. The top and bottom1101* edges are at {@code y} and {@code y + h}. The rectangle is drawn using1102* the graphics context's current color. Note: it use {@code1103* Graphics.fillRect()} internally.1104*1105* @param g Graphics to draw the rectangle to.1106* @param x the <i>x</i> coordinate of the rectangle to be drawn.1107* @param y the <i>y</i> coordinate of the rectangle to be drawn.1108* @param w the w of the rectangle to be drawn.1109* @param h the h of the rectangle to be drawn.1110* @see SwingUtilities2#drawVLine(java.awt.Graphics, int, int, int)1111* @see SwingUtilities2#drawHLine(java.awt.Graphics, int, int, int)1112*/1113public static void drawRect(Graphics g, int x, int y, int w, int h) {1114if (w < 0 || h < 0) {1115return;1116}11171118if (h == 0 || w == 0) {1119g.fillRect(x, y, w + 1, h + 1);1120} else {1121g.fillRect(x, y, w, 1);1122g.fillRect(x + w, y, 1, h);1123g.fillRect(x + 1, y + h, w, 1);1124g.fillRect(x, y + 1, 1, h);1125}1126}11271128private static TextLayout createTextLayout(JComponent c, String s,1129Font f, FontRenderContext frc) {1130Object shaper = (c == null ?1131null : c.getClientProperty(TextAttribute.NUMERIC_SHAPING));1132if (shaper == null) {1133return new TextLayout(s, f, frc);1134} else {1135Map<TextAttribute, Object> a = new HashMap<TextAttribute, Object>();1136a.put(TextAttribute.FONT, f);1137a.put(TextAttribute.NUMERIC_SHAPING, shaper);1138return new TextLayout(s, a, frc);1139}1140}11411142/*1143* Checks if two given FontRenderContexts are compatible for printing.1144* We can't just use equals as we want to exclude from the comparison :1145* + whether AA is set as irrelevant for printing and shouldn't affect1146* printed metrics anyway1147* + any translation component in the transform of either FRC, as it1148* does not affect metrics.1149* Compatible means no special handling needed for text painting1150*/1151private static boolean1152isFontRenderContextPrintCompatible(FontRenderContext frc1,1153FontRenderContext frc2) {11541155if (frc1 == frc2) {1156return true;1157}11581159if (frc1 == null || frc2 == null) { // not supposed to happen1160return false;1161}11621163if (frc1.getFractionalMetricsHint() !=1164frc2.getFractionalMetricsHint()) {1165return false;1166}11671168/* If both are identity, return true */1169if (!frc1.isTransformed() && !frc2.isTransformed()) {1170return true;1171}11721173/* That's the end of the cheap tests, need to get and compare1174* the transform matrices. We don't care about the translation1175* components, so return true if they are otherwise identical.1176*/1177double[] mat1 = new double[4];1178double[] mat2 = new double[4];1179frc1.getTransform().getMatrix(mat1);1180frc2.getTransform().getMatrix(mat2);1181return1182mat1[0] == mat2[0] &&1183mat1[1] == mat2[1] &&1184mat1[2] == mat2[2] &&1185mat1[3] == mat2[3];1186}11871188/*1189* Tries it best to get Graphics2D out of the given Graphics1190* returns null if can not derive it.1191*/1192public static Graphics2D getGraphics2D(Graphics g) {1193if (g instanceof Graphics2D) {1194return (Graphics2D) g;1195} else if (g instanceof ProxyPrintGraphics) {1196return (Graphics2D)(((ProxyPrintGraphics)g).getGraphics());1197} else {1198return null;1199}1200}12011202/*1203* Returns FontRenderContext associated with Component.1204* FontRenderContext from Component.getFontMetrics is associated1205* with the component.1206*1207* Uses Component.getFontMetrics to get the FontRenderContext from.1208* see JComponent.getFontMetrics and TextLayoutStrategy.java1209*/1210public static FontRenderContext getFontRenderContext(Component c) {1211assert c != null;1212if (c == null) {1213return DEFAULT_FRC;1214} else {1215return c.getFontMetrics(c.getFont()).getFontRenderContext();1216}1217}12181219/**1220* A convenience method to get FontRenderContext.1221* Returns the FontRenderContext for the passed in FontMetrics or1222* for the passed in Component if FontMetrics is null1223*/1224private static FontRenderContext getFontRenderContext(Component c, FontMetrics fm) {1225assert fm != null || c!= null;1226return (fm != null) ? fm.getFontRenderContext()1227: getFontRenderContext(c);1228}12291230/*1231* This method is to be used only for JComponent.getFontMetrics.1232* In all other places to get FontMetrics we need to use1233* JComponent.getFontMetrics.1234*1235*/1236public static FontMetrics getFontMetrics(JComponent c, Font font) {1237FontRenderContext frc = getFRCProperty(c);1238if (frc == null) {1239frc = DEFAULT_FRC;1240}1241return FontDesignMetrics.getMetrics(font, frc);1242}124312441245/* Get any FontRenderContext associated with a JComponent1246* - may return null1247*/1248private static FontRenderContext getFRCProperty(JComponent c) {1249if (c != null) {12501251GraphicsConfiguration gc = c.getGraphicsConfiguration();1252AffineTransform tx = (gc == null) ? null : gc.getDefaultTransform();1253Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING);1254return getFRCFromCache(tx, aaHint);1255}1256return null;1257}12581259private static final Object APP_CONTEXT_FRC_CACHE_KEY = new Object();12601261private static FontRenderContext getFRCFromCache(AffineTransform tx,1262Object aaHint) {1263if (tx == null && aaHint == null) {1264return null;1265}12661267@SuppressWarnings("unchecked")1268Map<Object, FontRenderContext> cache = (Map<Object, FontRenderContext>)1269AppContext.getAppContext().get(APP_CONTEXT_FRC_CACHE_KEY);12701271if (cache == null) {1272cache = new HashMap<>();1273AppContext.getAppContext().put(APP_CONTEXT_FRC_CACHE_KEY, cache);1274}12751276Object key = (tx == null)1277? aaHint1278: (aaHint == null ? tx : new KeyPair(tx, aaHint));12791280FontRenderContext frc = cache.get(key);1281if (frc == null) {1282aaHint = (aaHint == null) ? VALUE_TEXT_ANTIALIAS_OFF : aaHint;1283frc = new FontRenderContext(tx, aaHint,1284VALUE_FRACTIONALMETRICS_DEFAULT);1285cache.put(key, frc);1286}1287return frc;1288}12891290private static class KeyPair {12911292private final Object key1;1293private final Object key2;12941295public KeyPair(Object key1, Object key2) {1296this.key1 = key1;1297this.key2 = key2;1298}12991300@Override1301public boolean equals(Object obj) {1302if (!(obj instanceof KeyPair)) {1303return false;1304}1305KeyPair that = (KeyPair) obj;1306return this.key1.equals(that.key1) && this.key2.equals(that.key2);1307}13081309@Override1310public int hashCode() {1311return key1.hashCode() + 37 * key2.hashCode();1312}1313}13141315/*1316* returns true if the Graphics is print Graphics1317* false otherwise1318*/1319static boolean isPrinting(Graphics g) {1320return (g instanceof PrinterGraphics || g instanceof PrintGraphics);1321}13221323private static String trimTrailingSpaces(String s) {1324int i = s.length() - 1;1325while(i >= 0 && Character.isWhitespace(s.charAt(i))) {1326i--;1327}1328return s.substring(0, i + 1);1329}13301331private static AttributedCharacterIterator getTrimmedTrailingSpacesIterator1332(AttributedCharacterIterator iterator) {1333int curIdx = iterator.getIndex();13341335char c = iterator.last();1336while(c != CharacterIterator.DONE && Character.isWhitespace(c)) {1337c = iterator.previous();1338}13391340if (c != CharacterIterator.DONE) {1341int endIdx = iterator.getIndex();13421343if (endIdx == iterator.getEndIndex() - 1) {1344iterator.setIndex(curIdx);1345return iterator;1346} else {1347AttributedString trimmedText = new AttributedString(iterator,1348iterator.getBeginIndex(), endIdx + 1);1349return trimmedText.getIterator();1350}1351} else {1352return null;1353}1354}13551356/**1357* Determines whether the SelectedTextColor should be used for painting text1358* foreground for the specified highlight.1359*1360* Returns true only if the highlight painter for the specified highlight1361* is the swing painter (whether inner class of javax.swing.text.DefaultHighlighter1362* or com.sun.java.swing.plaf.windows.WindowsTextUI) and its background color1363* is null or equals to the selection color of the text component.1364*1365* This is a hack for fixing both bugs 4761990 and 50032941366*/1367public static boolean useSelectedTextColor(Highlighter.Highlight h, JTextComponent c) {1368Highlighter.HighlightPainter painter = h.getPainter();1369String painterClass = painter.getClass().getName();1370if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&1371painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {1372return false;1373}1374try {1375DefaultHighlighter.DefaultHighlightPainter defPainter =1376(DefaultHighlighter.DefaultHighlightPainter) painter;1377if (defPainter.getColor() != null &&1378!defPainter.getColor().equals(c.getSelectionColor())) {1379return false;1380}1381} catch (ClassCastException e) {1382return false;1383}1384return true;1385}13861387/**1388* LSBCacheEntry is used to cache the left side bearing (lsb) for1389* a particular {@code Font} and {@code FontRenderContext}.1390* This only caches characters that fall in the range1391* {@code MIN_CHAR_INDEX} to {@code MAX_CHAR_INDEX}.1392*/1393private static class LSBCacheEntry {1394// Used to indicate a particular entry in lsb has not been set.1395private static final byte UNSET = Byte.MAX_VALUE;1396// Used in creating a GlyphVector to get the lsb1397private static final char[] oneChar = new char[1];13981399private byte[] lsbCache;1400private Font font;1401private FontRenderContext frc;140214031404public LSBCacheEntry(FontRenderContext frc, Font font) {1405lsbCache = new byte[MAX_CHAR_INDEX - MIN_CHAR_INDEX];1406reset(frc, font);14071408}14091410public void reset(FontRenderContext frc, Font font) {1411this.font = font;1412this.frc = frc;1413for (int counter = lsbCache.length - 1; counter >= 0; counter--) {1414lsbCache[counter] = UNSET;1415}1416}14171418public int getLeftSideBearing(char aChar) {1419int index = aChar - MIN_CHAR_INDEX;1420assert (index >= 0 && index < (MAX_CHAR_INDEX - MIN_CHAR_INDEX));1421byte lsb = lsbCache[index];1422if (lsb == UNSET) {1423oneChar[0] = aChar;1424GlyphVector gv = font.createGlyphVector(frc, oneChar);1425lsb = (byte) gv.getGlyphPixelBounds(0, frc, 0f, 0f).x;1426if (lsb < 0) {1427/* HRGB/HBGR LCD glyph images will always have a pixel1428* on the left used in colour fringe reduction.1429* Text rendering positions this correctly but here1430* we are using the glyph image to adjust that position1431* so must account for it.1432*/1433Object aaHint = frc.getAntiAliasingHint();1434if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||1435aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {1436lsb++;1437}1438}1439lsbCache[index] = lsb;1440}1441return lsb;144214431444}14451446public boolean equals(Object entry) {1447if (entry == this) {1448return true;1449}1450if (!(entry instanceof LSBCacheEntry)) {1451return false;1452}1453LSBCacheEntry oEntry = (LSBCacheEntry) entry;1454return (font.equals(oEntry.font) &&1455frc.equals(oEntry.frc));1456}14571458public int hashCode() {1459int result = 17;1460if (font != null) {1461result = 37 * result + font.hashCode();1462}1463if (frc != null) {1464result = 37 * result + frc.hashCode();1465}1466return result;1467}1468}14691470/*1471* here goes the fix for 4856343 [Problem with applet interaction1472* with system selection clipboard]1473*1474* NOTE. In case isTrustedContext() no checking1475* are to be performed1476*/14771478/**1479* checks the security permissions for accessing system clipboard1480*1481* for untrusted context (see isTrustedContext) checks the1482* permissions for the current event being handled1483*1484*/1485public static boolean canAccessSystemClipboard() {1486boolean canAccess = false;1487if (!GraphicsEnvironment.isHeadless()) {1488@SuppressWarnings("removal")1489SecurityManager sm = System.getSecurityManager();1490if (sm == null) {1491canAccess = true;1492} else {1493try {1494sm.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);1495canAccess = true;1496} catch (SecurityException e) {1497}1498if (canAccess && ! isTrustedContext()) {1499canAccess = canCurrentEventAccessSystemClipboard(true);1500}1501}1502}1503return canAccess;1504}1505/**1506* Returns true if EventQueue.getCurrentEvent() has the permissions to1507* access the system clipboard1508*/1509public static boolean canCurrentEventAccessSystemClipboard() {1510return isTrustedContext()1511|| canCurrentEventAccessSystemClipboard(false);1512}15131514/**1515* Returns true if the given event has permissions to access the1516* system clipboard1517*1518* @param e AWTEvent to check1519*/1520public static boolean canEventAccessSystemClipboard(AWTEvent e) {1521return isTrustedContext()1522|| canEventAccessSystemClipboard(e, false);1523}15241525/**1526* Returns true if the given event is corrent gesture for1527* accessing clipboard1528*1529* @param ie InputEvent to check1530*/1531@SuppressWarnings("deprecation")1532private static boolean isAccessClipboardGesture(InputEvent ie) {1533boolean allowedGesture = false;1534if (ie instanceof KeyEvent) { //we can validate only keyboard gestures1535KeyEvent ke = (KeyEvent)ie;1536int keyCode = ke.getKeyCode();1537int keyModifiers = ke.getModifiers();1538switch(keyCode) {1539case KeyEvent.VK_C:1540case KeyEvent.VK_V:1541case KeyEvent.VK_X:1542allowedGesture = (keyModifiers == InputEvent.CTRL_MASK);1543break;1544case KeyEvent.VK_INSERT:1545allowedGesture = (keyModifiers == InputEvent.CTRL_MASK ||1546keyModifiers == InputEvent.SHIFT_MASK);1547break;1548case KeyEvent.VK_COPY:1549case KeyEvent.VK_PASTE:1550case KeyEvent.VK_CUT:1551allowedGesture = true;1552break;1553case KeyEvent.VK_DELETE:1554allowedGesture = ( keyModifiers == InputEvent.SHIFT_MASK);1555break;1556}1557}1558return allowedGesture;1559}15601561/**1562* Returns true if e has the permissions to1563* access the system clipboard and if it is allowed gesture (if1564* checkGesture is true)1565*1566* @param e AWTEvent to check1567* @param checkGesture boolean1568*/1569private static boolean canEventAccessSystemClipboard(AWTEvent e,1570boolean checkGesture) {1571if (EventQueue.isDispatchThread()) {1572/*1573* Checking event permissions makes sense only for event1574* dispathing thread1575*/1576if (e instanceof InputEvent1577&& (! checkGesture || isAccessClipboardGesture((InputEvent)e))) {1578return AWTAccessor.getInputEventAccessor().1579canAccessSystemClipboard((InputEvent) e);1580} else {1581return false;1582}1583} else {1584return true;1585}1586}15871588/**1589* Utility method that throws SecurityException if SecurityManager is set1590* and modifiers are not public1591*1592* @param modifiers a set of modifiers1593*/1594@SuppressWarnings("removal")1595public static void checkAccess(int modifiers) {1596if (System.getSecurityManager() != null1597&& !Modifier.isPublic(modifiers)) {1598throw new SecurityException("Resource is not accessible");1599}1600}16011602/**1603* Returns true if EventQueue.getCurrentEvent() has the permissions to1604* access the system clipboard and if it is allowed gesture (if1605* checkGesture true)1606*1607* @param checkGesture boolean1608*/1609private static boolean canCurrentEventAccessSystemClipboard(boolean1610checkGesture) {1611AWTEvent event = EventQueue.getCurrentEvent();1612return canEventAccessSystemClipboard(event, checkGesture);1613}16141615/**1616* see RFE 5012841 [Per AppContect security permissions] for the1617* details1618*1619*/1620@SuppressWarnings("removal")1621private static boolean isTrustedContext() {1622return (System.getSecurityManager() == null)1623|| (AppContext.getAppContext().1624get(UntrustedClipboardAccess) == null);1625}16261627public static String displayPropertiesToCSS(Font font, Color fg) {1628StringBuilder rule = new StringBuilder("body {");1629if (font != null) {1630rule.append(" font-family: ");1631rule.append(font.getFamily());1632rule.append(" ; ");1633rule.append(" font-size: ");1634rule.append(font.getSize());1635rule.append("pt ;");1636if (font.isBold()) {1637rule.append(" font-weight: 700 ; ");1638}1639if (font.isItalic()) {1640rule.append(" font-style: italic ; ");1641}1642}1643if (fg != null) {1644rule.append(" color: #");1645if (fg.getRed() < 16) {1646rule.append('0');1647}1648rule.append(Integer.toHexString(fg.getRed()));1649if (fg.getGreen() < 16) {1650rule.append('0');1651}1652rule.append(Integer.toHexString(fg.getGreen()));1653if (fg.getBlue() < 16) {1654rule.append('0');1655}1656rule.append(Integer.toHexString(fg.getBlue()));1657rule.append(" ; ");1658}1659rule.append(" }");1660return rule.toString();1661}16621663/**1664* Utility method that creates a {@code UIDefaults.LazyValue} that1665* creates an {@code ImageIcon} {@code UIResource} for the1666* specified image file name. The image is loaded using1667* {@code getResourceAsStream}, starting with a call to that method1668* on the base class parameter. If it cannot be found, searching will1669* continue through the base class' inheritance hierarchy, up to and1670* including {@code rootClass}.1671*1672* @param baseClass the first class to use in searching for the resource1673* @param rootClass an ancestor of {@code baseClass} to finish the1674* search at1675* @param imageFile the name of the file to be found1676* @return a lazy value that creates the {@code ImageIcon}1677* {@code UIResource} for the image,1678* or null if it cannot be found1679*/1680public static Object makeIcon(final Class<?> baseClass,1681final Class<?> rootClass,1682final String imageFile) {1683return makeIcon(baseClass, rootClass, imageFile, true);1684}16851686/**1687* Utility method that creates a {@code UIDefaults.LazyValue} that1688* creates an {@code ImageIcon} {@code UIResource} for the1689* specified image file name. The image is loaded using1690* {@code getResourceAsStream}, starting with a call to that method1691* on the base class parameter. If it cannot be found, searching will1692* continue through the base class' inheritance hierarchy, up to and1693* including {@code rootClass}.1694*1695* Finds an image with a given name without privileges enabled.1696*1697* @param baseClass the first class to use in searching for the resource1698* @param rootClass an ancestor of {@code baseClass} to finish the1699* search at1700* @param imageFile the name of the file to be found1701* @return a lazy value that creates the {@code ImageIcon}1702* {@code UIResource} for the image,1703* or null if it cannot be found1704*/1705public static Object makeIcon_Unprivileged(final Class<?> baseClass,1706final Class<?> rootClass,1707final String imageFile) {1708return makeIcon(baseClass, rootClass, imageFile, false);1709}17101711private static Object makeIcon(final Class<?> baseClass,1712final Class<?> rootClass,1713final String imageFile,1714final boolean enablePrivileges) {1715return (UIDefaults.LazyValue) (table) -> {1716@SuppressWarnings("removal")1717byte[] buffer = enablePrivileges ? AccessController.doPrivileged(1718(PrivilegedAction<byte[]>) ()1719-> getIconBytes(baseClass, rootClass, imageFile))1720: getIconBytes(baseClass, rootClass, imageFile);17211722if (buffer == null) {1723return null;1724}1725if (buffer.length == 0) {1726System.err.println("warning: " + imageFile1727+ " is zero-length");1728return null;1729}17301731return new ImageIconUIResource(buffer);1732};1733}17341735private static byte[] getIconBytes(final Class<?> baseClass,1736final Class<?> rootClass,1737final String imageFile) {1738/* Copy resource into a byte array. This is1739* necessary because several browsers consider1740* Class.getResource a security risk because it1741* can be used to load additional classes.1742* Class.getResourceAsStream just returns raw1743* bytes, which we can convert to an image.1744*/1745Class<?> srchClass = baseClass;17461747while (srchClass != null) {17481749try (InputStream resource =1750srchClass.getResourceAsStream(imageFile)) {1751if (resource == null) {1752if (srchClass == rootClass) {1753break;1754}1755srchClass = srchClass.getSuperclass();1756continue;1757}17581759try (BufferedInputStream in = new BufferedInputStream(resource)) {1760return in.readAllBytes();1761}1762} catch (IOException ioe) {1763System.err.println(ioe.toString());1764}1765}1766return null;1767}17681769/* Used to help decide if AA text rendering should be used, so1770* this local display test should be additionally qualified1771* against whether we have XRender support on both ends of the wire,1772* as with that support remote performance may be good enough to turn1773* on by default. An additional complication there is XRender does not1774* appear capable of performing gamma correction needed for LCD text.1775*/1776public static boolean isLocalDisplay() {1777boolean isLocal;1778GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();1779if (ge instanceof SunGraphicsEnvironment) {1780isLocal = ((SunGraphicsEnvironment) ge).isDisplayLocal();1781} else {1782isLocal = true;1783}1784return isLocal;1785}17861787/**1788* Returns an integer from the defaults table. If {@code key} does1789* not map to a valid {@code Integer}, or can not be convered from1790* a {@code String} to an integer, the value 0 is returned.1791*1792* @param key an {@code Object} specifying the int.1793* @return the int1794*/1795public static int getUIDefaultsInt(Object key) {1796return getUIDefaultsInt(key, 0);1797}17981799/**1800* Returns an integer from the defaults table that is appropriate1801* for the given locale. If {@code key} does not map to a valid1802* {@code Integer}, or can not be convered from a {@code String}1803* to an integer, the value 0 is returned.1804*1805* @param key an {@code Object} specifying the int. Returned value1806* is 0 if {@code key} is not available,1807* @param l the {@code Locale} for which the int is desired1808* @return the int1809*/1810public static int getUIDefaultsInt(Object key, Locale l) {1811return getUIDefaultsInt(key, l, 0);1812}18131814/**1815* Returns an integer from the defaults table. If {@code key} does1816* not map to a valid {@code Integer}, or can not be convered from1817* a {@code String} to an integer, {@code default} is1818* returned.1819*1820* @param key an {@code Object} specifying the int. Returned value1821* is 0 if {@code key} is not available,1822* @param defaultValue Returned value if {@code key} is not available,1823* or is not an Integer1824* @return the int1825*/1826public static int getUIDefaultsInt(Object key, int defaultValue) {1827return getUIDefaultsInt(key, null, defaultValue);1828}18291830/**1831* Returns an integer from the defaults table that is appropriate1832* for the given locale. If {@code key} does not map to a valid1833* {@code Integer}, or can not be convered from a {@code String}1834* to an integer, {@code default} is returned.1835*1836* @param key an {@code Object} specifying the int. Returned value1837* is 0 if {@code key} is not available,1838* @param l the {@code Locale} for which the int is desired1839* @param defaultValue Returned value if {@code key} is not available,1840* or is not an Integer1841* @return the int1842*/1843public static int getUIDefaultsInt(Object key, Locale l, int defaultValue) {1844Object value = UIManager.get(key, l);18451846if (value instanceof Integer) {1847return ((Integer)value).intValue();1848}1849if (value instanceof String) {1850try {1851return Integer.parseInt((String)value);1852} catch (NumberFormatException nfe) {}1853}1854return defaultValue;1855}18561857// At this point we need this method here. But we assume that there1858// will be a common method for this purpose in the future releases.1859public static Component compositeRequestFocus(Component component) {1860if (component instanceof Container) {1861Container container = (Container)component;1862if (container.isFocusCycleRoot()) {1863FocusTraversalPolicy policy = container.getFocusTraversalPolicy();1864Component comp = policy.getDefaultComponent(container);1865if (comp!=null) {1866comp.requestFocus();1867return comp;1868}1869}1870Container rootAncestor = container.getFocusCycleRootAncestor();1871if (rootAncestor!=null) {1872FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();1873Component comp = policy.getComponentAfter(rootAncestor, container);18741875if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) {1876comp.requestFocus();1877return comp;1878}1879}1880}1881if (component.isFocusable()) {1882component.requestFocus();1883return component;1884}1885return null;1886}18871888/**1889* Change focus to the visible component in {@code JTabbedPane}.1890* This is not a general-purpose method and is here only to permit1891* sharing code.1892*/1893@SuppressWarnings("deprecation")1894public static boolean tabbedPaneChangeFocusTo(Component comp) {1895if (comp != null) {1896if (comp.isFocusTraversable()) {1897SwingUtilities2.compositeRequestFocus(comp);1898return true;1899} else if (comp instanceof JComponent1900&& ((JComponent)comp).requestDefaultFocus()) {19011902return true;1903}1904}19051906return false;1907}19081909/**1910* Submits a value-returning task for execution on the EDT and1911* returns a Future representing the pending results of the task.1912*1913* @param task the task to submit1914* @return a Future representing pending completion of the task1915* @throws NullPointerException if the task is null1916*/1917public static <V> Future<V> submit(Callable<V> task) {1918if (task == null) {1919throw new NullPointerException();1920}1921FutureTask<V> future = new FutureTask<V>(task);1922execute(future);1923return future;1924}19251926/**1927* Submits a Runnable task for execution on the EDT and returns a1928* Future representing that task.1929*1930* @param task the task to submit1931* @param result the result to return upon successful completion1932* @return a Future representing pending completion of the task,1933* and whose {@code get()} method will return the given1934* result value upon completion1935* @throws NullPointerException if the task is null1936*/1937public static <V> Future<V> submit(Runnable task, V result) {1938if (task == null) {1939throw new NullPointerException();1940}1941FutureTask<V> future = new FutureTask<V>(task, result);1942execute(future);1943return future;1944}19451946/**1947* Sends a Runnable to the EDT for the execution.1948*/1949private static void execute(Runnable command) {1950SwingUtilities.invokeLater(command);1951}19521953/**1954* Sets the {@code SKIP_CLICK_COUNT} client property on the component1955* if it is an instance of {@code JTextComponent} with a1956* {@code DefaultCaret}. This property, used for text components acting1957* as editors in a table or tree, tells {@code DefaultCaret} how many1958* clicks to skip before starting selection.1959*/1960public static void setSkipClickCount(Component comp, int count) {1961if (comp instanceof JTextComponent1962&& ((JTextComponent) comp).getCaret() instanceof DefaultCaret) {19631964((JTextComponent) comp).putClientProperty(SKIP_CLICK_COUNT, count);1965}1966}19671968/**1969* Return the MouseEvent's click count, possibly reduced by the value of1970* the component's {@code SKIP_CLICK_COUNT} client property. Clears1971* the {@code SKIP_CLICK_COUNT} property if the mouse event's click count1972* is 1. In order for clearing of the property to work correctly, there1973* must be a mousePressed implementation on the caller with this1974* call as the first line.1975*/1976public static int getAdjustedClickCount(JTextComponent comp, MouseEvent e) {1977int cc = e.getClickCount();19781979if (cc == 1) {1980comp.putClientProperty(SKIP_CLICK_COUNT, null);1981} else {1982Integer sub = (Integer) comp.getClientProperty(SKIP_CLICK_COUNT);1983if (sub != null) {1984return cc - sub;1985}1986}19871988return cc;1989}19901991/**1992* Used by the {@code liesIn} method to return which section1993* the point lies in.1994*1995* @see #liesIn1996*/1997public enum Section {19981999/** The leading section */2000LEADING,20012002/** The middle section */2003MIDDLE,20042005/** The trailing section */2006TRAILING2007}20082009/**2010* This method divides a rectangle into two or three sections along2011* the specified axis and determines which section the given point2012* lies in on that axis; used by drag and drop when calculating drop2013* locations.2014* <p>2015* For two sections, the rectangle is divided equally and the method2016* returns whether the point lies in {@code Section.LEADING} or2017* {@code Section.TRAILING}. For horizontal divisions, the calculation2018* respects component orientation.2019* <p>2020* For three sections, if the rectangle is greater than or equal to2021* 30 pixels in length along the axis, the calculation gives 10 pixels2022* to each of the leading and trailing sections and the remainder to the2023* middle. For smaller sizes, the rectangle is divided equally into three2024* sections.2025* <p>2026* Note: This method assumes that the point is within the bounds of2027* the given rectangle on the specified axis. However, in cases where2028* it isn't, the results still have meaning: {@code Section.MIDDLE}2029* remains the same, {@code Section.LEADING} indicates that the point2030* is in or somewhere before the leading section, and2031* {@code Section.TRAILING} indicates that the point is in or somewhere2032* after the trailing section.2033*2034* @param rect the rectangle2035* @param p the point the check2036* @param horizontal {@code true} to use the horizontal axis,2037* or {@code false} for the vertical axis2038* @param ltr {@code true} for left to right orientation,2039* or {@code false} for right to left orientation;2040* only used for horizontal calculations2041* @param three {@code true} for three sections,2042* or {@code false} for two2043*2044* @return the {@code Section} where the point lies2045*2046* @throws NullPointerException if {@code rect} or {@code p} are2047* {@code null}2048*/2049private static Section liesIn(Rectangle rect, Point p, boolean horizontal,2050boolean ltr, boolean three) {20512052/* beginning of the rectangle on the axis */2053int p0;20542055/* point on the axis we're interested in */2056int pComp;20572058/* length of the rectangle on the axis */2059int length;20602061/* value of ltr if horizontal, else true */2062boolean forward;20632064if (horizontal) {2065p0 = rect.x;2066pComp = p.x;2067length = rect.width;2068forward = ltr;2069} else {2070p0 = rect.y;2071pComp = p.y;2072length = rect.height;2073forward = true;2074}20752076if (three) {2077int boundary = (length >= 30) ? 10 : length / 3;20782079if (pComp < p0 + boundary) {2080return forward ? Section.LEADING : Section.TRAILING;2081} else if (pComp >= p0 + length - boundary) {2082return forward ? Section.TRAILING : Section.LEADING;2083}20842085return Section.MIDDLE;2086} else {2087int middle = p0 + length / 2;2088if (forward) {2089return pComp >= middle ? Section.TRAILING : Section.LEADING;2090} else {2091return pComp < middle ? Section.TRAILING : Section.LEADING;2092}2093}2094}20952096/**2097* This method divides a rectangle into two or three sections along2098* the horizontal axis and determines which section the given point2099* lies in; used by drag and drop when calculating drop locations.2100* <p>2101* See the documentation for {@link #liesIn} for more information2102* on how the section is calculated.2103*2104* @param rect the rectangle2105* @param p the point the check2106* @param ltr {@code true} for left to right orientation,2107* or {@code false} for right to left orientation2108* @param three {@code true} for three sections,2109* or {@code false} for two2110*2111* @return the {@code Section} where the point lies2112*2113* @throws NullPointerException if {@code rect} or {@code p} are2114* {@code null}2115*/2116public static Section liesInHorizontal(Rectangle rect, Point p,2117boolean ltr, boolean three) {2118return liesIn(rect, p, true, ltr, three);2119}21202121/**2122* This method divides a rectangle into two or three sections along2123* the vertical axis and determines which section the given point2124* lies in; used by drag and drop when calculating drop locations.2125* <p>2126* See the documentation for {@link #liesIn} for more information2127* on how the section is calculated.2128*2129* @param rect the rectangle2130* @param p the point the check2131* @param three {@code true} for three sections,2132* or {@code false} for two2133*2134* @return the {@code Section} where the point lies2135*2136* @throws NullPointerException if {@code rect} or {@code p} are2137* {@code null}2138*/2139public static Section liesInVertical(Rectangle rect, Point p,2140boolean three) {2141return liesIn(rect, p, false, false, three);2142}21432144/**2145* Maps the index of the column in the view at2146* {@code viewColumnIndex} to the index of the column2147* in the table model. Returns the index of the corresponding2148* column in the model. If {@code viewColumnIndex}2149* is less than zero, returns {@code viewColumnIndex}.2150*2151* @param cm the table model2152* @param viewColumnIndex the index of the column in the view2153* @return the index of the corresponding column in the model2154*2155* @see JTable#convertColumnIndexToModel(int)2156* @see javax.swing.plaf.basic.BasicTableHeaderUI2157*/2158public static int convertColumnIndexToModel(TableColumnModel cm,2159int viewColumnIndex) {2160if (viewColumnIndex < 0) {2161return viewColumnIndex;2162}2163return cm.getColumn(viewColumnIndex).getModelIndex();2164}21652166/**2167* Maps the index of the column in the {@code cm} at2168* {@code modelColumnIndex} to the index of the column2169* in the view. Returns the index of the2170* corresponding column in the view; returns {@code -1} if this column2171* is not being displayed. If {@code modelColumnIndex} is less than zero,2172* returns {@code modelColumnIndex}.2173*2174* @param cm the table model2175* @param modelColumnIndex the index of the column in the model2176* @return the index of the corresponding column in the view2177*2178* @see JTable#convertColumnIndexToView(int)2179* @see javax.swing.plaf.basic.BasicTableHeaderUI2180*/2181public static int convertColumnIndexToView(TableColumnModel cm,2182int modelColumnIndex) {2183if (modelColumnIndex < 0) {2184return modelColumnIndex;2185}2186for (int column = 0; column < cm.getColumnCount(); column++) {2187if (cm.getColumn(column).getModelIndex() == modelColumnIndex) {2188return column;2189}2190}2191return -1;2192}21932194/**2195* Sets the InputEvent.ALT_GRAPH mask on any modifier passed to the function2196* @param modifier the modifier passed2197* @return the modifier retiurned with ALT_GRAPH flag set2198*/2199public static int setAltGraphMask(int modifier) {2200return (modifier | InputEvent.ALT_GRAPH_DOWN_MASK);2201}22022203@SuppressWarnings("deprecation")2204public static int getSystemMnemonicKeyMask() {2205Toolkit toolkit = Toolkit.getDefaultToolkit();2206if (toolkit instanceof SunToolkit) {2207return ((SunToolkit) toolkit).getFocusAcceleratorKeyMask();2208}2209return InputEvent.ALT_MASK;2210}22112212/**2213* Returns the {@link TreePath} that identifies the changed nodes.2214*2215* @param event changes in a tree model2216* @param model corresponing tree model2217* @return the path to the changed nodes2218*/2219public static TreePath getTreePath(TreeModelEvent event, TreeModel model) {2220TreePath path = event.getTreePath();2221if ((path == null) && (model != null)) {2222Object root = model.getRoot();2223if (root != null) {2224path = new TreePath(root);2225}2226}2227return path;2228}22292230public static boolean isScaledGraphics(Graphics g) {2231if (g instanceof Graphics2D) {2232AffineTransform tx = ((Graphics2D) g).getTransform();2233return (tx.getType() & ~(TYPE_TRANSLATION | TYPE_FLIP)) != 0;2234}2235return false;2236}22372238/**2239* Enables the antialiasing rendering hint for the scaled graphics and2240* returns the previous hint value.2241* The returned null value indicates that the passed graphics is not2242* instance of Graphics2D.2243*2244* @param g the graphics2245* @return the previous antialiasing rendering hint value if the passed2246* graphics is instance of Graphics2D, null otherwise.2247*/2248public static Object getAndSetAntialisingHintForScaledGraphics(Graphics g) {2249if (isScaledGraphics(g) && isLocalDisplay()) {2250Graphics2D g2d = (Graphics2D) g;2251Object hint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);2252g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,2253RenderingHints.VALUE_ANTIALIAS_ON);2254return hint;2255}2256return null;2257}22582259/**2260* Sets the antialiasing rendering hint if its value is not null.2261* Null hint value indicates that the passed graphics is not instance of2262* Graphics2D.2263*2264* @param g the graphics2265* @param hint the antialiasing rendering hint2266*/2267public static void setAntialiasingHintForScaledGraphics(Graphics g, Object hint) {2268if (hint != null) {2269((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);2270}2271}22722273public static boolean isFloatingPointScale(AffineTransform tx) {2274int type = tx.getType() & ~(TYPE_FLIP | TYPE_TRANSLATION);2275if (type == 0) {2276return false;2277} else if ((type & ~TYPE_MASK_SCALE) == 0) {2278double scaleX = tx.getScaleX();2279double scaleY = tx.getScaleY();2280return (scaleX != (int) scaleX) || (scaleY != (int) scaleY);2281} else {2282return false;2283}2284}22852286/**2287* Returns the client property for the given key if it is set; otherwise2288* returns the {@L&F} property.2289*2290* @param component the component2291* @param key an {@code String} specifying the key for the desired boolean value2292* @return the boolean value of the client property if it is set or the {@L&F}2293* property in other case.2294*/2295public static boolean getBoolean(JComponent component, String key) {2296Object clientProperty = component.getClientProperty(key);22972298if (clientProperty instanceof Boolean) {2299return Boolean.TRUE.equals(clientProperty);2300}23012302return UIManager.getBoolean(key);2303}23042305/**2306* Used to listen to "blit" repaints in RepaintManager.2307*/2308public interface RepaintListener {2309void repaintPerformed(JComponent c, int x, int y, int w, int h);2310}23112312/**2313* Returns whether or not the scale used by {@code GraphicsConfiguration}2314* was changed.2315*2316* @param ev a {@code PropertyChangeEvent}2317* @return whether or not the scale was changed2318* @since 112319*/2320public static boolean isScaleChanged(final PropertyChangeEvent ev) {2321return isScaleChanged(ev.getPropertyName(), ev.getOldValue(),2322ev.getNewValue());2323}23242325/**2326* Returns whether or not the scale used by {@code GraphicsConfiguration}2327* was changed.2328*2329* @param name the name of the property2330* @param oldValue the old value of the property2331* @param newValue the new value of the property2332* @return whether or not the scale was changed2333* @since 112334*/2335public static boolean isScaleChanged(final String name,2336final Object oldValue,2337final Object newValue) {2338if (oldValue == newValue || !"graphicsConfiguration".equals(name)) {2339return false;2340}2341var newGC = (GraphicsConfiguration) oldValue;2342var oldGC = (GraphicsConfiguration) newValue;2343var newTx = newGC != null ? newGC.getDefaultTransform() : null;2344var oldTx = oldGC != null ? oldGC.getDefaultTransform() : null;2345return !Objects.equals(newTx, oldTx);2346}2347}234823492350