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