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