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