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