1 /*
   2  * Copyright (c) 2002, 2013, 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.security.*;
  29 import java.lang.reflect.*;
  30 import java.awt.*;
  31 import static java.awt.RenderingHints.*;
  32 import java.awt.event.*;
  33 import java.awt.font.*;
  34 import java.awt.geom.*;
  35 import java.awt.print.PrinterGraphics;
  36 import java.text.CharacterIterator;
  37 import java.text.AttributedCharacterIterator;
  38 import java.text.AttributedString;
  39 
  40 import javax.swing.*;
  41 import javax.swing.event.TreeModelEvent;
  42 import javax.swing.text.Highlighter;
  43 import javax.swing.text.JTextComponent;
  44 import javax.swing.text.DefaultHighlighter;
  45 import javax.swing.text.DefaultCaret;
  46 import javax.swing.table.TableCellRenderer;
  47 import javax.swing.table.TableColumnModel;
  48 import javax.swing.tree.TreeModel;
  49 import javax.swing.tree.TreePath;
  50 
  51 import sun.swing.PrintColorUIResource;
  52 import sun.swing.ImageIconUIResource;
  53 import sun.print.ProxyPrintGraphics;
  54 import sun.awt.*;
  55 import sun.security.action.GetPropertyAction;
  56 import java.io.*;
  57 import java.util.*;
  58 import sun.font.FontDesignMetrics;
  59 import sun.font.FontUtilities;
  60 import sun.java2d.SunGraphicsEnvironment;
  61 
  62 import java.util.concurrent.Callable;
  63 import java.util.concurrent.Future;
  64 import java.util.concurrent.FutureTask;
  65 
  66 /**
  67  * A collection of utility methods for Swing.
  68  * <p>
  69  * <b>WARNING:</b> While this class is public, it should not be treated as
  70  * public API and its API may change in incompatable ways between dot dot
  71  * releases and even patch releases. You should not rely on this class even
  72  * existing.
  73  *
  74  */
  75 public class SwingUtilities2 {
  76     /**
  77      * The <code>AppContext</code> key for our one <code>LAFState</code>
  78      * instance.
  79      */
  80     public static final Object LAF_STATE_KEY =
  81             new StringBuffer("LookAndFeel State");
  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 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         public static AATextInfo getAATextInfo(boolean lafCondition) {
 145             SunToolkit.setAAFontSettingsCondition(lafCondition);
 146             Toolkit tk = Toolkit.getDefaultToolkit();
 147             Object map = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS);
 148             if (map instanceof Map) {
 149                 return getAATextInfoFromMap((Map)map);
 150             } else {
 151                 return null;
 152             }
 153         }
 154 
 155         Object aaHint;
 156         Integer lcdContrastHint;
 157         FontRenderContext frc;
 158 
 159         /* These are rarely constructed objects, and only when a complete
 160          * UI is being updated, so the cost of the tests here is minimal
 161          * and saves tests elsewhere.
 162          * We test that the values are ones we support/expect.
 163          */
 164         public AATextInfo(Object aaHint, Integer lcdContrastHint) {
 165             if (aaHint == null) {
 166                 throw new InternalError("null not allowed here");
 167             }
 168             if (aaHint == VALUE_TEXT_ANTIALIAS_OFF ||
 169                 aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) {
 170                 throw new InternalError("AA must be on");
 171             }
 172             this.aaHint = aaHint;
 173             this.lcdContrastHint = lcdContrastHint;
 174             this.frc = new FontRenderContext(null, aaHint,
 175                                              VALUE_FRACTIONALMETRICS_DEFAULT);
 176         }
 177     }
 178 
 179     /**
 180      * Key used in client properties used to indicate that the
 181      * <code>ComponentUI</code> of the JComponent instance should be returned.
 182      */
 183     public static final Object COMPONENT_UI_PROPERTY_KEY =
 184                             new StringBuffer("ComponentUIPropertyKey");
 185 
 186     /** Client Property key for the text maximal offsets for BasicMenuItemUI */
 187     public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET =
 188         new StringUIClientPropertyKey ("maxTextOffset");
 189 
 190     // security stuff
 191     private static Field inputEvent_CanAccessSystemClipboard_Field = null;
 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 boolean pointIsInActualBounds(JList list, int index,
 684                                                 Point point) {
 685         ListCellRenderer renderer = list.getCellRenderer();
 686         ListModel dataModel = list.getModel();
 687         Object value = dataModel.getElementAt(index);
 688         Component item = renderer.getListCellRendererComponent(list,
 689                           value, index, false, false);
 690         Dimension itemSize = item.getPreferredSize();
 691         Rectangle cellBounds = list.getCellBounds(index, index);
 692         if (!item.getComponentOrientation().isLeftToRight()) {
 693             cellBounds.x += (cellBounds.width - itemSize.width);
 694         }
 695         cellBounds.width = itemSize.width;
 696 
 697         return cellBounds.contains(point);
 698     }
 699 
 700 
 701     /**
 702      * Returns true if the given point is outside the preferredSize of the
 703      * item at the given row of the table.  (Column must be 0).
 704      * Does not check the "Table.isFileList" property. That should be checked
 705      * before calling this method.
 706      * This is used to make WindowsL&F JFileChooser act like native dialogs.
 707      */
 708     public static boolean pointOutsidePrefSize(JTable table, int row, int column, Point p) {
 709         if (table.convertColumnIndexToModel(column) != 0 || row == -1) {
 710             return true;
 711         }
 712         TableCellRenderer tcr = table.getCellRenderer(row, column);
 713         Object value = table.getValueAt(row, column);
 714         Component cell = tcr.getTableCellRendererComponent(table, value, false,
 715                 false, row, column);
 716         Dimension itemSize = cell.getPreferredSize();
 717         Rectangle cellBounds = table.getCellRect(row, column, false);
 718         cellBounds.width = itemSize.width;
 719         cellBounds.height = itemSize.height;
 720 
 721         // See if coords are inside
 722         // ASSUME: mouse x,y will never be < cell's x,y
 723         assert (p.x >= cellBounds.x && p.y >= cellBounds.y);
 724         return p.x > cellBounds.x + cellBounds.width ||
 725                 p.y > cellBounds.y + cellBounds.height;
 726     }
 727 
 728     /**
 729      * Set the lead and anchor without affecting selection.
 730      */
 731     public static void setLeadAnchorWithoutSelection(ListSelectionModel model,
 732                                                      int lead, int anchor) {
 733         if (anchor == -1) {
 734             anchor = lead;
 735         }
 736         if (lead == -1) {
 737             model.setAnchorSelectionIndex(-1);
 738             model.setLeadSelectionIndex(-1);
 739         } else {
 740             if (model.isSelectedIndex(lead)) {
 741                 model.addSelectionInterval(lead, lead);
 742             } else {
 743                 model.removeSelectionInterval(lead, lead);
 744             }
 745             model.setAnchorSelectionIndex(anchor);
 746         }
 747     }
 748 
 749     /**
 750      * Ignore mouse events if the component is null, not enabled, the event
 751      * is not associated with the left mouse button, or the event has been
 752      * consumed.
 753      */
 754     public static boolean shouldIgnore(MouseEvent me, JComponent c) {
 755         return c == null || !c.isEnabled()
 756                          || !SwingUtilities.isLeftMouseButton(me)
 757                          || me.isConsumed();
 758     }
 759 
 760     /**
 761      * Request focus on the given component if it doesn't already have it
 762      * and <code>isRequestFocusEnabled()</code> returns true.
 763      */
 764     public static void adjustFocus(JComponent c) {
 765         if (!c.hasFocus() && c.isRequestFocusEnabled()) {
 766             c.requestFocus();
 767         }
 768     }
 769 
 770     /**
 771      * The following draw functions have the same semantic as the
 772      * Graphics methods with the same names.
 773      *
 774      * this is used for printing
 775      */
 776     public static int drawChars(JComponent c, Graphics g,
 777                                  char[] data,
 778                                  int offset,
 779                                  int length,
 780                                  int x,
 781                                  int y) {
 782         if ( length <= 0 ) { //no need to paint empty strings
 783             return x;
 784         }
 785         int nextX = x + getFontMetrics(c, g).charsWidth(data, offset, length);
 786         if (isPrinting(g)) {
 787             Graphics2D g2d = getGraphics2D(g);
 788             if (g2d != null) {
 789                 FontRenderContext deviceFontRenderContext = g2d.
 790                     getFontRenderContext();
 791                 FontRenderContext frc = getFontRenderContext(c);
 792                 if (frc != null &&
 793                     !isFontRenderContextPrintCompatible
 794                     (deviceFontRenderContext, frc)) {
 795 
 796                     String text = new String(data, offset, length);
 797                     TextLayout layout = new TextLayout(text, g2d.getFont(),
 798                                     deviceFontRenderContext);
 799                     String trimmedText = trimTrailingSpaces(text);
 800                     if (!trimmedText.isEmpty()) {
 801                         float screenWidth = (float)g2d.getFont().
 802                             getStringBounds(trimmedText, frc).getWidth();
 803                         layout = layout.getJustifiedLayout(screenWidth);
 804 
 805                         /* Use alternate print color if specified */
 806                         Color col = g2d.getColor();
 807                         if (col instanceof PrintColorUIResource) {
 808                             g2d.setColor(((PrintColorUIResource)col).getPrintColor());
 809                         }
 810 
 811                         layout.draw(g2d,x,y);
 812 
 813                         g2d.setColor(col);
 814                     }
 815 
 816                     return nextX;
 817                 }
 818             }
 819         }
 820         // Assume we're not printing if we get here, or that we are invoked
 821         // via Swing text printing which is laid out for the printer.
 822         AATextInfo info = drawTextAntialiased(c);
 823         if (info != null && (g instanceof Graphics2D)) {
 824             Graphics2D g2 = (Graphics2D)g;
 825 
 826             Object oldContrast = null;
 827             Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
 828             if (info.aaHint != null && info.aaHint != oldAAValue) {
 829                 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, info.aaHint);
 830             } else {
 831                 oldAAValue = null;
 832             }
 833             if (info.lcdContrastHint != null) {
 834                 oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);
 835                 if (info.lcdContrastHint.equals(oldContrast)) {
 836                     oldContrast = null;
 837                 } else {
 838                     g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,
 839                                         info.lcdContrastHint);
 840                 }
 841             }
 842 
 843             g.drawChars(data, offset, length, x, y);
 844 
 845             if (oldAAValue != null) {
 846                 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
 847             }
 848             if (oldContrast != null) {
 849                 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);
 850             }
 851         }
 852         else {
 853             g.drawChars(data, offset, length, x, y);
 854         }
 855         return nextX;
 856     }
 857 
 858     /*
 859      * see documentation for drawChars
 860      * returns the advance
 861      */
 862     public static float drawString(JComponent c, Graphics g,
 863                                    AttributedCharacterIterator iterator,
 864                                    int x,
 865                                    int y) {
 866 
 867         float retVal;
 868         boolean isPrinting = isPrinting(g);
 869         Color col = g.getColor();
 870 
 871         if (isPrinting) {
 872             /* Use alternate print color if specified */
 873             if (col instanceof PrintColorUIResource) {
 874                 g.setColor(((PrintColorUIResource)col).getPrintColor());
 875             }
 876         }
 877 
 878         Graphics2D g2d = getGraphics2D(g);
 879         if (g2d == null) {
 880             g.drawString(iterator,x,y); //for the cases where advance
 881                                         //matters it should not happen
 882             retVal = x;
 883 
 884         } else {
 885             FontRenderContext frc;
 886             if (isPrinting) {
 887                 frc = getFontRenderContext(c);
 888                 if (frc.isAntiAliased() || frc.usesFractionalMetrics()) {
 889                     frc = new FontRenderContext(frc.getTransform(), false, false);
 890                 }
 891             } else if ((frc = getFRCProperty(c)) != null) {
 892                 /* frc = frc; ! */
 893             } else {
 894                 frc = g2d.getFontRenderContext();
 895             }
 896             TextLayout layout;
 897             if (isPrinting) {
 898                 FontRenderContext deviceFRC = g2d.getFontRenderContext();
 899                 if (!isFontRenderContextPrintCompatible(frc, deviceFRC)) {
 900                     layout = new TextLayout(iterator, deviceFRC);
 901                     AttributedCharacterIterator trimmedIt =
 902                             getTrimmedTrailingSpacesIterator(iterator);
 903                     if (trimmedIt != null) {
 904                         float screenWidth = new TextLayout(trimmedIt, frc).
 905                                 getAdvance();
 906                         layout = layout.getJustifiedLayout(screenWidth);
 907                     }
 908                 } else {
 909                     layout = new TextLayout(iterator, frc);
 910                 }
 911             } else {
 912                 layout = new TextLayout(iterator, frc);
 913             }
 914             layout.draw(g2d, x, y);
 915             retVal = layout.getAdvance();
 916         }
 917 
 918         if (isPrinting) {
 919             g.setColor(col);
 920         }
 921 
 922         return retVal;
 923     }
 924 
 925     private static TextLayout createTextLayout(JComponent c, String s,
 926                                             Font f, FontRenderContext frc) {
 927         Object shaper = (c == null ?
 928                     null : c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
 929         if (shaper == null) {
 930             return new TextLayout(s, f, frc);
 931         } else {
 932             Map<TextAttribute, Object> a = new HashMap<TextAttribute, Object>();
 933             a.put(TextAttribute.FONT, f);
 934             a.put(TextAttribute.NUMERIC_SHAPING, shaper);
 935             return new TextLayout(s, a, frc);
 936         }
 937     }
 938 
 939     /*
 940      * Checks if two given FontRenderContexts are compatible for printing.
 941      * We can't just use equals as we want to exclude from the comparison :
 942      * + whether AA is set as irrelevant for printing and shouldn't affect
 943      * printed metrics anyway
 944      * + any translation component in the transform of either FRC, as it
 945      * does not affect metrics.
 946      * Compatible means no special handling needed for text painting
 947      */
 948     private static boolean
 949         isFontRenderContextPrintCompatible(FontRenderContext frc1,
 950                                            FontRenderContext frc2) {
 951 
 952         if (frc1 == frc2) {
 953             return true;
 954         }
 955 
 956         if (frc1 == null || frc2 == null) { // not supposed to happen
 957             return false;
 958         }
 959 
 960         if (frc1.getFractionalMetricsHint() !=
 961             frc2.getFractionalMetricsHint()) {
 962             return false;
 963         }
 964 
 965         /* If both are identity, return true */
 966         if (!frc1.isTransformed() && !frc2.isTransformed()) {
 967             return true;
 968         }
 969 
 970         /* That's the end of the cheap tests, need to get and compare
 971          * the transform matrices. We don't care about the translation
 972          * components, so return true if they are otherwise identical.
 973          */
 974         double[] mat1 = new double[4];
 975         double[] mat2 = new double[4];
 976         frc1.getTransform().getMatrix(mat1);
 977         frc2.getTransform().getMatrix(mat2);
 978         return
 979             mat1[0] == mat2[0] &&
 980             mat1[1] == mat2[1] &&
 981             mat1[2] == mat2[2] &&
 982             mat1[3] == mat2[3];
 983     }
 984 
 985     /*
 986      * Tries it best to get Graphics2D out of the given Graphics
 987      * returns null if can not derive it.
 988      */
 989     public static Graphics2D getGraphics2D(Graphics g) {
 990         if (g instanceof Graphics2D) {
 991             return (Graphics2D) g;
 992         } else if (g instanceof ProxyPrintGraphics) {
 993             return (Graphics2D)(((ProxyPrintGraphics)g).getGraphics());
 994         } else {
 995             return null;
 996         }
 997     }
 998 
 999     /*
1000      * Returns FontRenderContext associated with Component.
1001      * FontRenderContext from Component.getFontMetrics is associated
1002      * with the component.
1003      *
1004      * Uses Component.getFontMetrics to get the FontRenderContext from.
1005      * see JComponent.getFontMetrics and TextLayoutStrategy.java
1006      */
1007     public static FontRenderContext getFontRenderContext(Component c) {
1008         assert c != null;
1009         if (c == null) {
1010             return DEFAULT_FRC;
1011         } else {
1012             return c.getFontMetrics(c.getFont()).getFontRenderContext();
1013         }
1014     }
1015 
1016     /**
1017      * A convenience method to get FontRenderContext.
1018      * Returns the FontRenderContext for the passed in FontMetrics or
1019      * for the passed in Component if FontMetrics is null
1020      */
1021     private static FontRenderContext getFontRenderContext(Component c, FontMetrics fm) {
1022         assert fm != null || c!= null;
1023         return (fm != null) ? fm.getFontRenderContext()
1024             : getFontRenderContext(c);
1025     }
1026 
1027     /*
1028      * This method is to be used only for JComponent.getFontMetrics.
1029      * In all other places to get FontMetrics we need to use
1030      * JComponent.getFontMetrics.
1031      *
1032      */
1033     public static FontMetrics getFontMetrics(JComponent c, Font font) {
1034         FontRenderContext  frc = getFRCProperty(c);
1035         if (frc == null) {
1036             frc = DEFAULT_FRC;
1037         }
1038         return FontDesignMetrics.getMetrics(font, frc);
1039     }
1040 
1041 
1042     /* Get any FontRenderContext associated with a JComponent
1043      * - may return null
1044      */
1045     private static FontRenderContext getFRCProperty(JComponent c) {
1046         if (c != null) {
1047             AATextInfo info =
1048                 (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY);
1049             if (info != null) {
1050                 return info.frc;
1051             }
1052         }
1053         return null;
1054     }
1055 
1056     /*
1057      * returns true if the Graphics is print Graphics
1058      * false otherwise
1059      */
1060     static boolean isPrinting(Graphics g) {
1061         return (g instanceof PrinterGraphics || g instanceof PrintGraphics);
1062     }
1063 
1064     private static String trimTrailingSpaces(String s) {
1065         int i = s.length() - 1;
1066         while(i >= 0 && Character.isWhitespace(s.charAt(i))) {
1067             i--;
1068         }
1069         return s.substring(0, i + 1);
1070     }
1071 
1072     private static AttributedCharacterIterator getTrimmedTrailingSpacesIterator
1073             (AttributedCharacterIterator iterator) {
1074         int curIdx = iterator.getIndex();
1075 
1076         char c = iterator.last();
1077         while(c != CharacterIterator.DONE && Character.isWhitespace(c)) {
1078             c = iterator.previous();
1079         }
1080 
1081         if (c != CharacterIterator.DONE) {
1082             int endIdx = iterator.getIndex();
1083 
1084             if (endIdx == iterator.getEndIndex() - 1) {
1085                 iterator.setIndex(curIdx);
1086                 return iterator;
1087             } else {
1088                 AttributedString trimmedText = new AttributedString(iterator,
1089                         iterator.getBeginIndex(), endIdx + 1);
1090                 return trimmedText.getIterator();
1091             }
1092         } else {
1093             return null;
1094         }
1095     }
1096 
1097     /**
1098      * Determines whether the SelectedTextColor should be used for painting text
1099      * foreground for the specified highlight.
1100      *
1101      * Returns true only if the highlight painter for the specified highlight
1102      * is the swing painter (whether inner class of javax.swing.text.DefaultHighlighter
1103      * or com.sun.java.swing.plaf.windows.WindowsTextUI) and its background color
1104      * is null or equals to the selection color of the text component.
1105      *
1106      * This is a hack for fixing both bugs 4761990 and 5003294
1107      */
1108     public static boolean useSelectedTextColor(Highlighter.Highlight h, JTextComponent c) {
1109         Highlighter.HighlightPainter painter = h.getPainter();
1110         String painterClass = painter.getClass().getName();
1111         if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&
1112                 painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {
1113             return false;
1114         }
1115         try {
1116             DefaultHighlighter.DefaultHighlightPainter defPainter =
1117                     (DefaultHighlighter.DefaultHighlightPainter) painter;
1118             if (defPainter.getColor() != null &&
1119                     !defPainter.getColor().equals(c.getSelectionColor())) {
1120                 return false;
1121             }
1122         } catch (ClassCastException e) {
1123             return false;
1124         }
1125         return true;
1126     }
1127 
1128     /**
1129      * LSBCacheEntry is used to cache the left side bearing (lsb) for
1130      * a particular <code>Font</code> and <code>FontRenderContext</code>.
1131      * This only caches characters that fall in the range
1132      * <code>MIN_CHAR_INDEX</code> to <code>MAX_CHAR_INDEX</code>.
1133      */
1134     private static class LSBCacheEntry {
1135         // Used to indicate a particular entry in lsb has not been set.
1136         private static final byte UNSET = Byte.MAX_VALUE;
1137         // Used in creating a GlyphVector to get the lsb
1138         private static final char[] oneChar = new char[1];
1139 
1140         private byte[] lsbCache;
1141         private Font font;
1142         private FontRenderContext frc;
1143 
1144 
1145         public LSBCacheEntry(FontRenderContext frc, Font font) {
1146             lsbCache = new byte[MAX_CHAR_INDEX - MIN_CHAR_INDEX];
1147             reset(frc, font);
1148 
1149         }
1150 
1151         public void reset(FontRenderContext frc, Font font) {
1152             this.font = font;
1153             this.frc = frc;
1154             for (int counter = lsbCache.length - 1; counter >= 0; counter--) {
1155                 lsbCache[counter] = UNSET;
1156             }
1157         }
1158 
1159         public int getLeftSideBearing(char aChar) {
1160             int index = aChar - MIN_CHAR_INDEX;
1161             assert (index >= 0 && index < (MAX_CHAR_INDEX - MIN_CHAR_INDEX));
1162             byte lsb = lsbCache[index];
1163             if (lsb == UNSET) {
1164                 oneChar[0] = aChar;
1165                 GlyphVector gv = font.createGlyphVector(frc, oneChar);
1166                 lsb = (byte) gv.getGlyphPixelBounds(0, frc, 0f, 0f).x;
1167                 if (lsb < 0) {
1168                     /* HRGB/HBGR LCD glyph images will always have a pixel
1169                      * on the left used in colour fringe reduction.
1170                      * Text rendering positions this correctly but here
1171                      * we are using the glyph image to adjust that position
1172                      * so must account for it.
1173                      */
1174                     Object aaHint = frc.getAntiAliasingHint();
1175                     if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
1176                             aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
1177                         lsb++;
1178                     }
1179                 }
1180                 lsbCache[index] = lsb;
1181             }
1182             return lsb;
1183 
1184 
1185         }
1186 
1187         public boolean equals(Object entry) {
1188             if (entry == this) {
1189                 return true;
1190             }
1191             if (!(entry instanceof LSBCacheEntry)) {
1192                 return false;
1193             }
1194             LSBCacheEntry oEntry = (LSBCacheEntry) entry;
1195             return (font.equals(oEntry.font) &&
1196                     frc.equals(oEntry.frc));
1197         }
1198 
1199         public int hashCode() {
1200             int result = 17;
1201             if (font != null) {
1202                 result = 37 * result + font.hashCode();
1203             }
1204             if (frc != null) {
1205                 result = 37 * result + frc.hashCode();
1206             }
1207             return result;
1208         }
1209     }
1210 
1211     /*
1212      * here goes the fix for 4856343 [Problem with applet interaction
1213      * with system selection clipboard]
1214      *
1215      * NOTE. In case isTrustedContext() no checking
1216      * are to be performed
1217      */
1218 
1219     /**
1220     * checks the security permissions for accessing system clipboard
1221     *
1222     * for untrusted context (see isTrustedContext) checks the
1223     * permissions for the current event being handled
1224     *
1225     */
1226    public static boolean canAccessSystemClipboard() {
1227        boolean canAccess = false;
1228        if (!GraphicsEnvironment.isHeadless()) {
1229            SecurityManager sm = System.getSecurityManager();
1230            if (sm == null) {
1231                canAccess = true;
1232            } else {
1233                try {
1234                    sm.checkPermission(AWTPermissions.ACCESS_CLIPBOARD_PERMISSION);
1235                    canAccess = true;
1236                } catch (SecurityException e) {
1237                }
1238                if (canAccess && ! isTrustedContext()) {
1239                    canAccess = canCurrentEventAccessSystemClipboard(true);
1240                }
1241            }
1242        }
1243        return canAccess;
1244    }
1245     /**
1246     * Returns true if EventQueue.getCurrentEvent() has the permissions to
1247      * access the system clipboard
1248      */
1249     public static boolean canCurrentEventAccessSystemClipboard() {
1250         return  isTrustedContext()
1251             || canCurrentEventAccessSystemClipboard(false);
1252     }
1253 
1254     /**
1255      * Returns true if the given event has permissions to access the
1256      * system clipboard
1257      *
1258      * @param e AWTEvent to check
1259      */
1260     public static boolean canEventAccessSystemClipboard(AWTEvent e) {
1261         return isTrustedContext()
1262             || canEventAccessSystemClipboard(e, false);
1263     }
1264 
1265     /**
1266      * returns canAccessSystemClipboard field from InputEvent
1267      *
1268      * @param ie InputEvent to get the field from
1269      */
1270     private static synchronized boolean inputEvent_canAccessSystemClipboard(InputEvent ie) {
1271         if (inputEvent_CanAccessSystemClipboard_Field == null) {
1272             inputEvent_CanAccessSystemClipboard_Field =
1273                 AccessController.doPrivileged(
1274                     new java.security.PrivilegedAction<Field>() {
1275                         public Field run() {
1276                             try {
1277                                 Field field = InputEvent.class.
1278                                     getDeclaredField("canAccessSystemClipboard");
1279                                 field.setAccessible(true);
1280                                 return field;
1281                             } catch (SecurityException e) {
1282                             } catch (NoSuchFieldException e) {
1283                             }
1284                             return null;
1285                         }
1286                     });
1287         }
1288         if (inputEvent_CanAccessSystemClipboard_Field == null) {
1289             return false;
1290         }
1291         boolean ret = false;
1292         try {
1293             ret = inputEvent_CanAccessSystemClipboard_Field.
1294                 getBoolean(ie);
1295         } catch(IllegalAccessException e) {
1296         }
1297         return ret;
1298     }
1299 
1300     /**
1301      * Returns true if the given event is corrent gesture for
1302      * accessing clipboard
1303      *
1304      * @param ie InputEvent to check
1305      */
1306 
1307     private static boolean isAccessClipboardGesture(InputEvent ie) {
1308         boolean allowedGesture = false;
1309         if (ie instanceof KeyEvent) { //we can validate only keyboard gestures
1310             KeyEvent ke = (KeyEvent)ie;
1311             int keyCode = ke.getKeyCode();
1312             int keyModifiers = ke.getModifiers();
1313             switch(keyCode) {
1314             case KeyEvent.VK_C:
1315             case KeyEvent.VK_V:
1316             case KeyEvent.VK_X:
1317                 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK);
1318                 break;
1319             case KeyEvent.VK_INSERT:
1320                 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK ||
1321                                   keyModifiers == InputEvent.SHIFT_MASK);
1322                 break;
1323             case KeyEvent.VK_COPY:
1324             case KeyEvent.VK_PASTE:
1325             case KeyEvent.VK_CUT:
1326                 allowedGesture = true;
1327                 break;
1328             case KeyEvent.VK_DELETE:
1329                 allowedGesture = ( keyModifiers == InputEvent.SHIFT_MASK);
1330                 break;
1331             }
1332         }
1333         return allowedGesture;
1334     }
1335 
1336     /**
1337      * Returns true if e has the permissions to
1338      * access the system clipboard and if it is allowed gesture (if
1339      * checkGesture is true)
1340      *
1341      * @param e AWTEvent to check
1342      * @param checkGesture boolean
1343      */
1344     private static boolean canEventAccessSystemClipboard(AWTEvent e,
1345                                                         boolean checkGesture) {
1346         if (EventQueue.isDispatchThread()) {
1347             /*
1348              * Checking event permissions makes sense only for event
1349              * dispathing thread
1350              */
1351             if (e instanceof InputEvent
1352                 && (! checkGesture || isAccessClipboardGesture((InputEvent)e))) {
1353                 return inputEvent_canAccessSystemClipboard((InputEvent)e);
1354             } else {
1355                 return false;
1356             }
1357         } else {
1358             return true;
1359         }
1360     }
1361 
1362     /**
1363      * Utility method that throws SecurityException if SecurityManager is set
1364      * and modifiers are not public
1365      *
1366      * @param modifiers a set of modifiers
1367      */
1368     public static void checkAccess(int modifiers) {
1369         if (System.getSecurityManager() != null
1370                 && !Modifier.isPublic(modifiers)) {
1371             throw new SecurityException("Resource is not accessible");
1372         }
1373     }
1374 
1375     /**
1376      * Returns true if EventQueue.getCurrentEvent() has the permissions to
1377      * access the system clipboard and if it is allowed gesture (if
1378      * checkGesture true)
1379      *
1380      * @param checkGesture boolean
1381      */
1382     private static boolean canCurrentEventAccessSystemClipboard(boolean
1383                                                                checkGesture) {
1384         AWTEvent event = EventQueue.getCurrentEvent();
1385         return canEventAccessSystemClipboard(event, checkGesture);
1386     }
1387 
1388     /**
1389      * see RFE 5012841 [Per AppContect security permissions] for the
1390      * details
1391      *
1392      */
1393     private static boolean isTrustedContext() {
1394         return (System.getSecurityManager() == null)
1395             || (AppContext.getAppContext().
1396                 get(UntrustedClipboardAccess) == null);
1397     }
1398 
1399     public static String displayPropertiesToCSS(Font font, Color fg) {
1400         StringBuffer rule = new StringBuffer("body {");
1401         if (font != null) {
1402             rule.append(" font-family: ");
1403             rule.append(font.getFamily());
1404             rule.append(" ; ");
1405             rule.append(" font-size: ");
1406             rule.append(font.getSize());
1407             rule.append("pt ;");
1408             if (font.isBold()) {
1409                 rule.append(" font-weight: 700 ; ");
1410             }
1411             if (font.isItalic()) {
1412                 rule.append(" font-style: italic ; ");
1413             }
1414         }
1415         if (fg != null) {
1416             rule.append(" color: #");
1417             if (fg.getRed() < 16) {
1418                 rule.append('0');
1419             }
1420             rule.append(Integer.toHexString(fg.getRed()));
1421             if (fg.getGreen() < 16) {
1422                 rule.append('0');
1423             }
1424             rule.append(Integer.toHexString(fg.getGreen()));
1425             if (fg.getBlue() < 16) {
1426                 rule.append('0');
1427             }
1428             rule.append(Integer.toHexString(fg.getBlue()));
1429             rule.append(" ; ");
1430         }
1431         rule.append(" }");
1432         return rule.toString();
1433     }
1434 
1435     /**
1436      * Utility method that creates a <code>UIDefaults.LazyValue</code> that
1437      * creates an <code>ImageIcon</code> <code>UIResource</code> for the
1438      * specified image file name. The image is loaded using
1439      * <code>getResourceAsStream</code>, starting with a call to that method
1440      * on the base class parameter. If it cannot be found, searching will
1441      * continue through the base class' inheritance hierarchy, up to and
1442      * including <code>rootClass</code>.
1443      *
1444      * @param baseClass the first class to use in searching for the resource
1445      * @param rootClass an ancestor of <code>baseClass</code> to finish the
1446      *                  search at
1447      * @param imageFile the name of the file to be found
1448      * @return a lazy value that creates the <code>ImageIcon</code>
1449      *         <code>UIResource</code> for the image,
1450      *         or null if it cannot be found
1451      */
1452     public static Object makeIcon(final Class<?> baseClass,
1453                                   final Class<?> rootClass,
1454                                   final String imageFile) {
1455 
1456         return new UIDefaults.LazyValue() {
1457             public Object createValue(UIDefaults table) {
1458                 /* Copy resource into a byte array.  This is
1459                  * necessary because several browsers consider
1460                  * Class.getResource a security risk because it
1461                  * can be used to load additional classes.
1462                  * Class.getResourceAsStream just returns raw
1463                  * bytes, which we can convert to an image.
1464                  */
1465                 byte[] buffer =
1466                     java.security.AccessController.doPrivileged(
1467                         new java.security.PrivilegedAction<byte[]>() {
1468                     public byte[] run() {
1469                         try {
1470                             InputStream resource = null;
1471                             Class<?> srchClass = baseClass;
1472 
1473                             while (srchClass != null) {
1474                                 resource = srchClass.getResourceAsStream(imageFile);
1475 
1476                                 if (resource != null || srchClass == rootClass) {
1477                                     break;
1478                                 }
1479 
1480                                 srchClass = srchClass.getSuperclass();
1481                             }
1482 
1483                             if (resource == null) {
1484                                 return null;
1485                             }
1486 
1487                             BufferedInputStream in =
1488                                 new BufferedInputStream(resource);
1489                             ByteArrayOutputStream out =
1490                                 new ByteArrayOutputStream(1024);
1491                             byte[] buffer = new byte[1024];
1492                             int n;
1493                             while ((n = in.read(buffer)) > 0) {
1494                                 out.write(buffer, 0, n);
1495                             }
1496                             in.close();
1497                             out.flush();
1498                             return out.toByteArray();
1499                         } catch (IOException ioe) {
1500                             System.err.println(ioe.toString());
1501                         }
1502                         return null;
1503                     }
1504                 });
1505 
1506                 if (buffer == null) {
1507                     return null;
1508                 }
1509                 if (buffer.length == 0) {
1510                     System.err.println("warning: " + imageFile +
1511                                        " is zero-length");
1512                     return null;
1513                 }
1514 
1515                 return new ImageIconUIResource(buffer);
1516             }
1517         };
1518     }
1519 
1520     /* Used to help decide if AA text rendering should be used, so
1521      * this local display test should be additionally qualified
1522      * against whether we have XRender support on both ends of the wire,
1523      * as with that support remote performance may be good enough to turn
1524      * on by default. An additional complication there is XRender does not
1525      * appear capable of performing gamma correction needed for LCD text.
1526      */
1527     public static boolean isLocalDisplay() {
1528         boolean isLocal;
1529         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1530         if (ge instanceof SunGraphicsEnvironment) {
1531             isLocal = ((SunGraphicsEnvironment) ge).isDisplayLocal();
1532         } else {
1533             isLocal = true;
1534         }
1535         return isLocal;
1536     }
1537 
1538     /**
1539      * Returns an integer from the defaults table. If <code>key</code> does
1540      * not map to a valid <code>Integer</code>, or can not be convered from
1541      * a <code>String</code> to an integer, the value 0 is returned.
1542      *
1543      * @param key  an <code>Object</code> specifying the int.
1544      * @return the int
1545      */
1546     public static int getUIDefaultsInt(Object key) {
1547         return getUIDefaultsInt(key, 0);
1548     }
1549 
1550     /**
1551      * Returns an integer from the defaults table that is appropriate
1552      * for the given locale. If <code>key</code> does not map to a valid
1553      * <code>Integer</code>, or can not be convered from a <code>String</code>
1554      * to an integer, the value 0 is returned.
1555      *
1556      * @param key  an <code>Object</code> specifying the int. Returned value
1557      *             is 0 if <code>key</code> is not available,
1558      * @param l the <code>Locale</code> for which the int is desired
1559      * @return the int
1560      */
1561     public static int getUIDefaultsInt(Object key, Locale l) {
1562         return getUIDefaultsInt(key, l, 0);
1563     }
1564 
1565     /**
1566      * Returns an integer from the defaults table. If <code>key</code> does
1567      * not map to a valid <code>Integer</code>, or can not be convered from
1568      * a <code>String</code> to an integer, <code>default</code> is
1569      * returned.
1570      *
1571      * @param key  an <code>Object</code> specifying the int. Returned value
1572      *             is 0 if <code>key</code> is not available,
1573      * @param defaultValue Returned value if <code>key</code> is not available,
1574      *                     or is not an Integer
1575      * @return the int
1576      */
1577     public static int getUIDefaultsInt(Object key, int defaultValue) {
1578         return getUIDefaultsInt(key, null, defaultValue);
1579     }
1580 
1581     /**
1582      * Returns an integer from the defaults table that is appropriate
1583      * for the given locale. If <code>key</code> does not map to a valid
1584      * <code>Integer</code>, or can not be convered from a <code>String</code>
1585      * to an integer, <code>default</code> is returned.
1586      *
1587      * @param key  an <code>Object</code> specifying the int. Returned value
1588      *             is 0 if <code>key</code> is not available,
1589      * @param l the <code>Locale</code> for which the int is desired
1590      * @param defaultValue Returned value if <code>key</code> is not available,
1591      *                     or is not an Integer
1592      * @return the int
1593      */
1594     public static int getUIDefaultsInt(Object key, Locale l, int defaultValue) {
1595         Object value = UIManager.get(key, l);
1596 
1597         if (value instanceof Integer) {
1598             return ((Integer)value).intValue();
1599         }
1600         if (value instanceof String) {
1601             try {
1602                 return Integer.parseInt((String)value);
1603             } catch (NumberFormatException nfe) {}
1604         }
1605         return defaultValue;
1606     }
1607 
1608     // At this point we need this method here. But we assume that there
1609     // will be a common method for this purpose in the future releases.
1610     public static Component compositeRequestFocus(Component component) {
1611         if (component instanceof Container) {
1612             Container container = (Container)component;
1613             if (container.isFocusCycleRoot()) {
1614                 FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
1615                 Component comp = policy.getDefaultComponent(container);
1616                 if (comp!=null) {
1617                     comp.requestFocus();
1618                     return comp;
1619                 }
1620             }
1621             Container rootAncestor = container.getFocusCycleRootAncestor();
1622             if (rootAncestor!=null) {
1623                 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
1624                 Component comp = policy.getComponentAfter(rootAncestor, container);
1625 
1626                 if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) {
1627                     comp.requestFocus();
1628                     return comp;
1629                 }
1630             }
1631         }
1632         if (component.isFocusable()) {
1633             component.requestFocus();
1634             return component;
1635         }
1636         return null;
1637     }
1638 
1639     /**
1640      * Change focus to the visible component in {@code JTabbedPane}.
1641      * This is not a general-purpose method and is here only to permit
1642      * sharing code.
1643      */
1644     public static boolean tabbedPaneChangeFocusTo(Component comp) {
1645         if (comp != null) {
1646             if (comp.isFocusTraversable()) {
1647                 SwingUtilities2.compositeRequestFocus(comp);
1648                 return true;
1649             } else if (comp instanceof JComponent
1650                        && ((JComponent)comp).requestDefaultFocus()) {
1651 
1652                  return true;
1653             }
1654         }
1655 
1656         return false;
1657     }
1658 
1659     /**
1660      * Submits a value-returning task for execution on the EDT and
1661      * returns a Future representing the pending results of the task.
1662      *
1663      * @param task the task to submit
1664      * @return a Future representing pending completion of the task
1665      * @throws NullPointerException if the task is null
1666      */
1667     public static <V> Future<V> submit(Callable<V> task) {
1668         if (task == null) {
1669             throw new NullPointerException();
1670         }
1671         FutureTask<V> future = new FutureTask<V>(task);
1672         execute(future);
1673         return future;
1674     }
1675 
1676     /**
1677      * Submits a Runnable task for execution on the EDT and returns a
1678      * Future representing that task.
1679      *
1680      * @param task the task to submit
1681      * @param result the result to return upon successful completion
1682      * @return a Future representing pending completion of the task,
1683      *         and whose <tt>get()</tt> method will return the given
1684      *         result value upon completion
1685      * @throws NullPointerException if the task is null
1686      */
1687     public static <V> Future<V> submit(Runnable task, V result) {
1688         if (task == null) {
1689             throw new NullPointerException();
1690         }
1691         FutureTask<V> future = new FutureTask<V>(task, result);
1692         execute(future);
1693         return future;
1694     }
1695 
1696     /**
1697      * Sends a Runnable to the EDT for the execution.
1698      */
1699     private static void execute(Runnable command) {
1700         SwingUtilities.invokeLater(command);
1701     }
1702 
1703     /**
1704      * Sets the {@code SKIP_CLICK_COUNT} client property on the component
1705      * if it is an instance of {@code JTextComponent} with a
1706      * {@code DefaultCaret}. This property, used for text components acting
1707      * as editors in a table or tree, tells {@code DefaultCaret} how many
1708      * clicks to skip before starting selection.
1709      */
1710     public static void setSkipClickCount(Component comp, int count) {
1711         if (comp instanceof JTextComponent
1712                 && ((JTextComponent) comp).getCaret() instanceof DefaultCaret) {
1713 
1714             ((JTextComponent) comp).putClientProperty(SKIP_CLICK_COUNT, count);
1715         }
1716     }
1717 
1718     /**
1719      * Return the MouseEvent's click count, possibly reduced by the value of
1720      * the component's {@code SKIP_CLICK_COUNT} client property. Clears
1721      * the {@code SKIP_CLICK_COUNT} property if the mouse event's click count
1722      * is 1. In order for clearing of the property to work correctly, there
1723      * must be a mousePressed implementation on the caller with this
1724      * call as the first line.
1725      */
1726     public static int getAdjustedClickCount(JTextComponent comp, MouseEvent e) {
1727         int cc = e.getClickCount();
1728 
1729         if (cc == 1) {
1730             comp.putClientProperty(SKIP_CLICK_COUNT, null);
1731         } else {
1732             Integer sub = (Integer) comp.getClientProperty(SKIP_CLICK_COUNT);
1733             if (sub != null) {
1734                 return cc - sub;
1735             }
1736         }
1737 
1738         return cc;
1739     }
1740 
1741     /**
1742      * Used by the {@code liesIn} method to return which section
1743      * the point lies in.
1744      *
1745      * @see #liesIn
1746      */
1747     public enum Section {
1748 
1749         /** The leading section */
1750         LEADING,
1751 
1752         /** The middle section */
1753         MIDDLE,
1754 
1755         /** The trailing section */
1756         TRAILING
1757     }
1758 
1759     /**
1760      * This method divides a rectangle into two or three sections along
1761      * the specified axis and determines which section the given point
1762      * lies in on that axis; used by drag and drop when calculating drop
1763      * locations.
1764      * <p>
1765      * For two sections, the rectangle is divided equally and the method
1766      * returns whether the point lies in {@code Section.LEADING} or
1767      * {@code Section.TRAILING}. For horizontal divisions, the calculation
1768      * respects component orientation.
1769      * <p>
1770      * For three sections, if the rectangle is greater than or equal to
1771      * 30 pixels in length along the axis, the calculation gives 10 pixels
1772      * to each of the leading and trailing sections and the remainder to the
1773      * middle. For smaller sizes, the rectangle is divided equally into three
1774      * sections.
1775      * <p>
1776      * Note: This method assumes that the point is within the bounds of
1777      * the given rectangle on the specified axis. However, in cases where
1778      * it isn't, the results still have meaning: {@code Section.MIDDLE}
1779      * remains the same, {@code Section.LEADING} indicates that the point
1780      * is in or somewhere before the leading section, and
1781      * {@code Section.TRAILING} indicates that the point is in or somewhere
1782      * after the trailing section.
1783      *
1784      * @param rect the rectangle
1785      * @param p the point the check
1786      * @param horizontal {@code true} to use the horizontal axis,
1787      *        or {@code false} for the vertical axis
1788      * @param ltr {@code true} for left to right orientation,
1789      *        or {@code false} for right to left orientation;
1790      *        only used for horizontal calculations
1791      * @param three {@code true} for three sections,
1792      *        or {@code false} for two
1793      *
1794      * @return the {@code Section} where the point lies
1795      *
1796      * @throws NullPointerException if {@code rect} or {@code p} are
1797      *         {@code null}
1798      */
1799     private static Section liesIn(Rectangle rect, Point p, boolean horizontal,
1800                                   boolean ltr, boolean three) {
1801 
1802         /* beginning of the rectangle on the axis */
1803         int p0;
1804 
1805         /* point on the axis we're interested in */
1806         int pComp;
1807 
1808         /* length of the rectangle on the axis */
1809         int length;
1810 
1811         /* value of ltr if horizontal, else true */
1812         boolean forward;
1813 
1814         if (horizontal) {
1815             p0 = rect.x;
1816             pComp = p.x;
1817             length = rect.width;
1818             forward = ltr;
1819         } else {
1820             p0 = rect.y;
1821             pComp = p.y;
1822             length = rect.height;
1823             forward = true;
1824         }
1825 
1826         if (three) {
1827             int boundary = (length >= 30) ? 10 : length / 3;
1828 
1829             if (pComp < p0 + boundary) {
1830                return forward ? Section.LEADING : Section.TRAILING;
1831            } else if (pComp >= p0 + length - boundary) {
1832                return forward ? Section.TRAILING : Section.LEADING;
1833            }
1834 
1835            return Section.MIDDLE;
1836         } else {
1837             int middle = p0 + length / 2;
1838             if (forward) {
1839                 return pComp >= middle ? Section.TRAILING : Section.LEADING;
1840             } else {
1841                 return pComp < middle ? Section.TRAILING : Section.LEADING;
1842             }
1843         }
1844     }
1845 
1846     /**
1847      * This method divides a rectangle into two or three sections along
1848      * the horizontal axis and determines which section the given point
1849      * lies in; used by drag and drop when calculating drop locations.
1850      * <p>
1851      * See the documentation for {@link #liesIn} for more information
1852      * on how the section is calculated.
1853      *
1854      * @param rect the rectangle
1855      * @param p the point the check
1856      * @param ltr {@code true} for left to right orientation,
1857      *        or {@code false} for right to left orientation
1858      * @param three {@code true} for three sections,
1859      *        or {@code false} for two
1860      *
1861      * @return the {@code Section} where the point lies
1862      *
1863      * @throws NullPointerException if {@code rect} or {@code p} are
1864      *         {@code null}
1865      */
1866     public static Section liesInHorizontal(Rectangle rect, Point p,
1867                                            boolean ltr, boolean three) {
1868         return liesIn(rect, p, true, ltr, three);
1869     }
1870 
1871     /**
1872      * This method divides a rectangle into two or three sections along
1873      * the vertical axis and determines which section the given point
1874      * lies in; used by drag and drop when calculating drop locations.
1875      * <p>
1876      * See the documentation for {@link #liesIn} for more information
1877      * on how the section is calculated.
1878      *
1879      * @param rect the rectangle
1880      * @param p the point the check
1881      * @param three {@code true} for three sections,
1882      *        or {@code false} for two
1883      *
1884      * @return the {@code Section} where the point lies
1885      *
1886      * @throws NullPointerException if {@code rect} or {@code p} are
1887      *         {@code null}
1888      */
1889     public static Section liesInVertical(Rectangle rect, Point p,
1890                                          boolean three) {
1891         return liesIn(rect, p, false, false, three);
1892     }
1893 
1894     /**
1895      * Maps the index of the column in the view at
1896      * {@code viewColumnIndex} to the index of the column
1897      * in the table model.  Returns the index of the corresponding
1898      * column in the model.  If {@code viewColumnIndex}
1899      * is less than zero, returns {@code viewColumnIndex}.
1900      *
1901      * @param cm the table model
1902      * @param   viewColumnIndex     the index of the column in the view
1903      * @return  the index of the corresponding column in the model
1904      *
1905      * @see JTable#convertColumnIndexToModel(int)
1906      * @see javax.swing.plaf.basic.BasicTableHeaderUI
1907      */
1908     public static int convertColumnIndexToModel(TableColumnModel cm,
1909                                                 int viewColumnIndex) {
1910         if (viewColumnIndex < 0) {
1911             return viewColumnIndex;
1912         }
1913         return cm.getColumn(viewColumnIndex).getModelIndex();
1914     }
1915 
1916     /**
1917      * Maps the index of the column in the {@code cm} at
1918      * {@code modelColumnIndex} to the index of the column
1919      * in the view.  Returns the index of the
1920      * corresponding column in the view; returns {@code -1} if this column
1921      * is not being displayed. If {@code modelColumnIndex} is less than zero,
1922      * returns {@code modelColumnIndex}.
1923      *
1924      * @param cm the table model
1925      * @param modelColumnIndex the index of the column in the model
1926      * @return the index of the corresponding column in the view
1927      *
1928      * @see JTable#convertColumnIndexToView(int)
1929      * @see javax.swing.plaf.basic.BasicTableHeaderUI
1930      */
1931     public static int convertColumnIndexToView(TableColumnModel cm,
1932                                         int modelColumnIndex) {
1933         if (modelColumnIndex < 0) {
1934             return modelColumnIndex;
1935         }
1936         for (int column = 0; column < cm.getColumnCount(); column++) {
1937             if (cm.getColumn(column).getModelIndex() == modelColumnIndex) {
1938                 return column;
1939             }
1940         }
1941         return -1;
1942     }
1943 
1944     public static int getSystemMnemonicKeyMask() {
1945         Toolkit toolkit = Toolkit.getDefaultToolkit();
1946         if (toolkit instanceof SunToolkit) {
1947             return ((SunToolkit) toolkit).getFocusAcceleratorKeyMask();
1948         }
1949         return InputEvent.ALT_MASK;
1950     }
1951 
1952     /**
1953      * Returns the {@link TreePath} that identifies the changed nodes.
1954      *
1955      * @param event  changes in a tree model
1956      * @param model  corresponing tree model
1957      * @return  the path to the changed nodes
1958      */
1959     public static TreePath getTreePath(TreeModelEvent event, TreeModel model) {
1960         TreePath path = event.getTreePath();
1961         if ((path == null) && (model != null)) {
1962             Object root = model.getRoot();
1963             if (root != null) {
1964                 path = new TreePath(root);
1965             }
1966         }
1967         return path;
1968     }
1969 }