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