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