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