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