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