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