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