1 /*
   2  * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.swing;
  27 
  28 import java.awt.AWTEvent;
  29 import java.awt.Color;
  30 import java.awt.Component;
  31 import java.awt.Container;
  32 import java.awt.Dimension;
  33 import java.awt.EventQueue;
  34 import java.awt.FocusTraversalPolicy;
  35 import java.awt.Font;
  36 import java.awt.FontMetrics;
  37 import java.awt.Graphics;
  38 import java.awt.Graphics2D;
  39 import java.awt.GraphicsConfiguration;
  40 import java.awt.GraphicsEnvironment;
  41 import java.awt.Point;
  42 import java.awt.PrintGraphics;
  43 import java.awt.Rectangle;
  44 import java.awt.RenderingHints;
  45 import java.awt.Shape;
  46 import java.awt.Toolkit;
  47 import java.awt.event.InputEvent;
  48 import java.awt.event.KeyEvent;
  49 import java.awt.event.MouseEvent;
  50 import java.awt.font.FontRenderContext;
  51 import java.awt.font.GlyphVector;
  52 import java.awt.font.LineBreakMeasurer;
  53 import java.awt.font.TextAttribute;
  54 import java.awt.font.TextHitInfo;
  55 import java.awt.font.TextLayout;
  56 import java.awt.geom.AffineTransform;
  57 import java.awt.geom.Rectangle2D;
  58 import java.awt.print.PrinterGraphics;
  59 import java.beans.PropertyChangeEvent;
  60 import java.io.BufferedInputStream;
  61 import java.io.ByteArrayOutputStream;
  62 import java.io.IOException;
  63 import java.io.InputStream;
  64 import java.lang.reflect.Modifier;
  65 import java.security.AccessController;
  66 import java.security.PrivilegedAction;
  67 import java.text.AttributedCharacterIterator;
  68 import java.text.AttributedString;
  69 import java.text.BreakIterator;
  70 import java.text.CharacterIterator;
  71 import java.util.HashMap;
  72 import java.util.Locale;
  73 import java.util.Map;
  74 import java.util.Objects;
  75 import java.util.concurrent.Callable;
  76 import java.util.concurrent.Future;
  77 import java.util.concurrent.FutureTask;
  78 
  79 import javax.swing.JComponent;
  80 import javax.swing.JList;
  81 import javax.swing.JTable;
  82 import javax.swing.ListCellRenderer;
  83 import javax.swing.ListSelectionModel;
  84 import javax.swing.SwingUtilities;
  85 import javax.swing.UIDefaults;
  86 import javax.swing.UIManager;
  87 import javax.swing.event.TreeModelEvent;
  88 import javax.swing.table.TableCellRenderer;
  89 import javax.swing.table.TableColumnModel;
  90 import javax.swing.text.DefaultCaret;
  91 import javax.swing.text.DefaultHighlighter;
  92 import javax.swing.text.Highlighter;
  93 import javax.swing.text.JTextComponent;
  94 import javax.swing.tree.TreeModel;
  95 import javax.swing.tree.TreePath;
  96 
  97 import sun.awt.AWTAccessor;
  98 import sun.awt.AWTPermissions;
  99 import sun.awt.AppContext;
 100 import sun.awt.SunToolkit;
 101 import sun.font.FontDesignMetrics;
 102 import sun.font.FontUtilities;
 103 import sun.java2d.SunGraphicsEnvironment;
 104 import sun.print.ProxyPrintGraphics;
 105 
 106 import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
 107 import static java.awt.RenderingHints.KEY_TEXT_LCD_CONTRAST;
 108 import static java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT;
 109 import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;
 110 import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;
 111 import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;
 112 import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
 113 import static java.awt.geom.AffineTransform.TYPE_FLIP;
 114 import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;
 115 import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
 116 
 117 /**
 118  * A collection of utility methods for Swing.
 119  * <p>
 120  * <b>WARNING:</b> While this class is public, it should not be treated as
 121  * public API and its API may change in incompatable ways between dot dot
 122  * releases and even patch releases. You should not rely on this class even
 123  * existing.
 124  *
 125  */
 126 public class SwingUtilities2 {
 127     /**
 128      * The {@code AppContext} key for our one {@code LAFState}
 129      * instance.
 130      */
 131     public static final Object LAF_STATE_KEY =
 132             new StringBuffer("LookAndFeel State");
 133 
 134     public static final Object MENU_SELECTION_MANAGER_LISTENER_KEY =
 135             new StringBuffer("MenuSelectionManager listener key");
 136 
 137     // Maintain a cache of CACHE_SIZE fonts and the left side bearing
 138      // of the characters falling into the range MIN_CHAR_INDEX to
 139      // MAX_CHAR_INDEX. The values in fontCache are created as needed.
 140      private static LSBCacheEntry[] fontCache;
 141      // Windows defines 6 font desktop properties, we will therefore only
 142      // cache the metrics for 6 fonts.
 143      private static final int CACHE_SIZE = 6;
 144      // nextIndex in fontCache to insert a font into.
 145      private static int nextIndex;
 146      // LSBCacheEntry used to search in fontCache to see if we already
 147      // have an entry for a particular font
 148      private static LSBCacheEntry searchKey;
 149 
 150      // getLeftSideBearing will consult all characters that fall in the
 151      // range MIN_CHAR_INDEX to MAX_CHAR_INDEX.
 152      private static final int MIN_CHAR_INDEX = (int)'W';
 153      private static final int MAX_CHAR_INDEX = (int)'W' + 1;
 154 
 155     public static final FontRenderContext DEFAULT_FRC =
 156         new FontRenderContext(null, false, false);
 157 
 158     /**
 159      * Attribute key for the content elements.  If it is set on an element, the
 160      * element is considered to be a line break.
 161      */
 162     public static final String IMPLIED_CR = "CR";
 163 
 164     /**
 165      * Used to tell a text component, being used as an editor for table
 166      * or tree, how many clicks it took to start editing.
 167      */
 168     private static final StringBuilder SKIP_CLICK_COUNT =
 169         new StringBuilder("skipClickCount");
 170 
 171     @SuppressWarnings("unchecked")
 172     public static void putAATextInfo(boolean lafCondition,
 173             Map<Object, Object> map) {
 174         SunToolkit.setAAFontSettingsCondition(lafCondition);
 175         Toolkit tk = Toolkit.getDefaultToolkit();
 176         Object desktopHints = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS);
 177 
 178         if (desktopHints instanceof Map) {
 179             Map<Object, Object> hints = (Map<Object, Object>) desktopHints;
 180             Object aaHint = hints.get(KEY_TEXT_ANTIALIASING);
 181             if (aaHint == null
 182                     || aaHint == VALUE_TEXT_ANTIALIAS_OFF
 183                     || aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) {
 184                 return;
 185             }
 186             map.put(KEY_TEXT_ANTIALIASING, aaHint);
 187             map.put(KEY_TEXT_LCD_CONTRAST, hints.get(KEY_TEXT_LCD_CONTRAST));
 188         }
 189     }
 190 
 191     /** Client Property key for the text maximal offsets for BasicMenuItemUI */
 192     public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET =
 193         new StringUIClientPropertyKey ("maxTextOffset");
 194 
 195     // security stuff
 196     private static final String UntrustedClipboardAccess =
 197         "UNTRUSTED_CLIPBOARD_ACCESS_KEY";
 198 
 199     //all access to  charsBuffer is to be synchronized on charsBufferLock
 200     private static final int CHAR_BUFFER_SIZE = 100;
 201     private static final Object charsBufferLock = new Object();
 202     private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE];
 203 
 204     static {
 205         fontCache = new LSBCacheEntry[CACHE_SIZE];
 206     }
 207 
 208     /**
 209      * Fill the character buffer cache.  Return the buffer length.
 210      */
 211     private static int syncCharsBuffer(String s) {
 212         int length = s.length();
 213         if ((charsBuffer == null) || (charsBuffer.length < length)) {
 214             charsBuffer = s.toCharArray();
 215         } else {
 216             s.getChars(0, length, charsBuffer, 0);
 217         }
 218         return length;
 219     }
 220 
 221     /**
 222      * checks whether TextLayout is required to handle characters.
 223      *
 224      * @param text characters to be tested
 225      * @param start start
 226      * @param limit limit
 227      * @return {@code true}  if TextLayout is required
 228      *         {@code false} if TextLayout is not required
 229      */
 230     public static final boolean isComplexLayout(char[] text, int start, int limit) {
 231         return FontUtilities.isComplexText(text, start, limit);
 232     }
 233 
 234     //
 235     // WARNING WARNING WARNING WARNING WARNING WARNING
 236     // Many of the following methods are invoked from older API.
 237     // As this older API was not passed a Component, a null Component may
 238     // now be passsed in.  For example, SwingUtilities.computeStringWidth
 239     // is implemented to call SwingUtilities2.stringWidth, the
 240     // SwingUtilities variant does not take a JComponent, as such
 241     // SwingUtilities2.stringWidth can be passed a null Component.
 242     // In other words, if you add new functionality to these methods you
 243     // need to gracefully handle null.
 244     //
 245 
 246     /**
 247      * Returns the left side bearing of the first character of string. The
 248      * left side bearing is calculated from the passed in
 249      * FontMetrics.  If the passed in String is less than one
 250      * character {@code 0} is returned.
 251      *
 252      * @param c JComponent that will display the string
 253      * @param fm FontMetrics used to measure the String width
 254      * @param string String to get the left side bearing for.
 255      * @throws NullPointerException if {@code string} is {@code null}
 256      *
 257      * @return the left side bearing of the first character of string
 258      * or {@code 0} if the string is empty
 259      */
 260     public static int getLeftSideBearing(JComponent c, FontMetrics fm,
 261                                          String string) {
 262         if ((string == null) || (string.length() == 0)) {
 263             return 0;
 264         }
 265         return getLeftSideBearing(c, fm, string.charAt(0));
 266     }
 267 
 268     /**
 269      * Returns the left side bearing of the first character of string. The
 270      * left side bearing is calculated from the passed in FontMetrics.
 271      *
 272      * @param c JComponent that will display the string
 273      * @param fm FontMetrics used to measure the String width
 274      * @param firstChar Character to get the left side bearing for.
 275      */
 276     public static int getLeftSideBearing(JComponent c, FontMetrics fm,
 277                                          char firstChar) {
 278         int charIndex = (int) firstChar;
 279         if (charIndex < MAX_CHAR_INDEX && charIndex >= MIN_CHAR_INDEX) {
 280             byte[] lsbs = null;
 281 
 282             FontRenderContext frc = getFontRenderContext(c, fm);
 283             Font font = fm.getFont();
 284             synchronized (SwingUtilities2.class) {
 285                 LSBCacheEntry entry = null;
 286                 if (searchKey == null) {
 287                     searchKey = new LSBCacheEntry(frc, font);
 288                 } else {
 289                     searchKey.reset(frc, font);
 290                 }
 291                 // See if we already have an entry for this pair
 292                 for (LSBCacheEntry cacheEntry : fontCache) {
 293                     if (searchKey.equals(cacheEntry)) {
 294                         entry = cacheEntry;
 295                         break;
 296                     }
 297                 }
 298                 if (entry == null) {
 299                     // No entry for this pair, add it.
 300                     entry = searchKey;
 301                     fontCache[nextIndex] = searchKey;
 302                     searchKey = null;
 303                     nextIndex = (nextIndex + 1) % CACHE_SIZE;
 304                 }
 305                 return entry.getLeftSideBearing(firstChar);
 306             }
 307         }
 308         return 0;
 309     }
 310 
 311     /**
 312      * Returns the FontMetrics for the current Font of the passed
 313      * in Graphics.  This method is used when a Graphics
 314      * is available, typically when painting.  If a Graphics is not
 315      * available the JComponent method of the same name should be used.
 316      * <p>
 317      * Callers should pass in a non-null JComponent, the exception
 318      * to this is if a JComponent is not readily available at the time of
 319      * painting.
 320      * <p>
 321      * This does not necessarily return the FontMetrics from the
 322      * Graphics.
 323      *
 324      * @param c JComponent requesting FontMetrics, may be null
 325      * @param g Graphics Graphics
 326      */
 327     public static FontMetrics getFontMetrics(JComponent c, Graphics g) {
 328         return getFontMetrics(c, g, g.getFont());
 329     }
 330 
 331 
 332     /**
 333      * Returns the FontMetrics for the specified Font.
 334      * This method is used when a Graphics is available, typically when
 335      * painting.  If a Graphics is not available the JComponent method of
 336      * the same name should be used.
 337      * <p>
 338      * Callers should pass in a non-null JComonent, the exception
 339      * to this is if a JComponent is not readily available at the time of
 340      * painting.
 341      * <p>
 342      * This does not necessarily return the FontMetrics from the
 343      * Graphics.
 344      *
 345      * @param c JComponent requesting FontMetrics, may be null
 346      * @param c Graphics Graphics
 347      * @param font Font to get FontMetrics for
 348      */
 349     @SuppressWarnings("deprecation")
 350     public static FontMetrics getFontMetrics(JComponent c, Graphics g,
 351                                              Font font) {
 352         if (c != null) {
 353             // Note: We assume that we're using the FontMetrics
 354             // from the widget to layout out text, otherwise we can get
 355             // mismatches when printing.
 356             return c.getFontMetrics(font);
 357         }
 358         return Toolkit.getDefaultToolkit().getFontMetrics(font);
 359     }
 360 
 361 
 362     /**
 363      * Returns the width of the passed in String.
 364      * If the passed String is {@code null}, returns zero.
 365      *
 366      * @param c JComponent that will display the string, may be null
 367      * @param fm FontMetrics used to measure the String width
 368      * @param string String to get the width of
 369      */
 370     public static int stringWidth(JComponent c, FontMetrics fm, String string) {
 371         return (int) stringWidth(c, fm, string, false);
 372     }
 373 
 374     /**
 375      * Returns the width of the passed in String.
 376      * If the passed String is {@code null}, returns zero.
 377      *
 378      * @param c JComponent that will display the string, may be null
 379      * @param fm FontMetrics used to measure the String width
 380      * @param string String to get the width of
 381      * @param useFPAPI use floating point API
 382      */
 383     public static float stringWidth(JComponent c, FontMetrics fm, String string,
 384             boolean useFPAPI){
 385         if (string == null || string.isEmpty()) {
 386             return 0;
 387         }
 388         boolean needsTextLayout = ((c != null) &&
 389                 (c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));
 390         if (needsTextLayout) {
 391             synchronized(charsBufferLock) {
 392                 int length = syncCharsBuffer(string);
 393                 needsTextLayout = isComplexLayout(charsBuffer, 0, length);
 394             }
 395         }
 396         if (needsTextLayout) {
 397             TextLayout layout = createTextLayout(c, string,
 398                                     fm.getFont(), fm.getFontRenderContext());
 399             return layout.getAdvance();
 400         } else {
 401             return getFontStringWidth(string, fm, useFPAPI);
 402         }
 403     }
 404 
 405 
 406     /**
 407      * Clips the passed in String to the space provided.
 408      *
 409      * @param c JComponent that will display the string, may be null
 410      * @param fm FontMetrics used to measure the String width
 411      * @param string String to display
 412      * @param availTextWidth Amount of space that the string can be drawn in
 413      * @return Clipped string that can fit in the provided space.
 414      */
 415     public static String clipStringIfNecessary(JComponent c, FontMetrics fm,
 416                                                String string,
 417                                                int availTextWidth) {
 418         if (string == null || string.isEmpty())  {
 419             return "";
 420         }
 421         int textWidth = SwingUtilities2.stringWidth(c, fm, string);
 422         if (textWidth > availTextWidth) {
 423             return SwingUtilities2.clipString(c, fm, string, availTextWidth);
 424         }
 425         return string;
 426     }
 427 
 428 
 429     /**
 430      * Clips the passed in String to the space provided.  NOTE: this assumes
 431      * the string does not fit in the available space.
 432      *
 433      * @param c JComponent that will display the string, may be null
 434      * @param fm FontMetrics used to measure the String width
 435      * @param string String to display
 436      * @param availTextWidth Amount of space that the string can be drawn in
 437      * @return Clipped string that can fit in the provided space.
 438      */
 439     public static String clipString(JComponent c, FontMetrics fm,
 440                                     String string, int availTextWidth) {
 441         // c may be null here.
 442         String clipString = "...";
 443         availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString);
 444         if (availTextWidth <= 0) {
 445             //can not fit any characters
 446             return clipString;
 447         }
 448 
 449         boolean needsTextLayout;
 450         synchronized (charsBufferLock) {
 451             int stringLength = syncCharsBuffer(string);
 452             needsTextLayout =
 453                 isComplexLayout(charsBuffer, 0, stringLength);
 454             if (!needsTextLayout) {
 455                 int width = 0;
 456                 for (int nChars = 0; nChars < stringLength; nChars++) {
 457                     width += fm.charWidth(charsBuffer[nChars]);
 458                     if (width > availTextWidth) {
 459                         string = string.substring(0, nChars);
 460                         break;
 461                     }
 462                 }
 463             }
 464         }
 465         if (needsTextLayout) {
 466             AttributedString aString = new AttributedString(string);
 467             if (c != null) {
 468                 aString.addAttribute(TextAttribute.NUMERIC_SHAPING,
 469                         c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
 470             }
 471             LineBreakMeasurer measurer = new LineBreakMeasurer(
 472                     aString.getIterator(), BreakIterator.getCharacterInstance(),
 473                     getFontRenderContext(c, fm));
 474             string = string.substring(0, measurer.nextOffset(availTextWidth));
 475 
 476         }
 477         return string + clipString;
 478     }
 479 
 480 
 481     /**
 482      * Draws the string at the specified location.
 483      *
 484      * @param c JComponent that will display the string, may be null
 485      * @param g Graphics to draw the text to
 486      * @param text String to display
 487      * @param x X coordinate to draw the text at
 488      * @param y Y coordinate to draw the text at
 489      */
 490     public static void drawString(JComponent c, Graphics g, String text,
 491                                   int x, int y) {
 492         drawString(c, g, text, x, y, false);
 493     }
 494 
 495     /**
 496      * Draws the string at the specified location.
 497      *
 498      * @param c JComponent that will display the string, may be null
 499      * @param g Graphics to draw the text to
 500      * @param text String to display
 501      * @param x X coordinate to draw the text at
 502      * @param y Y coordinate to draw the text at
 503      * @param useFPAPI use floating point API
 504      */
 505     public static void drawString(JComponent c, Graphics g, String text,
 506                                   float x, float y, boolean useFPAPI) {
 507         // c may be null
 508 
 509         // All non-editable widgets that draw strings call into this
 510         // methods.  By non-editable that means widgets like JLabel, JButton
 511         // but NOT JTextComponents.
 512         if ( text == null || text.length() <= 0 ) { //no need to paint empty strings
 513             return;
 514         }
 515         if (isPrinting(g)) {
 516             Graphics2D g2d = getGraphics2D(g);
 517             if (g2d != null) {
 518                 /* The printed text must scale linearly with the UI.
 519                  * Calculate the width on screen, obtain a TextLayout with
 520                  * advances for the printer graphics FRC, and then justify
 521                  * it to fit in the screen width. This distributes the spacing
 522                  * more evenly than directly laying out to the screen advances.
 523                  */
 524                 String trimmedText = trimTrailingSpaces(text);
 525                 if (!trimmedText.isEmpty()) {
 526                     float screenWidth = (float) g2d.getFont().getStringBounds
 527                             (trimmedText, getFontRenderContext(c)).getWidth();
 528                     TextLayout layout = createTextLayout(c, text, g2d.getFont(),
 529                                                        g2d.getFontRenderContext());
 530 
 531                     // If text fits the screenWidth, then do not need to justify
 532                     if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),
 533                                             trimmedText) > screenWidth) {
 534                         layout = layout.getJustifiedLayout(screenWidth);
 535                     }
 536                     /* Use alternate print color if specified */
 537                     Color col = g2d.getColor();
 538                     if (col instanceof PrintColorUIResource) {
 539                         g2d.setColor(((PrintColorUIResource)col).getPrintColor());
 540                     }
 541 
 542                     layout.draw(g2d, x, y);
 543 
 544                     g2d.setColor(col);
 545                 }
 546 
 547                 return;
 548             }
 549         }
 550 
 551         // If we get here we're not printing
 552         if (g instanceof Graphics2D) {
 553             Graphics2D g2 = (Graphics2D)g;
 554 
 555             boolean needsTextLayout = ((c != null) &&
 556                 (c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));
 557 
 558             if (needsTextLayout) {
 559                 synchronized(charsBufferLock) {
 560                     int length = syncCharsBuffer(text);
 561                     needsTextLayout = isComplexLayout(charsBuffer, 0, length);
 562                 }
 563             }
 564 
 565             Object aaHint = (c == null)
 566                                 ? null
 567                                 : c.getClientProperty(KEY_TEXT_ANTIALIASING);
 568             if (aaHint != null) {
 569                 Object oldContrast = null;
 570                 Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
 571                 if (aaHint != oldAAValue) {
 572                     g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint);
 573                 } else {
 574                     oldAAValue = null;
 575                 }
 576 
 577                 Object lcdContrastHint = c.getClientProperty(
 578                         KEY_TEXT_LCD_CONTRAST);
 579                 if (lcdContrastHint != null) {
 580                     oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);
 581                     if (lcdContrastHint.equals(oldContrast)) {
 582                         oldContrast = null;
 583                     } else {
 584                         g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,
 585                                             lcdContrastHint);
 586                     }
 587                 }
 588 
 589                 if (needsTextLayout) {
 590                     TextLayout layout = createTextLayout(c, text, g2.getFont(),
 591                                                     g2.getFontRenderContext());
 592                     layout.draw(g2, x, y);
 593                 } else {
 594                     g2.drawString(text, x, y);
 595                 }
 596 
 597                 if (oldAAValue != null) {
 598                     g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
 599                 }
 600                 if (oldContrast != null) {
 601                     g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);
 602                 }
 603 
 604                 return;
 605             }
 606 
 607             if (needsTextLayout){
 608                 TextLayout layout = createTextLayout(c, text, g2.getFont(),
 609                                                     g2.getFontRenderContext());
 610                 layout.draw(g2, x, y);
 611                 return;
 612             }
 613         }
 614 
 615         g.drawString(text, (int) x, (int) y);
 616     }
 617 
 618     /**
 619      * Draws the string at the specified location underlining the specified
 620      * character.
 621      *
 622      * @param c JComponent that will display the string, may be null
 623      * @param g Graphics to draw the text to
 624      * @param text String to display
 625      * @param underlinedIndex Index of a character in the string to underline
 626      * @param x X coordinate to draw the text at
 627      * @param y Y coordinate to draw the text at
 628      */
 629 
 630     public static void drawStringUnderlineCharAt(JComponent c,Graphics g,
 631                            String text, int underlinedIndex, int x, int y) {
 632         drawStringUnderlineCharAt(c, g, text, underlinedIndex, x, y, false);
 633     }
 634     /**
 635      * Draws the string at the specified location underlining the specified
 636      * character.
 637      *
 638      * @param c JComponent that will display the string, may be null
 639      * @param g Graphics to draw the text to
 640      * @param text String to display
 641      * @param underlinedIndex Index of a character in the string to underline
 642      * @param x X coordinate to draw the text at
 643      * @param y Y coordinate to draw the text at
 644      * @param useFPAPI use floating point API
 645      */
 646     public static void drawStringUnderlineCharAt(JComponent c, Graphics g,
 647                                                  String text, int underlinedIndex,
 648                                                  float x, float y,
 649                                                  boolean useFPAPI) {
 650         if (text == null || text.length() <= 0) {
 651             return;
 652         }
 653         SwingUtilities2.drawString(c, g, text, x, y, useFPAPI);
 654         int textLength = text.length();
 655         if (underlinedIndex >= 0 && underlinedIndex < textLength ) {
 656             float underlineRectY = y;
 657             int underlineRectHeight = 1;
 658             float underlineRectX = 0;
 659             int underlineRectWidth = 0;
 660             boolean isPrinting = isPrinting(g);
 661             boolean needsTextLayout = isPrinting;
 662             if (!needsTextLayout) {
 663                 synchronized (charsBufferLock) {
 664                     syncCharsBuffer(text);
 665                     needsTextLayout =
 666                         isComplexLayout(charsBuffer, 0, textLength);
 667                 }
 668             }
 669             if (!needsTextLayout) {
 670                 FontMetrics fm = g.getFontMetrics();
 671                 underlineRectX = x +
 672                     SwingUtilities2.stringWidth(c,fm,
 673                                         text.substring(0,underlinedIndex));
 674                 underlineRectWidth = fm.charWidth(text.
 675                                                   charAt(underlinedIndex));
 676             } else {
 677                 Graphics2D g2d = getGraphics2D(g);
 678                 if (g2d != null) {
 679                     TextLayout layout =
 680                         createTextLayout(c, text, g2d.getFont(),
 681                                        g2d.getFontRenderContext());
 682                     if (isPrinting) {
 683                         float screenWidth = (float)g2d.getFont().
 684                             getStringBounds(text, getFontRenderContext(c)).getWidth();
 685                         // If text fits the screenWidth, then do not need to justify
 686                         if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),
 687                                                         text) > screenWidth) {
 688                             layout = layout.getJustifiedLayout(screenWidth);
 689                         }
 690                     }
 691                     TextHitInfo leading =
 692                         TextHitInfo.leading(underlinedIndex);
 693                     TextHitInfo trailing =
 694                         TextHitInfo.trailing(underlinedIndex);
 695                     Shape shape =
 696                         layout.getVisualHighlightShape(leading, trailing);
 697                     Rectangle rect = shape.getBounds();
 698                     underlineRectX = x + rect.x;
 699                     underlineRectWidth = rect.width;
 700                 }
 701             }
 702             g.fillRect((int) underlineRectX, (int) underlineRectY + 1,
 703                        underlineRectWidth, underlineRectHeight);
 704         }
 705     }
 706 
 707 
 708     /**
 709      * A variation of locationToIndex() which only returns an index if the
 710      * Point is within the actual bounds of a list item (not just in the cell)
 711      * and if the JList has the "List.isFileList" client property set.
 712      * Otherwise, this method returns -1.
 713      * This is used to make Windows {@literal L&F} JFileChooser act
 714      * like native dialogs.
 715      */
 716     public static int loc2IndexFileList(JList<?> list, Point point) {
 717         int index = list.locationToIndex(point);
 718         if (index != -1) {
 719             Object bySize = list.getClientProperty("List.isFileList");
 720             if (bySize instanceof Boolean && ((Boolean)bySize).booleanValue() &&
 721                 !pointIsInActualBounds(list, index, point)) {
 722                 index = -1;
 723             }
 724         }
 725         return index;
 726     }
 727 
 728 
 729     /**
 730      * Returns true if the given point is within the actual bounds of the
 731      * JList item at index (not just inside the cell).
 732      */
 733     private static <T> boolean pointIsInActualBounds(JList<T> list, int index,
 734                                                 Point point) {
 735         ListCellRenderer<? super T> renderer = list.getCellRenderer();
 736         T value = list.getModel().getElementAt(index);
 737         Component item = renderer.getListCellRendererComponent(list,
 738                           value, index, false, false);
 739         Dimension itemSize = item.getPreferredSize();
 740         Rectangle cellBounds = list.getCellBounds(index, index);
 741         if (!item.getComponentOrientation().isLeftToRight()) {
 742             cellBounds.x += (cellBounds.width - itemSize.width);
 743         }
 744         cellBounds.width = itemSize.width;
 745 
 746         return cellBounds.contains(point);
 747     }
 748 
 749 
 750     /**
 751      * Returns true if the given point is outside the preferredSize of the
 752      * item at the given row of the table.  (Column must be 0).
 753      * Does not check the "Table.isFileList" property. That should be checked
 754      * before calling this method.
 755      * This is used to make Windows {@literal L&F} JFileChooser act
 756      * like native dialogs.
 757      */
 758     public static boolean pointOutsidePrefSize(JTable table, int row, int column, Point p) {
 759         if (table.convertColumnIndexToModel(column) != 0 || row == -1) {
 760             return true;
 761         }
 762         TableCellRenderer tcr = table.getCellRenderer(row, column);
 763         Object value = table.getValueAt(row, column);
 764         Component cell = tcr.getTableCellRendererComponent(table, value, false,
 765                 false, row, column);
 766         Dimension itemSize = cell.getPreferredSize();
 767         Rectangle cellBounds = table.getCellRect(row, column, false);
 768         cellBounds.width = itemSize.width;
 769         cellBounds.height = itemSize.height;
 770 
 771         // See if coords are inside
 772         // ASSUME: mouse x,y will never be < cell's x,y
 773         assert (p.x >= cellBounds.x && p.y >= cellBounds.y);
 774         return p.x > cellBounds.x + cellBounds.width ||
 775                 p.y > cellBounds.y + cellBounds.height;
 776     }
 777 
 778     /**
 779      * Set the lead and anchor without affecting selection.
 780      */
 781     public static void setLeadAnchorWithoutSelection(ListSelectionModel model,
 782                                                      int lead, int anchor) {
 783         if (anchor == -1) {
 784             anchor = lead;
 785         }
 786         if (lead == -1) {
 787             model.setAnchorSelectionIndex(-1);
 788             model.setLeadSelectionIndex(-1);
 789         } else {
 790             if (model.isSelectedIndex(lead)) {
 791                 model.addSelectionInterval(lead, lead);
 792             } else {
 793                 model.removeSelectionInterval(lead, lead);
 794             }
 795             model.setAnchorSelectionIndex(anchor);
 796         }
 797     }
 798 
 799     /**
 800      * Ignore mouse events if the component is null, not enabled, the event
 801      * is not associated with the left mouse button, or the event has been
 802      * consumed.
 803      */
 804     public static boolean shouldIgnore(MouseEvent me, JComponent c) {
 805         return c == null || !c.isEnabled()
 806                          || !SwingUtilities.isLeftMouseButton(me)
 807                          || me.isConsumed();
 808     }
 809 
 810     /**
 811      * Request focus on the given component if it doesn't already have it
 812      * and {@code isRequestFocusEnabled()} returns true.
 813      */
 814     public static void adjustFocus(JComponent c) {
 815         if (!c.hasFocus() && c.isRequestFocusEnabled()) {
 816             c.requestFocus();
 817         }
 818     }
 819 
 820     /**
 821      * The following draw functions have the same semantic as the
 822      * Graphics methods with the same names.
 823      *
 824      * this is used for printing
 825      */
 826     public static int drawChars(JComponent c, Graphics g,
 827                                  char[] data,
 828                                  int offset,
 829                                  int length,
 830                                  int x,
 831                                  int y) {
 832         return (int) drawChars(c, g, data, offset, length, x, y, false);
 833     }
 834 
 835     public static float drawChars(JComponent c, Graphics g,
 836                                  char[] data,
 837                                  int offset,
 838                                  int length,
 839                                  float x,
 840                                  float y) {
 841         return drawChars(c, g, data, offset, length, x, y, true);
 842     }
 843 
 844     public static float drawChars(JComponent c, Graphics g,
 845                                  char[] data,
 846                                  int offset,
 847                                  int length,
 848                                  float x,
 849                                  float y,
 850                                  boolean useFPAPI) {
 851         if ( length <= 0 ) { //no need to paint empty strings
 852             return x;
 853         }
 854         float nextX = x + getFontCharsWidth(data, offset, length,
 855                                             getFontMetrics(c, g),
 856                                             useFPAPI);
 857         if (isPrinting(g)) {
 858             Graphics2D g2d = getGraphics2D(g);
 859             if (g2d != null) {
 860                 FontRenderContext deviceFontRenderContext = g2d.
 861                     getFontRenderContext();
 862                 FontRenderContext frc = getFontRenderContext(c);
 863                 if (frc != null &&
 864                     !isFontRenderContextPrintCompatible
 865                     (deviceFontRenderContext, frc)) {
 866 
 867                     String text = new String(data, offset, length);
 868                     TextLayout layout = new TextLayout(text, g2d.getFont(),
 869                                     deviceFontRenderContext);
 870                     String trimmedText = trimTrailingSpaces(text);
 871                     if (!trimmedText.isEmpty()) {
 872                         float screenWidth = (float)g2d.getFont().
 873                             getStringBounds(trimmedText, frc).getWidth();
 874                         // If text fits the screenWidth, then do not need to justify
 875                         if (SwingUtilities2.stringWidth(c, g2d.getFontMetrics(),
 876                                                 trimmedText) > screenWidth) {
 877                             layout = layout.getJustifiedLayout(screenWidth);
 878                         }
 879 
 880                         /* Use alternate print color if specified */
 881                         Color col = g2d.getColor();
 882                         if (col instanceof PrintColorUIResource) {
 883                             g2d.setColor(((PrintColorUIResource)col).getPrintColor());
 884                         }
 885 
 886                         layout.draw(g2d,x,y);
 887 
 888                         g2d.setColor(col);
 889                     }
 890 
 891                     return nextX;
 892                 }
 893             }
 894         }
 895         // Assume we're not printing if we get here, or that we are invoked
 896         // via Swing text printing which is laid out for the printer.
 897         Object aaHint = (c == null)
 898                             ? null
 899                             : c.getClientProperty(KEY_TEXT_ANTIALIASING);
 900 
 901         if (!(g instanceof Graphics2D)) {
 902             g.drawChars(data, offset, length, (int) x, (int) y);
 903             return nextX;
 904         }
 905 
 906         Graphics2D g2 = (Graphics2D) g;
 907         if (aaHint != null) {
 908 
 909             Object oldContrast = null;
 910             Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
 911             if (aaHint != null && aaHint != oldAAValue) {
 912                 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, aaHint);
 913             } else {
 914                 oldAAValue = null;
 915             }
 916 
 917             Object lcdContrastHint = c.getClientProperty(KEY_TEXT_LCD_CONTRAST);
 918             if (lcdContrastHint != null) {
 919                 oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);
 920                 if (lcdContrastHint.equals(oldContrast)) {
 921                     oldContrast = null;
 922                 } else {
 923                     g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,
 924                                         lcdContrastHint);
 925                 }
 926             }
 927 
 928             g2.drawString(new String(data, offset, length), x, y);
 929 
 930             if (oldAAValue != null) {
 931                 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
 932             }
 933             if (oldContrast != null) {
 934                 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);
 935             }
 936         }
 937         else {
 938             g2.drawString(new String(data, offset, length), x, y);
 939         }
 940         return nextX;
 941     }
 942 
 943     public static float getFontCharWidth(char c, FontMetrics fm,
 944                                          boolean useFPAPI)
 945     {
 946         return getFontCharsWidth(new char[]{c}, 0, 1, fm, useFPAPI);
 947     }
 948 
 949     public static float getFontCharsWidth(char[] data, int offset, int len,
 950                                           FontMetrics fm,
 951                                           boolean useFPAPI)
 952     {
 953         if (len == 0) {
 954            return 0;
 955         }
 956         if (useFPAPI) {
 957             Rectangle2D bounds = fm.getFont().
 958                                      getStringBounds(data, offset, offset + len,
 959                                                      fm.getFontRenderContext());
 960             return (float) bounds.getWidth();
 961         } else {
 962             return fm.charsWidth(data, offset, len);
 963         }
 964     }
 965 
 966     public static float getFontStringWidth(String data, FontMetrics fm,
 967                                            boolean useFPAPI)
 968     {
 969         if (useFPAPI) {
 970             Rectangle2D bounds = fm.getFont()
 971                     .getStringBounds(data, fm.getFontRenderContext());
 972             return (float) bounds.getWidth();
 973         } else {
 974             return fm.stringWidth(data);
 975         }
 976     }
 977 
 978     /*
 979      * see documentation for drawChars
 980      * returns the advance
 981      */
 982     public static float drawString(JComponent c, Graphics g,
 983                                    AttributedCharacterIterator iterator,
 984                                    int x, int y)
 985     {
 986         return drawStringImpl(c, g, iterator, x, y);
 987     }
 988 
 989     public static float drawString(JComponent c, Graphics g,
 990                                    AttributedCharacterIterator iterator,
 991                                    float x, float y)
 992     {
 993         return drawStringImpl(c, g, iterator, x, y);
 994     }
 995 
 996     private static float drawStringImpl(JComponent c, Graphics g,
 997                                    AttributedCharacterIterator iterator,
 998                                    float x, float y)
 999     {
1000 
1001         float retVal;
1002         boolean isPrinting = isPrinting(g);
1003         Color col = g.getColor();
1004 
1005         if (isPrinting) {
1006             /* Use alternate print color if specified */
1007             if (col instanceof PrintColorUIResource) {
1008                 g.setColor(((PrintColorUIResource)col).getPrintColor());
1009             }
1010         }
1011 
1012         Graphics2D g2d = getGraphics2D(g);
1013         if (g2d == null) {
1014             g.drawString(iterator, (int)x, (int)y); //for the cases where advance
1015                                                     //matters it should not happen
1016             retVal = x;
1017 
1018         } else {
1019             FontRenderContext frc;
1020             if (isPrinting) {
1021                 frc = getFontRenderContext(c);
1022                 if (frc.isAntiAliased() || frc.usesFractionalMetrics()) {
1023                     frc = new FontRenderContext(frc.getTransform(), false, false);
1024                 }
1025             } else if ((frc = getFRCProperty(c)) != null) {
1026                 /* frc = frc; ! */
1027             } else {
1028                 frc = g2d.getFontRenderContext();
1029             }
1030             TextLayout layout;
1031             if (isPrinting) {
1032                 FontRenderContext deviceFRC = g2d.getFontRenderContext();
1033                 if (!isFontRenderContextPrintCompatible(frc, deviceFRC)) {
1034                     layout = new TextLayout(iterator, deviceFRC);
1035                     AttributedCharacterIterator trimmedIt =
1036                             getTrimmedTrailingSpacesIterator(iterator);
1037                     if (trimmedIt != null) {
1038                         float screenWidth = new TextLayout(trimmedIt, frc).
1039                                 getAdvance();
1040                         layout = layout.getJustifiedLayout(screenWidth);
1041                     }
1042                 } else {
1043                     layout = new TextLayout(iterator, frc);
1044                 }
1045             } else {
1046                 layout = new TextLayout(iterator, frc);
1047             }
1048             layout.draw(g2d, x, y);
1049             retVal = layout.getAdvance();
1050         }
1051 
1052         if (isPrinting) {
1053             g.setColor(col);
1054         }
1055 
1056         return retVal;
1057     }
1058 
1059     /**
1060      * This method should be used for drawing a borders over a filled rectangle.
1061      * Draws vertical line, using the current color, between the points {@code
1062      * (x, y1)} and {@code (x, y2)} in graphics context's coordinate system.
1063      * Note: it use {@code Graphics.fillRect()} internally.
1064      *
1065      * @param g  Graphics to draw the line to.
1066      * @param x  the <i>x</i> coordinate.
1067      * @param y1 the first point's <i>y</i> coordinate.
1068      * @param y2 the second point's <i>y</i> coordinate.
1069      */
1070     public static void drawVLine(Graphics g, int x, int y1, int y2) {
1071         if (y2 < y1) {
1072             final int temp = y2;
1073             y2 = y1;
1074             y1 = temp;
1075         }
1076         g.fillRect(x, y1, 1, y2 - y1 + 1);
1077     }
1078 
1079     /**
1080      * This method should be used for drawing a borders over a filled rectangle.
1081      * Draws horizontal line, using the current color, between the points {@code
1082      * (x1, y)} and {@code (x2, y)} in graphics context's coordinate system.
1083      * Note: it use {@code Graphics.fillRect()} internally.
1084      *
1085      * @param g  Graphics to draw the line to.
1086      * @param x1 the first point's <i>x</i> coordinate.
1087      * @param x2 the second point's <i>x</i> coordinate.
1088      * @param y  the <i>y</i> coordinate.
1089      */
1090     public static void drawHLine(Graphics g, int x1, int x2, int y) {
1091         if (x2 < x1) {
1092             final int temp = x2;
1093             x2 = x1;
1094             x1 = temp;
1095         }
1096         g.fillRect(x1, y, x2 - x1 + 1, 1);
1097     }
1098 
1099     /**
1100      * This method should be used for drawing a borders over a filled rectangle.
1101      * Draws the outline of the specified rectangle. The left and right edges of
1102      * the rectangle are at {@code x} and {@code x + w}. The top and bottom
1103      * edges are at {@code y} and {@code y + h}. The rectangle is drawn using
1104      * the graphics context's current color. Note: it use {@code
1105      * Graphics.fillRect()} internally.
1106      *
1107      * @param g Graphics to draw the rectangle to.
1108      * @param x the <i>x</i> coordinate of the rectangle to be drawn.
1109      * @param y the <i>y</i> coordinate of the rectangle to be drawn.
1110      * @param w the w of the rectangle to be drawn.
1111      * @param h the h of the rectangle to be drawn.
1112      * @see SwingUtilities2#drawVLine(java.awt.Graphics, int, int, int)
1113      * @see SwingUtilities2#drawHLine(java.awt.Graphics, int, int, int)
1114      */
1115     public static void drawRect(Graphics g, int x, int y, int w, int h) {
1116         if (w < 0 || h < 0) {
1117             return;
1118         }
1119 
1120         if (h == 0 || w == 0) {
1121             g.fillRect(x, y, w + 1, h + 1);
1122         } else {
1123             g.fillRect(x, y, w, 1);
1124             g.fillRect(x + w, y, 1, h);
1125             g.fillRect(x + 1, y + h, w, 1);
1126             g.fillRect(x, y + 1, 1, h);
1127         }
1128     }
1129 
1130     private static TextLayout createTextLayout(JComponent c, String s,
1131                                             Font f, FontRenderContext frc) {
1132         Object shaper = (c == null ?
1133                     null : c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
1134         if (shaper == null) {
1135             return new TextLayout(s, f, frc);
1136         } else {
1137             Map<TextAttribute, Object> a = new HashMap<TextAttribute, Object>();
1138             a.put(TextAttribute.FONT, f);
1139             a.put(TextAttribute.NUMERIC_SHAPING, shaper);
1140             return new TextLayout(s, a, frc);
1141         }
1142     }
1143 
1144     /*
1145      * Checks if two given FontRenderContexts are compatible for printing.
1146      * We can't just use equals as we want to exclude from the comparison :
1147      * + whether AA is set as irrelevant for printing and shouldn't affect
1148      * printed metrics anyway
1149      * + any translation component in the transform of either FRC, as it
1150      * does not affect metrics.
1151      * Compatible means no special handling needed for text painting
1152      */
1153     private static boolean
1154         isFontRenderContextPrintCompatible(FontRenderContext frc1,
1155                                            FontRenderContext frc2) {
1156 
1157         if (frc1 == frc2) {
1158             return true;
1159         }
1160 
1161         if (frc1 == null || frc2 == null) { // not supposed to happen
1162             return false;
1163         }
1164 
1165         if (frc1.getFractionalMetricsHint() !=
1166             frc2.getFractionalMetricsHint()) {
1167             return false;
1168         }
1169 
1170         /* If both are identity, return true */
1171         if (!frc1.isTransformed() && !frc2.isTransformed()) {
1172             return true;
1173         }
1174 
1175         /* That's the end of the cheap tests, need to get and compare
1176          * the transform matrices. We don't care about the translation
1177          * components, so return true if they are otherwise identical.
1178          */
1179         double[] mat1 = new double[4];
1180         double[] mat2 = new double[4];
1181         frc1.getTransform().getMatrix(mat1);
1182         frc2.getTransform().getMatrix(mat2);
1183         return
1184             mat1[0] == mat2[0] &&
1185             mat1[1] == mat2[1] &&
1186             mat1[2] == mat2[2] &&
1187             mat1[3] == mat2[3];
1188     }
1189 
1190     /*
1191      * Tries it best to get Graphics2D out of the given Graphics
1192      * returns null if can not derive it.
1193      */
1194     public static Graphics2D getGraphics2D(Graphics g) {
1195         if (g instanceof Graphics2D) {
1196             return (Graphics2D) g;
1197         } else if (g instanceof ProxyPrintGraphics) {
1198             return (Graphics2D)(((ProxyPrintGraphics)g).getGraphics());
1199         } else {
1200             return null;
1201         }
1202     }
1203 
1204     /*
1205      * Returns FontRenderContext associated with Component.
1206      * FontRenderContext from Component.getFontMetrics is associated
1207      * with the component.
1208      *
1209      * Uses Component.getFontMetrics to get the FontRenderContext from.
1210      * see JComponent.getFontMetrics and TextLayoutStrategy.java
1211      */
1212     public static FontRenderContext getFontRenderContext(Component c) {
1213         assert c != null;
1214         if (c == null) {
1215             return DEFAULT_FRC;
1216         } else {
1217             return c.getFontMetrics(c.getFont()).getFontRenderContext();
1218         }
1219     }
1220 
1221     /**
1222      * A convenience method to get FontRenderContext.
1223      * Returns the FontRenderContext for the passed in FontMetrics or
1224      * for the passed in Component if FontMetrics is null
1225      */
1226     private static FontRenderContext getFontRenderContext(Component c, FontMetrics fm) {
1227         assert fm != null || c!= null;
1228         return (fm != null) ? fm.getFontRenderContext()
1229             : getFontRenderContext(c);
1230     }
1231 
1232     /*
1233      * This method is to be used only for JComponent.getFontMetrics.
1234      * In all other places to get FontMetrics we need to use
1235      * JComponent.getFontMetrics.
1236      *
1237      */
1238     public static FontMetrics getFontMetrics(JComponent c, Font font) {
1239         FontRenderContext  frc = getFRCProperty(c);
1240         if (frc == null) {
1241             frc = DEFAULT_FRC;
1242         }
1243         return FontDesignMetrics.getMetrics(font, frc);
1244     }
1245 
1246 
1247     /* Get any FontRenderContext associated with a JComponent
1248      * - may return null
1249      */
1250     private static FontRenderContext getFRCProperty(JComponent c) {
1251         if (c != null) {
1252 
1253             GraphicsConfiguration gc = c.getGraphicsConfiguration();
1254             AffineTransform tx = (gc == null) ? null : gc.getDefaultTransform();
1255             Object aaHint = c.getClientProperty(KEY_TEXT_ANTIALIASING);
1256             return getFRCFromCache(tx, aaHint);
1257         }
1258         return null;
1259     }
1260 
1261     private static final Object APP_CONTEXT_FRC_CACHE_KEY = new Object();
1262 
1263     private static FontRenderContext getFRCFromCache(AffineTransform tx,
1264                                                      Object aaHint) {
1265         if (tx == null && aaHint == null) {
1266             return null;
1267         }
1268 
1269         @SuppressWarnings("unchecked")
1270         Map<Object, FontRenderContext> cache = (Map<Object, FontRenderContext>)
1271                 AppContext.getAppContext().get(APP_CONTEXT_FRC_CACHE_KEY);
1272 
1273         if (cache == null) {
1274             cache = new HashMap<>();
1275             AppContext.getAppContext().put(APP_CONTEXT_FRC_CACHE_KEY, cache);
1276         }
1277 
1278         Object key = (tx == null)
1279                 ? aaHint
1280                 : (aaHint == null ? tx : new KeyPair(tx, aaHint));
1281 
1282         FontRenderContext frc = cache.get(key);
1283         if (frc == null) {
1284             aaHint = (aaHint == null) ? VALUE_TEXT_ANTIALIAS_OFF : aaHint;
1285             frc = new FontRenderContext(tx, aaHint,
1286                                         VALUE_FRACTIONALMETRICS_DEFAULT);
1287             cache.put(key, frc);
1288         }
1289         return frc;
1290     }
1291 
1292     private static class KeyPair {
1293 
1294         private final Object key1;
1295         private final Object key2;
1296 
1297         public KeyPair(Object key1, Object key2) {
1298             this.key1 = key1;
1299             this.key2 = key2;
1300         }
1301 
1302         @Override
1303         public boolean equals(Object obj) {
1304             if (!(obj instanceof KeyPair)) {
1305                 return false;
1306             }
1307             KeyPair that = (KeyPair) obj;
1308             return this.key1.equals(that.key1) && this.key2.equals(that.key2);
1309         }
1310 
1311         @Override
1312         public int hashCode() {
1313             return key1.hashCode() + 37 * key2.hashCode();
1314         }
1315     }
1316 
1317     /*
1318      * returns true if the Graphics is print Graphics
1319      * false otherwise
1320      */
1321     static boolean isPrinting(Graphics g) {
1322         return (g instanceof PrinterGraphics || g instanceof PrintGraphics);
1323     }
1324 
1325     private static String trimTrailingSpaces(String s) {
1326         int i = s.length() - 1;
1327         while(i >= 0 && Character.isWhitespace(s.charAt(i))) {
1328             i--;
1329         }
1330         return s.substring(0, i + 1);
1331     }
1332 
1333     private static AttributedCharacterIterator getTrimmedTrailingSpacesIterator
1334             (AttributedCharacterIterator iterator) {
1335         int curIdx = iterator.getIndex();
1336 
1337         char c = iterator.last();
1338         while(c != CharacterIterator.DONE && Character.isWhitespace(c)) {
1339             c = iterator.previous();
1340         }
1341 
1342         if (c != CharacterIterator.DONE) {
1343             int endIdx = iterator.getIndex();
1344 
1345             if (endIdx == iterator.getEndIndex() - 1) {
1346                 iterator.setIndex(curIdx);
1347                 return iterator;
1348             } else {
1349                 AttributedString trimmedText = new AttributedString(iterator,
1350                         iterator.getBeginIndex(), endIdx + 1);
1351                 return trimmedText.getIterator();
1352             }
1353         } else {
1354             return null;
1355         }
1356     }
1357 
1358     /**
1359      * Determines whether the SelectedTextColor should be used for painting text
1360      * foreground for the specified highlight.
1361      *
1362      * Returns true only if the highlight painter for the specified highlight
1363      * is the swing painter (whether inner class of javax.swing.text.DefaultHighlighter
1364      * or com.sun.java.swing.plaf.windows.WindowsTextUI) and its background color
1365      * is null or equals to the selection color of the text component.
1366      *
1367      * This is a hack for fixing both bugs 4761990 and 5003294
1368      */
1369     public static boolean useSelectedTextColor(Highlighter.Highlight h, JTextComponent c) {
1370         Highlighter.HighlightPainter painter = h.getPainter();
1371         String painterClass = painter.getClass().getName();
1372         if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&
1373                 painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {
1374             return false;
1375         }
1376         try {
1377             DefaultHighlighter.DefaultHighlightPainter defPainter =
1378                     (DefaultHighlighter.DefaultHighlightPainter) painter;
1379             if (defPainter.getColor() != null &&
1380                     !defPainter.getColor().equals(c.getSelectionColor())) {
1381                 return false;
1382             }
1383         } catch (ClassCastException e) {
1384             return false;
1385         }
1386         return true;
1387     }
1388 
1389     /**
1390      * LSBCacheEntry is used to cache the left side bearing (lsb) for
1391      * a particular {@code Font} and {@code FontRenderContext}.
1392      * This only caches characters that fall in the range
1393      * {@code MIN_CHAR_INDEX} to {@code MAX_CHAR_INDEX}.
1394      */
1395     private static class LSBCacheEntry {
1396         // Used to indicate a particular entry in lsb has not been set.
1397         private static final byte UNSET = Byte.MAX_VALUE;
1398         // Used in creating a GlyphVector to get the lsb
1399         private static final char[] oneChar = new char[1];
1400 
1401         private byte[] lsbCache;
1402         private Font font;
1403         private FontRenderContext frc;
1404 
1405 
1406         public LSBCacheEntry(FontRenderContext frc, Font font) {
1407             lsbCache = new byte[MAX_CHAR_INDEX - MIN_CHAR_INDEX];
1408             reset(frc, font);
1409 
1410         }
1411 
1412         public void reset(FontRenderContext frc, Font font) {
1413             this.font = font;
1414             this.frc = frc;
1415             for (int counter = lsbCache.length - 1; counter >= 0; counter--) {
1416                 lsbCache[counter] = UNSET;
1417             }
1418         }
1419 
1420         public int getLeftSideBearing(char aChar) {
1421             int index = aChar - MIN_CHAR_INDEX;
1422             assert (index >= 0 && index < (MAX_CHAR_INDEX - MIN_CHAR_INDEX));
1423             byte lsb = lsbCache[index];
1424             if (lsb == UNSET) {
1425                 oneChar[0] = aChar;
1426                 GlyphVector gv = font.createGlyphVector(frc, oneChar);
1427                 lsb = (byte) gv.getGlyphPixelBounds(0, frc, 0f, 0f).x;
1428                 if (lsb < 0) {
1429                     /* HRGB/HBGR LCD glyph images will always have a pixel
1430                      * on the left used in colour fringe reduction.
1431                      * Text rendering positions this correctly but here
1432                      * we are using the glyph image to adjust that position
1433                      * so must account for it.
1434                      */
1435                     Object aaHint = frc.getAntiAliasingHint();
1436                     if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
1437                             aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
1438                         lsb++;
1439                     }
1440                 }
1441                 lsbCache[index] = lsb;
1442             }
1443             return lsb;
1444 
1445 
1446         }
1447 
1448         public boolean equals(Object entry) {
1449             if (entry == this) {
1450                 return true;
1451             }
1452             if (!(entry instanceof LSBCacheEntry)) {
1453                 return false;
1454             }
1455             LSBCacheEntry oEntry = (LSBCacheEntry) entry;
1456             return (font.equals(oEntry.font) &&
1457                     frc.equals(oEntry.frc));
1458         }
1459 
1460         public int hashCode() {
1461             int result = 17;
1462             if (font != null) {
1463                 result = 37 * result + font.hashCode();
1464             }
1465             if (frc != null) {
1466                 result = 37 * result + frc.hashCode();
1467             }
1468             return result;
1469         }
1470     }
1471 
1472     /*
1473      * here goes the fix for 4856343 [Problem with applet interaction
1474      * with system selection clipboard]
1475      *
1476      * NOTE. In case isTrustedContext() no checking
1477      * are to be performed
1478      */
1479 
1480     /**
1481     * checks the security permissions for accessing system clipboard
1482     *
1483     * for untrusted context (see isTrustedContext) checks the
1484     * permissions for the current event being handled
1485     *
1486     */
1487    public static boolean canAccessSystemClipboard() {
1488        boolean canAccess = false;
1489        if (!GraphicsEnvironment.isHeadless()) {
1490            SecurityManager sm = System.getSecurityManager();
1491            if (sm == null) {
1492                canAccess = true;
1493            } else {
1494                try {
1495                    sm.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
1496                    canAccess = true;
1497                } catch (SecurityException e) {
1498                }
1499                if (canAccess && ! isTrustedContext()) {
1500                    canAccess = canCurrentEventAccessSystemClipboard(true);
1501                }
1502            }
1503        }
1504        return canAccess;
1505    }
1506     /**
1507     * Returns true if EventQueue.getCurrentEvent() has the permissions to
1508      * access the system clipboard
1509      */
1510     public static boolean canCurrentEventAccessSystemClipboard() {
1511         return  isTrustedContext()
1512             || canCurrentEventAccessSystemClipboard(false);
1513     }
1514 
1515     /**
1516      * Returns true if the given event has permissions to access the
1517      * system clipboard
1518      *
1519      * @param e AWTEvent to check
1520      */
1521     public static boolean canEventAccessSystemClipboard(AWTEvent e) {
1522         return isTrustedContext()
1523             || canEventAccessSystemClipboard(e, false);
1524     }
1525 
1526     /**
1527      * Returns true if the given event is corrent gesture for
1528      * accessing clipboard
1529      *
1530      * @param ie InputEvent to check
1531      */
1532     @SuppressWarnings("deprecation")
1533     private static boolean isAccessClipboardGesture(InputEvent ie) {
1534         boolean allowedGesture = false;
1535         if (ie instanceof KeyEvent) { //we can validate only keyboard gestures
1536             KeyEvent ke = (KeyEvent)ie;
1537             int keyCode = ke.getKeyCode();
1538             int keyModifiers = ke.getModifiers();
1539             switch(keyCode) {
1540             case KeyEvent.VK_C:
1541             case KeyEvent.VK_V:
1542             case KeyEvent.VK_X:
1543                 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK);
1544                 break;
1545             case KeyEvent.VK_INSERT:
1546                 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK ||
1547                                   keyModifiers == InputEvent.SHIFT_MASK);
1548                 break;
1549             case KeyEvent.VK_COPY:
1550             case KeyEvent.VK_PASTE:
1551             case KeyEvent.VK_CUT:
1552                 allowedGesture = true;
1553                 break;
1554             case KeyEvent.VK_DELETE:
1555                 allowedGesture = ( keyModifiers == InputEvent.SHIFT_MASK);
1556                 break;
1557             }
1558         }
1559         return allowedGesture;
1560     }
1561 
1562     /**
1563      * Returns true if e has the permissions to
1564      * access the system clipboard and if it is allowed gesture (if
1565      * checkGesture is true)
1566      *
1567      * @param e AWTEvent to check
1568      * @param checkGesture boolean
1569      */
1570     private static boolean canEventAccessSystemClipboard(AWTEvent e,
1571                                                         boolean checkGesture) {
1572         if (EventQueue.isDispatchThread()) {
1573             /*
1574              * Checking event permissions makes sense only for event
1575              * dispathing thread
1576              */
1577             if (e instanceof InputEvent
1578                 && (! checkGesture || isAccessClipboardGesture((InputEvent)e))) {
1579                 return AWTAccessor.getInputEventAccessor().
1580                         canAccessSystemClipboard((InputEvent) e);
1581             } else {
1582                 return false;
1583             }
1584         } else {
1585             return true;
1586         }
1587     }
1588 
1589     /**
1590      * Utility method that throws SecurityException if SecurityManager is set
1591      * and modifiers are not public
1592      *
1593      * @param modifiers a set of modifiers
1594      */
1595     public static void checkAccess(int modifiers) {
1596         if (System.getSecurityManager() != null
1597                 && !Modifier.isPublic(modifiers)) {
1598             throw new SecurityException("Resource is not accessible");
1599         }
1600     }
1601 
1602     /**
1603      * Returns true if EventQueue.getCurrentEvent() has the permissions to
1604      * access the system clipboard and if it is allowed gesture (if
1605      * checkGesture true)
1606      *
1607      * @param checkGesture boolean
1608      */
1609     private static boolean canCurrentEventAccessSystemClipboard(boolean
1610                                                                checkGesture) {
1611         AWTEvent event = EventQueue.getCurrentEvent();
1612         return canEventAccessSystemClipboard(event, checkGesture);
1613     }
1614 
1615     /**
1616      * see RFE 5012841 [Per AppContect security permissions] for the
1617      * details
1618      *
1619      */
1620     private static boolean isTrustedContext() {
1621         return (System.getSecurityManager() == null)
1622             || (AppContext.getAppContext().
1623                 get(UntrustedClipboardAccess) == null);
1624     }
1625 
1626     public static String displayPropertiesToCSS(Font font, Color fg) {
1627         StringBuilder rule = new StringBuilder("body {");
1628         if (font != null) {
1629             rule.append(" font-family: ");
1630             rule.append(font.getFamily());
1631             rule.append(" ; ");
1632             rule.append(" font-size: ");
1633             rule.append(font.getSize());
1634             rule.append("pt ;");
1635             if (font.isBold()) {
1636                 rule.append(" font-weight: 700 ; ");
1637             }
1638             if (font.isItalic()) {
1639                 rule.append(" font-style: italic ; ");
1640             }
1641         }
1642         if (fg != null) {
1643             rule.append(" color: #");
1644             if (fg.getRed() < 16) {
1645                 rule.append('0');
1646             }
1647             rule.append(Integer.toHexString(fg.getRed()));
1648             if (fg.getGreen() < 16) {
1649                 rule.append('0');
1650             }
1651             rule.append(Integer.toHexString(fg.getGreen()));
1652             if (fg.getBlue() < 16) {
1653                 rule.append('0');
1654             }
1655             rule.append(Integer.toHexString(fg.getBlue()));
1656             rule.append(" ; ");
1657         }
1658         rule.append(" }");
1659         return rule.toString();
1660     }
1661 
1662     /**
1663      * Utility method that creates a {@code UIDefaults.LazyValue} that
1664      * creates an {@code ImageIcon} {@code UIResource} for the
1665      * specified image file name. The image is loaded using
1666      * {@code getResourceAsStream}, starting with a call to that method
1667      * on the base class parameter. If it cannot be found, searching will
1668      * continue through the base class' inheritance hierarchy, up to and
1669      * including {@code rootClass}.
1670      *
1671      * @param baseClass the first class to use in searching for the resource
1672      * @param rootClass an ancestor of {@code baseClass} to finish the
1673      *                  search at
1674      * @param imageFile the name of the file to be found
1675      * @return a lazy value that creates the {@code ImageIcon}
1676      *         {@code UIResource} for the image,
1677      *         or null if it cannot be found
1678      */
1679     public static Object makeIcon(final Class<?> baseClass,
1680                                   final Class<?> rootClass,
1681                                   final String imageFile) {
1682         return makeIcon(baseClass, rootClass, imageFile, true);
1683     }
1684 
1685     /**
1686      * Utility method that creates a {@code UIDefaults.LazyValue} that
1687      * creates an {@code ImageIcon} {@code UIResource} for the
1688      * specified image file name. The image is loaded using
1689      * {@code getResourceAsStream}, starting with a call to that method
1690      * on the base class parameter. If it cannot be found, searching will
1691      * continue through the base class' inheritance hierarchy, up to and
1692      * including {@code rootClass}.
1693      *
1694      * Finds an image with a given name without privileges enabled.
1695      *
1696      * @param baseClass the first class to use in searching for the resource
1697      * @param rootClass an ancestor of {@code baseClass} to finish the
1698      *                  search at
1699      * @param imageFile the name of the file to be found
1700      * @return a lazy value that creates the {@code ImageIcon}
1701      *         {@code UIResource} for the image,
1702      *         or null if it cannot be found
1703      */
1704     public static Object makeIcon_Unprivileged(final Class<?> baseClass,
1705                                   final Class<?> rootClass,
1706                                   final String imageFile) {
1707         return makeIcon(baseClass, rootClass, imageFile, false);
1708     }
1709 
1710     private static Object makeIcon(final Class<?> baseClass,
1711                                   final Class<?> rootClass,
1712                                   final String imageFile,
1713                                   final boolean enablePrivileges) {
1714         return (UIDefaults.LazyValue) (table) -> {
1715             byte[] buffer = enablePrivileges ? AccessController.doPrivileged(
1716                     (PrivilegedAction<byte[]>) ()
1717                     -> getIconBytes(baseClass, rootClass, imageFile))
1718                     : getIconBytes(baseClass, rootClass, imageFile);
1719 
1720             if (buffer == null) {
1721                 return null;
1722             }
1723             if (buffer.length == 0) {
1724                 System.err.println("warning: " + imageFile
1725                         + " is zero-length");
1726                 return null;
1727             }
1728 
1729             return new ImageIconUIResource(buffer);
1730         };
1731     }
1732 
1733     private static byte[] getIconBytes(final Class<?> baseClass,
1734                                   final Class<?> rootClass,
1735                                   final String imageFile) {
1736                 /* Copy resource into a byte array.  This is
1737                  * necessary because several browsers consider
1738                  * Class.getResource a security risk because it
1739                  * can be used to load additional classes.
1740                  * Class.getResourceAsStream just returns raw
1741                  * bytes, which we can convert to an image.
1742                  */
1743                             Class<?> srchClass = baseClass;
1744 
1745                             while (srchClass != null) {
1746 
1747             try (InputStream resource =
1748                     srchClass.getResourceAsStream(imageFile)) {
1749                 if (resource == null) {
1750                     if (srchClass == rootClass) {
1751                                     break;
1752                                 }
1753                                 srchClass = srchClass.getSuperclass();
1754                     continue;
1755                             }
1756 
1757                 try (BufferedInputStream in
1758                         = new BufferedInputStream(resource);
1759                         ByteArrayOutputStream out
1760                         = new ByteArrayOutputStream(1024)) {
1761                             byte[] buffer = new byte[1024];
1762                             int n;
1763                             while ((n = in.read(buffer)) > 0) {
1764                                 out.write(buffer, 0, n);
1765                             }
1766                             out.flush();
1767                             return out.toByteArray();
1768                 }
1769                         } catch (IOException ioe) {
1770                             System.err.println(ioe.toString());
1771                         }
1772         }
1773                         return null;
1774                     }
1775 
1776     /* Used to help decide if AA text rendering should be used, so
1777      * this local display test should be additionally qualified
1778      * against whether we have XRender support on both ends of the wire,
1779      * as with that support remote performance may be good enough to turn
1780      * on by default. An additional complication there is XRender does not
1781      * appear capable of performing gamma correction needed for LCD text.
1782      */
1783     public static boolean isLocalDisplay() {
1784         boolean isLocal;
1785         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1786         if (ge instanceof SunGraphicsEnvironment) {
1787             isLocal = ((SunGraphicsEnvironment) ge).isDisplayLocal();
1788         } else {
1789             isLocal = true;
1790         }
1791         return isLocal;
1792     }
1793 
1794     /**
1795      * Returns an integer from the defaults table. If {@code key} does
1796      * not map to a valid {@code Integer}, or can not be convered from
1797      * a {@code String} to an integer, the value 0 is returned.
1798      *
1799      * @param key  an {@code Object} specifying the int.
1800      * @return the int
1801      */
1802     public static int getUIDefaultsInt(Object key) {
1803         return getUIDefaultsInt(key, 0);
1804     }
1805 
1806     /**
1807      * Returns an integer from the defaults table that is appropriate
1808      * for the given locale. If {@code key} does not map to a valid
1809      * {@code Integer}, or can not be convered from a {@code String}
1810      * to an integer, the value 0 is returned.
1811      *
1812      * @param key  an {@code Object} specifying the int. Returned value
1813      *             is 0 if {@code key} is not available,
1814      * @param l the {@code Locale} for which the int is desired
1815      * @return the int
1816      */
1817     public static int getUIDefaultsInt(Object key, Locale l) {
1818         return getUIDefaultsInt(key, l, 0);
1819     }
1820 
1821     /**
1822      * Returns an integer from the defaults table. If {@code key} does
1823      * not map to a valid {@code Integer}, or can not be convered from
1824      * a {@code String} to an integer, {@code default} is
1825      * returned.
1826      *
1827      * @param key  an {@code Object} specifying the int. Returned value
1828      *             is 0 if {@code key} is not available,
1829      * @param defaultValue Returned value if {@code key} is not available,
1830      *                     or is not an Integer
1831      * @return the int
1832      */
1833     public static int getUIDefaultsInt(Object key, int defaultValue) {
1834         return getUIDefaultsInt(key, null, defaultValue);
1835     }
1836 
1837     /**
1838      * Returns an integer from the defaults table that is appropriate
1839      * for the given locale. If {@code key} does not map to a valid
1840      * {@code Integer}, or can not be convered from a {@code String}
1841      * to an integer, {@code default} is returned.
1842      *
1843      * @param key  an {@code Object} specifying the int. Returned value
1844      *             is 0 if {@code key} is not available,
1845      * @param l the {@code Locale} for which the int is desired
1846      * @param defaultValue Returned value if {@code key} is not available,
1847      *                     or is not an Integer
1848      * @return the int
1849      */
1850     public static int getUIDefaultsInt(Object key, Locale l, int defaultValue) {
1851         Object value = UIManager.get(key, l);
1852 
1853         if (value instanceof Integer) {
1854             return ((Integer)value).intValue();
1855         }
1856         if (value instanceof String) {
1857             try {
1858                 return Integer.parseInt((String)value);
1859             } catch (NumberFormatException nfe) {}
1860         }
1861         return defaultValue;
1862     }
1863 
1864     // At this point we need this method here. But we assume that there
1865     // will be a common method for this purpose in the future releases.
1866     public static Component compositeRequestFocus(Component component) {
1867         if (component instanceof Container) {
1868             Container container = (Container)component;
1869             if (container.isFocusCycleRoot()) {
1870                 FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
1871                 Component comp = policy.getDefaultComponent(container);
1872                 if (comp!=null) {
1873                     comp.requestFocus();
1874                     return comp;
1875                 }
1876             }
1877             Container rootAncestor = container.getFocusCycleRootAncestor();
1878             if (rootAncestor!=null) {
1879                 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
1880                 Component comp = policy.getComponentAfter(rootAncestor, container);
1881 
1882                 if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) {
1883                     comp.requestFocus();
1884                     return comp;
1885                 }
1886             }
1887         }
1888         if (component.isFocusable()) {
1889             component.requestFocus();
1890             return component;
1891         }
1892         return null;
1893     }
1894 
1895     /**
1896      * Change focus to the visible component in {@code JTabbedPane}.
1897      * This is not a general-purpose method and is here only to permit
1898      * sharing code.
1899      */
1900     @SuppressWarnings("deprecation")
1901     public static boolean tabbedPaneChangeFocusTo(Component comp) {
1902         if (comp != null) {
1903             if (comp.isFocusTraversable()) {
1904                 SwingUtilities2.compositeRequestFocus(comp);
1905                 return true;
1906             } else if (comp instanceof JComponent
1907                        && ((JComponent)comp).requestDefaultFocus()) {
1908 
1909                  return true;
1910             }
1911         }
1912 
1913         return false;
1914     }
1915 
1916     /**
1917      * Submits a value-returning task for execution on the EDT and
1918      * returns a Future representing the pending results of the task.
1919      *
1920      * @param task the task to submit
1921      * @return a Future representing pending completion of the task
1922      * @throws NullPointerException if the task is null
1923      */
1924     public static <V> Future<V> submit(Callable<V> task) {
1925         if (task == null) {
1926             throw new NullPointerException();
1927         }
1928         FutureTask<V> future = new FutureTask<V>(task);
1929         execute(future);
1930         return future;
1931     }
1932 
1933     /**
1934      * Submits a Runnable task for execution on the EDT and returns a
1935      * Future representing that task.
1936      *
1937      * @param task the task to submit
1938      * @param result the result to return upon successful completion
1939      * @return a Future representing pending completion of the task,
1940      *         and whose {@code get()} method will return the given
1941      *         result value upon completion
1942      * @throws NullPointerException if the task is null
1943      */
1944     public static <V> Future<V> submit(Runnable task, V result) {
1945         if (task == null) {
1946             throw new NullPointerException();
1947         }
1948         FutureTask<V> future = new FutureTask<V>(task, result);
1949         execute(future);
1950         return future;
1951     }
1952 
1953     /**
1954      * Sends a Runnable to the EDT for the execution.
1955      */
1956     private static void execute(Runnable command) {
1957         SwingUtilities.invokeLater(command);
1958     }
1959 
1960     /**
1961      * Sets the {@code SKIP_CLICK_COUNT} client property on the component
1962      * if it is an instance of {@code JTextComponent} with a
1963      * {@code DefaultCaret}. This property, used for text components acting
1964      * as editors in a table or tree, tells {@code DefaultCaret} how many
1965      * clicks to skip before starting selection.
1966      */
1967     public static void setSkipClickCount(Component comp, int count) {
1968         if (comp instanceof JTextComponent
1969                 && ((JTextComponent) comp).getCaret() instanceof DefaultCaret) {
1970 
1971             ((JTextComponent) comp).putClientProperty(SKIP_CLICK_COUNT, count);
1972         }
1973     }
1974 
1975     /**
1976      * Return the MouseEvent's click count, possibly reduced by the value of
1977      * the component's {@code SKIP_CLICK_COUNT} client property. Clears
1978      * the {@code SKIP_CLICK_COUNT} property if the mouse event's click count
1979      * is 1. In order for clearing of the property to work correctly, there
1980      * must be a mousePressed implementation on the caller with this
1981      * call as the first line.
1982      */
1983     public static int getAdjustedClickCount(JTextComponent comp, MouseEvent e) {
1984         int cc = e.getClickCount();
1985 
1986         if (cc == 1) {
1987             comp.putClientProperty(SKIP_CLICK_COUNT, null);
1988         } else {
1989             Integer sub = (Integer) comp.getClientProperty(SKIP_CLICK_COUNT);
1990             if (sub != null) {
1991                 return cc - sub;
1992             }
1993         }
1994 
1995         return cc;
1996     }
1997 
1998     /**
1999      * Used by the {@code liesIn} method to return which section
2000      * the point lies in.
2001      *
2002      * @see #liesIn
2003      */
2004     public enum Section {
2005 
2006         /** The leading section */
2007         LEADING,
2008 
2009         /** The middle section */
2010         MIDDLE,
2011 
2012         /** The trailing section */
2013         TRAILING
2014     }
2015 
2016     /**
2017      * This method divides a rectangle into two or three sections along
2018      * the specified axis and determines which section the given point
2019      * lies in on that axis; used by drag and drop when calculating drop
2020      * locations.
2021      * <p>
2022      * For two sections, the rectangle is divided equally and the method
2023      * returns whether the point lies in {@code Section.LEADING} or
2024      * {@code Section.TRAILING}. For horizontal divisions, the calculation
2025      * respects component orientation.
2026      * <p>
2027      * For three sections, if the rectangle is greater than or equal to
2028      * 30 pixels in length along the axis, the calculation gives 10 pixels
2029      * to each of the leading and trailing sections and the remainder to the
2030      * middle. For smaller sizes, the rectangle is divided equally into three
2031      * sections.
2032      * <p>
2033      * Note: This method assumes that the point is within the bounds of
2034      * the given rectangle on the specified axis. However, in cases where
2035      * it isn't, the results still have meaning: {@code Section.MIDDLE}
2036      * remains the same, {@code Section.LEADING} indicates that the point
2037      * is in or somewhere before the leading section, and
2038      * {@code Section.TRAILING} indicates that the point is in or somewhere
2039      * after the trailing section.
2040      *
2041      * @param rect the rectangle
2042      * @param p the point the check
2043      * @param horizontal {@code true} to use the horizontal axis,
2044      *        or {@code false} for the vertical axis
2045      * @param ltr {@code true} for left to right orientation,
2046      *        or {@code false} for right to left orientation;
2047      *        only used for horizontal calculations
2048      * @param three {@code true} for three sections,
2049      *        or {@code false} for two
2050      *
2051      * @return the {@code Section} where the point lies
2052      *
2053      * @throws NullPointerException if {@code rect} or {@code p} are
2054      *         {@code null}
2055      */
2056     private static Section liesIn(Rectangle rect, Point p, boolean horizontal,
2057                                   boolean ltr, boolean three) {
2058 
2059         /* beginning of the rectangle on the axis */
2060         int p0;
2061 
2062         /* point on the axis we're interested in */
2063         int pComp;
2064 
2065         /* length of the rectangle on the axis */
2066         int length;
2067 
2068         /* value of ltr if horizontal, else true */
2069         boolean forward;
2070 
2071         if (horizontal) {
2072             p0 = rect.x;
2073             pComp = p.x;
2074             length = rect.width;
2075             forward = ltr;
2076         } else {
2077             p0 = rect.y;
2078             pComp = p.y;
2079             length = rect.height;
2080             forward = true;
2081         }
2082 
2083         if (three) {
2084             int boundary = (length >= 30) ? 10 : length / 3;
2085 
2086             if (pComp < p0 + boundary) {
2087                return forward ? Section.LEADING : Section.TRAILING;
2088            } else if (pComp >= p0 + length - boundary) {
2089                return forward ? Section.TRAILING : Section.LEADING;
2090            }
2091 
2092            return Section.MIDDLE;
2093         } else {
2094             int middle = p0 + length / 2;
2095             if (forward) {
2096                 return pComp >= middle ? Section.TRAILING : Section.LEADING;
2097             } else {
2098                 return pComp < middle ? Section.TRAILING : Section.LEADING;
2099             }
2100         }
2101     }
2102 
2103     /**
2104      * This method divides a rectangle into two or three sections along
2105      * the horizontal axis and determines which section the given point
2106      * lies in; used by drag and drop when calculating drop locations.
2107      * <p>
2108      * See the documentation for {@link #liesIn} for more information
2109      * on how the section is calculated.
2110      *
2111      * @param rect the rectangle
2112      * @param p the point the check
2113      * @param ltr {@code true} for left to right orientation,
2114      *        or {@code false} for right to left orientation
2115      * @param three {@code true} for three sections,
2116      *        or {@code false} for two
2117      *
2118      * @return the {@code Section} where the point lies
2119      *
2120      * @throws NullPointerException if {@code rect} or {@code p} are
2121      *         {@code null}
2122      */
2123     public static Section liesInHorizontal(Rectangle rect, Point p,
2124                                            boolean ltr, boolean three) {
2125         return liesIn(rect, p, true, ltr, three);
2126     }
2127 
2128     /**
2129      * This method divides a rectangle into two or three sections along
2130      * the vertical axis and determines which section the given point
2131      * lies in; used by drag and drop when calculating drop locations.
2132      * <p>
2133      * See the documentation for {@link #liesIn} for more information
2134      * on how the section is calculated.
2135      *
2136      * @param rect the rectangle
2137      * @param p the point the check
2138      * @param three {@code true} for three sections,
2139      *        or {@code false} for two
2140      *
2141      * @return the {@code Section} where the point lies
2142      *
2143      * @throws NullPointerException if {@code rect} or {@code p} are
2144      *         {@code null}
2145      */
2146     public static Section liesInVertical(Rectangle rect, Point p,
2147                                          boolean three) {
2148         return liesIn(rect, p, false, false, three);
2149     }
2150 
2151     /**
2152      * Maps the index of the column in the view at
2153      * {@code viewColumnIndex} to the index of the column
2154      * in the table model.  Returns the index of the corresponding
2155      * column in the model.  If {@code viewColumnIndex}
2156      * is less than zero, returns {@code viewColumnIndex}.
2157      *
2158      * @param cm the table model
2159      * @param   viewColumnIndex     the index of the column in the view
2160      * @return  the index of the corresponding column in the model
2161      *
2162      * @see JTable#convertColumnIndexToModel(int)
2163      * @see javax.swing.plaf.basic.BasicTableHeaderUI
2164      */
2165     public static int convertColumnIndexToModel(TableColumnModel cm,
2166                                                 int viewColumnIndex) {
2167         if (viewColumnIndex < 0) {
2168             return viewColumnIndex;
2169         }
2170         return cm.getColumn(viewColumnIndex).getModelIndex();
2171     }
2172 
2173     /**
2174      * Maps the index of the column in the {@code cm} at
2175      * {@code modelColumnIndex} to the index of the column
2176      * in the view.  Returns the index of the
2177      * corresponding column in the view; returns {@code -1} if this column
2178      * is not being displayed. If {@code modelColumnIndex} is less than zero,
2179      * returns {@code modelColumnIndex}.
2180      *
2181      * @param cm the table model
2182      * @param modelColumnIndex the index of the column in the model
2183      * @return the index of the corresponding column in the view
2184      *
2185      * @see JTable#convertColumnIndexToView(int)
2186      * @see javax.swing.plaf.basic.BasicTableHeaderUI
2187      */
2188     public static int convertColumnIndexToView(TableColumnModel cm,
2189                                         int modelColumnIndex) {
2190         if (modelColumnIndex < 0) {
2191             return modelColumnIndex;
2192         }
2193         for (int column = 0; column < cm.getColumnCount(); column++) {
2194             if (cm.getColumn(column).getModelIndex() == modelColumnIndex) {
2195                 return column;
2196             }
2197         }
2198         return -1;
2199     }
2200 
2201     /**
2202      * Sets the InputEvent.ALT_GRAPH mask on any modifier passed to the function
2203      * @param modifier the modifier passed
2204      * @return the modifier retiurned with ALT_GRAPH flag set
2205      */
2206     public static int setAltGraphMask(int modifier) {
2207         return (modifier | InputEvent.ALT_GRAPH_DOWN_MASK);
2208     }
2209 
2210     @SuppressWarnings("deprecation")
2211     public static int getSystemMnemonicKeyMask() {
2212         Toolkit toolkit = Toolkit.getDefaultToolkit();
2213         if (toolkit instanceof SunToolkit) {
2214             return ((SunToolkit) toolkit).getFocusAcceleratorKeyMask();
2215         }
2216         return InputEvent.ALT_MASK;
2217     }
2218 
2219     /**
2220      * Returns the {@link TreePath} that identifies the changed nodes.
2221      *
2222      * @param event  changes in a tree model
2223      * @param model  corresponing tree model
2224      * @return  the path to the changed nodes
2225      */
2226     public static TreePath getTreePath(TreeModelEvent event, TreeModel model) {
2227         TreePath path = event.getTreePath();
2228         if ((path == null) && (model != null)) {
2229             Object root = model.getRoot();
2230             if (root != null) {
2231                 path = new TreePath(root);
2232             }
2233         }
2234         return path;
2235     }
2236 
2237     public static boolean isScaledGraphics(Graphics g) {
2238         if (g instanceof Graphics2D) {
2239             AffineTransform tx = ((Graphics2D) g).getTransform();
2240             return (tx.getType() & ~(TYPE_TRANSLATION | TYPE_FLIP)) != 0;
2241         }
2242         return false;
2243     }
2244 
2245     /**
2246      * Enables the antialiasing rendering hint for the scaled graphics and
2247      * returns the previous hint value.
2248      * The returned null value indicates that the passed graphics is not
2249      * instance of Graphics2D.
2250      *
2251      * @param g the graphics
2252      * @return the previous antialiasing rendering hint value if the passed
2253      * graphics is instance of Graphics2D, null otherwise.
2254      */
2255     public static Object getAndSetAntialisingHintForScaledGraphics(Graphics g) {
2256         if (isScaledGraphics(g) && isLocalDisplay()) {
2257             Graphics2D g2d = (Graphics2D) g;
2258             Object hint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
2259             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2260                     RenderingHints.VALUE_ANTIALIAS_ON);
2261             return hint;
2262         }
2263         return null;
2264     }
2265 
2266     /**
2267      * Sets the antialiasing rendering hint if its value is not null.
2268      * Null hint value indicates that the passed graphics is not instance of
2269      * Graphics2D.
2270      *
2271      * @param g the graphics
2272      * @param hint the antialiasing rendering hint
2273      */
2274     public static void setAntialiasingHintForScaledGraphics(Graphics g, Object hint) {
2275         if (hint != null) {
2276             ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
2277         }
2278     }
2279 
2280     public static boolean isFloatingPointScale(AffineTransform tx) {
2281         int type = tx.getType() & ~(TYPE_FLIP | TYPE_TRANSLATION);
2282         if (type == 0) {
2283             return false;
2284         } else if ((type & ~TYPE_MASK_SCALE) == 0) {
2285             double scaleX = tx.getScaleX();
2286             double scaleY = tx.getScaleY();
2287             return (scaleX != (int) scaleX) || (scaleY != (int) scaleY);
2288         } else {
2289             return false;
2290         }
2291     }
2292 
2293     /**
2294      * Returns the client property for the given key if it is set; otherwise
2295      * returns the {@L&F} property.
2296      *
2297      * @param component the component
2298      * @param key an {@code String} specifying the key for the desired boolean value
2299      * @return the boolean value of the client property if it is set or the {@L&F}
2300      *         property in other case.
2301      */
2302     public static boolean getBoolean(JComponent component, String key) {
2303         Object clientProperty = component.getClientProperty(key);
2304 
2305         if (clientProperty instanceof Boolean) {
2306             return Boolean.TRUE.equals(clientProperty);
2307         }
2308 
2309         return UIManager.getBoolean(key);
2310     }
2311 
2312     /**
2313      * Used to listen to "blit" repaints in RepaintManager.
2314      */
2315     public interface RepaintListener {
2316         void repaintPerformed(JComponent c, int x, int y, int w, int h);
2317     }
2318 
2319     /**
2320      * Returns whether or not the scale used by {@code GraphicsConfiguration}
2321      * was changed.
2322      *
2323      * @param  ev a {@code PropertyChangeEvent}
2324      * @return whether or not the scale was changed
2325      * @since 11
2326      */
2327     public static boolean isScaleChanged(final PropertyChangeEvent ev) {
2328         return isScaleChanged(ev.getPropertyName(), ev.getOldValue(),
2329                               ev.getNewValue());
2330     }
2331 
2332     /**
2333      * Returns whether or not the scale used by {@code GraphicsConfiguration}
2334      * was changed.
2335      *
2336      * @param  name the name of the property
2337      * @param  oldValue the old value of the property
2338      * @param  newValue the new value of the property
2339      * @return whether or not the scale was changed
2340      * @since 11
2341      */
2342     public static boolean isScaleChanged(final String name,
2343                                          final Object oldValue,
2344                                          final Object newValue) {
2345         if (oldValue == newValue || !"graphicsConfiguration".equals(name)) {
2346             return false;
2347         }
2348         var newGC = (GraphicsConfiguration) oldValue;
2349         var oldGC = (GraphicsConfiguration) newValue;
2350         var newTx = newGC != null ? newGC.getDefaultTransform() : null;
2351         var oldTx = oldGC != null ? oldGC.getDefaultTransform() : null;
2352         return !Objects.equals(newTx, oldTx);
2353     }
2354 }