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