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