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