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