1 /* 2 * Copyright (c) 1997, 2014, 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 package javax.swing; 26 27 import sun.reflect.misc.ReflectUtil; 28 import sun.swing.SwingUtilities2; 29 import sun.swing.UIAction; 30 31 import java.applet.*; 32 33 import java.awt.*; 34 import java.awt.event.*; 35 import java.awt.dnd.DropTarget; 36 37 import java.lang.reflect.*; 38 39 import javax.accessibility.*; 40 import javax.swing.event.MenuDragMouseEvent; 41 import javax.swing.plaf.UIResource; 42 import javax.swing.text.View; 43 import java.security.AccessController; 44 import sun.security.action.GetPropertyAction; 45 46 import sun.awt.AppContext; 47 48 /** 49 * A collection of utility methods for Swing. 50 * 51 * @author unknown 52 * @since 1.2 53 */ 54 public class SwingUtilities implements SwingConstants 55 { 56 // These states are system-wide, rather than AppContext wide. 57 private static boolean canAccessEventQueue = false; 58 private static boolean eventQueueTested = false; 59 60 /** 61 * Indicates if we should change the drop target when a 62 * {@code TransferHandler} is set. 63 */ 64 private static boolean suppressDropSupport; 65 66 /** 67 * Indiciates if we've checked the system property for suppressing 68 * drop support. 69 */ 70 private static boolean checkedSuppressDropSupport; 71 72 73 /** 74 * Returns true if <code>setTransferHandler</code> should change the 75 * <code>DropTarget</code>. 76 */ 77 private static boolean getSuppressDropTarget() { 78 if (!checkedSuppressDropSupport) { 79 suppressDropSupport = Boolean.valueOf( 80 AccessController.doPrivileged( 81 new GetPropertyAction("suppressSwingDropSupport"))); 82 checkedSuppressDropSupport = true; 83 } 84 return suppressDropSupport; 85 } 86 87 /** 88 * Installs a {@code DropTarget} on the component as necessary for a 89 * {@code TransferHandler} change. 90 */ 91 static void installSwingDropTargetAsNecessary(Component c, 92 TransferHandler t) { 93 94 if (!getSuppressDropTarget()) { 95 DropTarget dropHandler = c.getDropTarget(); 96 if ((dropHandler == null) || (dropHandler instanceof UIResource)) { 97 if (t == null) { 98 c.setDropTarget(null); 99 } else if (!GraphicsEnvironment.isHeadless()) { 100 c.setDropTarget(new TransferHandler.SwingDropTarget(c)); 101 } 102 } 103 } 104 } 105 106 /** 107 * Return {@code true} if @{code a} contains {@code b} 108 * 109 * @param a the first rectangle 110 * @param b the second rectangle 111 * 112 * @return {@code true} if @{code a} contains {@code b} 113 */ 114 public static final boolean isRectangleContainingRectangle(Rectangle a,Rectangle b) { 115 return b.x >= a.x && (b.x + b.width) <= (a.x + a.width) && 116 b.y >= a.y && (b.y + b.height) <= (a.y + a.height); 117 } 118 119 /** 120 * Return the rectangle (0,0,bounds.width,bounds.height) for the component {@code aComponent} 121 * 122 * @param aComponent a component 123 * @return the local bounds for the component {@code aComponent} 124 */ 125 public static Rectangle getLocalBounds(Component aComponent) { 126 Rectangle b = new Rectangle(aComponent.getBounds()); 127 b.x = b.y = 0; 128 return b; 129 } 130 131 132 /** 133 * Returns the first <code>Window </code> ancestor of <code>c</code>, or 134 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>. 135 * 136 * @param c <code>Component</code> to get <code>Window</code> ancestor 137 * of. 138 * @return the first <code>Window </code> ancestor of <code>c</code>, or 139 * {@code null} if <code>c</code> is not contained inside a 140 * <code>Window</code>. 141 * @since 1.3 142 */ 143 public static Window getWindowAncestor(Component c) { 144 for(Container p = c.getParent(); p != null; p = p.getParent()) { 145 if (p instanceof Window) { 146 return (Window)p; 147 } 148 } 149 return null; 150 } 151 152 /** 153 * Converts the location <code>x</code> <code>y</code> to the 154 * parents coordinate system, returning the location. 155 */ 156 static Point convertScreenLocationToParent(Container parent,int x, int y) { 157 for (Container p = parent; p != null; p = p.getParent()) { 158 if (p instanceof Window) { 159 Point point = new Point(x, y); 160 161 SwingUtilities.convertPointFromScreen(point, parent); 162 return point; 163 } 164 } 165 throw new Error("convertScreenLocationToParent: no window ancestor"); 166 } 167 168 /** 169 * Convert a <code>aPoint</code> in <code>source</code> coordinate system to 170 * <code>destination</code> coordinate system. 171 * If <code>source</code> is {@code null}, <code>aPoint</code> is assumed to be in <code>destination</code>'s 172 * root component coordinate system. 173 * If <code>destination</code> is {@code null}, <code>aPoint</code> will be converted to <code>source</code>'s 174 * root component coordinate system. 175 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aPoint</code> 176 * without any conversion. 177 * 178 * @param source the source component 179 * @param aPoint the point 180 * @param destination the destination component 181 * 182 * @return the converted coordinate 183 */ 184 public static Point convertPoint(Component source,Point aPoint,Component destination) { 185 Point p; 186 187 if(source == null && destination == null) 188 return aPoint; 189 if(source == null) { 190 source = getWindowAncestor(destination); 191 if(source == null) 192 throw new Error("Source component not connected to component tree hierarchy"); 193 } 194 p = new Point(aPoint); 195 convertPointToScreen(p,source); 196 if(destination == null) { 197 destination = getWindowAncestor(source); 198 if(destination == null) 199 throw new Error("Destination component not connected to component tree hierarchy"); 200 } 201 convertPointFromScreen(p,destination); 202 return p; 203 } 204 205 /** 206 * Convert the point <code>(x,y)</code> in <code>source</code> coordinate system to 207 * <code>destination</code> coordinate system. 208 * If <code>source</code> is {@code null}, <code>(x,y)</code> is assumed to be in <code>destination</code>'s 209 * root component coordinate system. 210 * If <code>destination</code> is {@code null}, <code>(x,y)</code> will be converted to <code>source</code>'s 211 * root component coordinate system. 212 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>(x,y)</code> 213 * without any conversion. 214 * 215 * @param source the source component 216 * @param x the x-coordinate of the point 217 * @param y the y-coordinate of the point 218 * @param destination the destination component 219 * 220 * @return the converted coordinate 221 */ 222 public static Point convertPoint(Component source,int x, int y,Component destination) { 223 Point point = new Point(x,y); 224 return convertPoint(source,point,destination); 225 } 226 227 /** 228 * Convert the rectangle <code>aRectangle</code> in <code>source</code> coordinate system to 229 * <code>destination</code> coordinate system. 230 * If <code>source</code> is {@code null}, <code>aRectangle</code> is assumed to be in <code>destination</code>'s 231 * root component coordinate system. 232 * If <code>destination</code> is {@code null}, <code>aRectangle</code> will be converted to <code>source</code>'s 233 * root component coordinate system. 234 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aRectangle</code> 235 * without any conversion. 236 * 237 * @param source the source component 238 * @param aRectangle a rectangle 239 * @param destination the destination component 240 * 241 * @return the converted rectangle 242 */ 243 public static Rectangle convertRectangle(Component source,Rectangle aRectangle,Component destination) { 244 Point point = new Point(aRectangle.x,aRectangle.y); 245 point = convertPoint(source,point,destination); 246 return new Rectangle(point.x,point.y,aRectangle.width,aRectangle.height); 247 } 248 249 /** 250 * Convenience method for searching above <code>comp</code> in the 251 * component hierarchy and returns the first object of class <code>c</code> it 252 * finds. Can return {@code null}, if a class <code>c</code> cannot be found. 253 * 254 * @param c the class of a component 255 * @param comp the component 256 * 257 * @return the ancestor of the {@code comp}, 258 * or {@code null} if {@code c} cannot be found. 259 */ 260 public static Container getAncestorOfClass(Class<?> c, Component comp) 261 { 262 if(comp == null || c == null) 263 return null; 264 265 Container parent = comp.getParent(); 266 while(parent != null && !(c.isInstance(parent))) 267 parent = parent.getParent(); 268 return parent; 269 } 270 271 /** 272 * Convenience method for searching above <code>comp</code> in the 273 * component hierarchy and returns the first object of <code>name</code> it 274 * finds. Can return {@code null}, if <code>name</code> cannot be found. 275 * 276 * @param name the name of a component 277 * @param comp the component 278 * 279 * @return the ancestor of the {@code comp}, 280 * or {@code null} if {@code name} cannot be found. 281 */ 282 public static Container getAncestorNamed(String name, Component comp) { 283 if(comp == null || name == null) 284 return null; 285 286 Container parent = comp.getParent(); 287 while(parent != null && !(name.equals(parent.getName()))) 288 parent = parent.getParent(); 289 return parent; 290 } 291 292 /** 293 * Returns the deepest visible descendent Component of <code>parent</code> 294 * that contains the location <code>x</code>, <code>y</code>. 295 * If <code>parent</code> does not contain the specified location, 296 * then <code>null</code> is returned. If <code>parent</code> is not a 297 * container, or none of <code>parent</code>'s visible descendents 298 * contain the specified location, <code>parent</code> is returned. 299 * 300 * @param parent the root component to begin the search 301 * @param x the x target location 302 * @param y the y target location 303 * 304 * @return the deepest component 305 */ 306 public static Component getDeepestComponentAt(Component parent, int x, int y) { 307 if (!parent.contains(x, y)) { 308 return null; 309 } 310 if (parent instanceof Container) { 311 Component components[] = ((Container)parent).getComponents(); 312 for (Component comp : components) { 313 if (comp != null && comp.isVisible()) { 314 Point loc = comp.getLocation(); 315 if (comp instanceof Container) { 316 comp = getDeepestComponentAt(comp, x - loc.x, y - loc.y); 317 } else { 318 comp = comp.getComponentAt(x - loc.x, y - loc.y); 319 } 320 if (comp != null && comp.isVisible()) { 321 return comp; 322 } 323 } 324 } 325 } 326 return parent; 327 } 328 329 330 /** 331 * Returns a MouseEvent similar to <code>sourceEvent</code> except that its x 332 * and y members have been converted to <code>destination</code>'s coordinate 333 * system. If <code>source</code> is {@code null}, <code>sourceEvent</code> x and y members 334 * are assumed to be into <code>destination</code>'s root component coordinate system. 335 * If <code>destination</code> is <code>null</code>, the 336 * returned MouseEvent will be in <code>source</code>'s coordinate system. 337 * <code>sourceEvent</code> will not be changed. A new event is returned. 338 * the <code>source</code> field of the returned event will be set 339 * to <code>destination</code> if destination is non-{@code null} 340 * use the translateMouseEvent() method to translate a mouse event from 341 * one component to another without changing the source. 342 * 343 * @param source the source component 344 * @param sourceEvent the source mouse event 345 * @param destination the destination component 346 * 347 * @return the new mouse event 348 */ 349 @SuppressWarnings("deprecation") 350 public static MouseEvent convertMouseEvent(Component source, 351 MouseEvent sourceEvent, 352 Component destination) { 353 Point p = convertPoint(source,new Point(sourceEvent.getX(), 354 sourceEvent.getY()), 355 destination); 356 Component newSource; 357 358 if(destination != null) 359 newSource = destination; 360 else 361 newSource = source; 362 363 MouseEvent newEvent; 364 if (sourceEvent instanceof MouseWheelEvent) { 365 MouseWheelEvent sourceWheelEvent = (MouseWheelEvent)sourceEvent; 366 newEvent = new MouseWheelEvent(newSource, 367 sourceWheelEvent.getID(), 368 sourceWheelEvent.getWhen(), 369 sourceWheelEvent.getModifiers() 370 | sourceWheelEvent.getModifiersEx(), 371 p.x,p.y, 372 sourceWheelEvent.getXOnScreen(), 373 sourceWheelEvent.getYOnScreen(), 374 sourceWheelEvent.getClickCount(), 375 sourceWheelEvent.isPopupTrigger(), 376 sourceWheelEvent.getScrollType(), 377 sourceWheelEvent.getScrollAmount(), 378 sourceWheelEvent.getWheelRotation(), 379 sourceWheelEvent.getPreciseWheelRotation()); 380 } 381 else if (sourceEvent instanceof MenuDragMouseEvent) { 382 MenuDragMouseEvent sourceMenuDragEvent = (MenuDragMouseEvent)sourceEvent; 383 newEvent = new MenuDragMouseEvent(newSource, 384 sourceMenuDragEvent.getID(), 385 sourceMenuDragEvent.getWhen(), 386 sourceMenuDragEvent.getModifiers() 387 | sourceMenuDragEvent.getModifiersEx(), 388 p.x,p.y, 389 sourceMenuDragEvent.getXOnScreen(), 390 sourceMenuDragEvent.getYOnScreen(), 391 sourceMenuDragEvent.getClickCount(), 392 sourceMenuDragEvent.isPopupTrigger(), 393 sourceMenuDragEvent.getPath(), 394 sourceMenuDragEvent.getMenuSelectionManager()); 395 } 396 else { 397 newEvent = new MouseEvent(newSource, 398 sourceEvent.getID(), 399 sourceEvent.getWhen(), 400 sourceEvent.getModifiers() 401 | sourceEvent.getModifiersEx(), 402 p.x,p.y, 403 sourceEvent.getXOnScreen(), 404 sourceEvent.getYOnScreen(), 405 sourceEvent.getClickCount(), 406 sourceEvent.isPopupTrigger(), 407 sourceEvent.getButton()); 408 } 409 return newEvent; 410 } 411 412 413 /** 414 * Convert a point from a component's coordinate system to 415 * screen coordinates. 416 * 417 * @param p a Point object (converted to the new coordinate system) 418 * @param c a Component object 419 */ 420 @SuppressWarnings("deprecation") 421 public static void convertPointToScreen(Point p,Component c) { 422 Rectangle b; 423 int x,y; 424 425 do { 426 if(c instanceof JComponent) { 427 x = c.getX(); 428 y = c.getY(); 429 } else if(c instanceof java.applet.Applet || 430 c instanceof java.awt.Window) { 431 try { 432 Point pp = c.getLocationOnScreen(); 433 x = pp.x; 434 y = pp.y; 435 } catch (IllegalComponentStateException icse) { 436 x = c.getX(); 437 y = c.getY(); 438 } 439 } else { 440 x = c.getX(); 441 y = c.getY(); 442 } 443 444 p.x += x; 445 p.y += y; 446 447 if(c instanceof java.awt.Window || c instanceof java.applet.Applet) 448 break; 449 c = c.getParent(); 450 } while(c != null); 451 } 452 453 /** 454 * Convert a point from a screen coordinates to a component's 455 * coordinate system 456 * 457 * @param p a Point object (converted to the new coordinate system) 458 * @param c a Component object 459 */ 460 @SuppressWarnings("deprecation") 461 public static void convertPointFromScreen(Point p,Component c) { 462 Rectangle b; 463 int x,y; 464 465 do { 466 if(c instanceof JComponent) { 467 x = c.getX(); 468 y = c.getY(); 469 } else if(c instanceof java.applet.Applet || 470 c instanceof java.awt.Window) { 471 try { 472 Point pp = c.getLocationOnScreen(); 473 x = pp.x; 474 y = pp.y; 475 } catch (IllegalComponentStateException icse) { 476 x = c.getX(); 477 y = c.getY(); 478 } 479 } else { 480 x = c.getX(); 481 y = c.getY(); 482 } 483 484 p.x -= x; 485 p.y -= y; 486 487 if(c instanceof java.awt.Window || c instanceof java.applet.Applet) 488 break; 489 c = c.getParent(); 490 } while(c != null); 491 } 492 493 /** 494 * Returns the first <code>Window </code> ancestor of <code>c</code>, or 495 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>. 496 * <p> 497 * Note: This method provides the same functionality as 498 * <code>getWindowAncestor</code>. 499 * 500 * @param c <code>Component</code> to get <code>Window</code> ancestor 501 * of. 502 * @return the first <code>Window </code> ancestor of <code>c</code>, or 503 * {@code null} if <code>c</code> is not contained inside a 504 * <code>Window</code>. 505 */ 506 public static Window windowForComponent(Component c) { 507 return getWindowAncestor(c); 508 } 509 510 /** 511 * Return {@code true} if a component {@code a} descends from a component {@code b} 512 * 513 * @param a the first component 514 * @param b the second component 515 * @return {@code true} if a component {@code a} descends from a component {@code b} 516 */ 517 public static boolean isDescendingFrom(Component a,Component b) { 518 if(a == b) 519 return true; 520 for(Container p = a.getParent();p!=null;p=p.getParent()) 521 if(p == b) 522 return true; 523 return false; 524 } 525 526 527 /** 528 * Convenience to calculate the intersection of two rectangles 529 * without allocating a new rectangle. 530 * If the two rectangles don't intersect, 531 * then the returned rectangle begins at (0,0) 532 * and has zero width and height. 533 * 534 * @param x the X coordinate of the first rectangle's top-left point 535 * @param y the Y coordinate of the first rectangle's top-left point 536 * @param width the width of the first rectangle 537 * @param height the height of the first rectangle 538 * @param dest the second rectangle 539 * 540 * @return <code>dest</code>, modified to specify the intersection 541 */ 542 public static Rectangle computeIntersection(int x,int y,int width,int height,Rectangle dest) { 543 int x1 = (x > dest.x) ? x : dest.x; 544 int x2 = ((x+width) < (dest.x + dest.width)) ? (x+width) : (dest.x + dest.width); 545 int y1 = (y > dest.y) ? y : dest.y; 546 int y2 = ((y + height) < (dest.y + dest.height) ? (y+height) : (dest.y + dest.height)); 547 548 dest.x = x1; 549 dest.y = y1; 550 dest.width = x2 - x1; 551 dest.height = y2 - y1; 552 553 // If rectangles don't intersect, return zero'd intersection. 554 if (dest.width < 0 || dest.height < 0) { 555 dest.x = dest.y = dest.width = dest.height = 0; 556 } 557 558 return dest; 559 } 560 561 /** 562 * Convenience method that calculates the union of two rectangles 563 * without allocating a new rectangle. 564 * 565 * @param x the x-coordinate of the first rectangle 566 * @param y the y-coordinate of the first rectangle 567 * @param width the width of the first rectangle 568 * @param height the height of the first rectangle 569 * @param dest the coordinates of the second rectangle; the union 570 * of the two rectangles is returned in this rectangle 571 * @return the <code>dest</code> <code>Rectangle</code> 572 */ 573 public static Rectangle computeUnion(int x,int y,int width,int height,Rectangle dest) { 574 int x1 = (x < dest.x) ? x : dest.x; 575 int x2 = ((x+width) > (dest.x + dest.width)) ? (x+width) : (dest.x + dest.width); 576 int y1 = (y < dest.y) ? y : dest.y; 577 int y2 = ((y+height) > (dest.y + dest.height)) ? (y+height) : (dest.y + dest.height); 578 579 dest.x = x1; 580 dest.y = y1; 581 dest.width = (x2 - x1); 582 dest.height= (y2 - y1); 583 return dest; 584 } 585 586 /** 587 * Convenience returning an array of rect representing the regions within 588 * <code>rectA</code> that do not overlap with <code>rectB</code>. If the 589 * two Rects do not overlap, returns an empty array 590 * 591 * @param rectA the first rectangle 592 * @param rectB the second rectangle 593 * 594 * @return an array of rectangles representing the regions within {@code rectA} 595 * that do not overlap with {@code rectB}. 596 */ 597 public static Rectangle[] computeDifference(Rectangle rectA,Rectangle rectB) { 598 if (rectB == null || !rectA.intersects(rectB) || isRectangleContainingRectangle(rectB,rectA)) { 599 return new Rectangle[0]; 600 } 601 602 Rectangle t = new Rectangle(); 603 Rectangle a=null,b=null,c=null,d=null; 604 Rectangle result[]; 605 int rectCount = 0; 606 607 /* rectA contains rectB */ 608 if (isRectangleContainingRectangle(rectA,rectB)) { 609 t.x = rectA.x; t.y = rectA.y; t.width = rectB.x - rectA.x; t.height = rectA.height; 610 if(t.width > 0 && t.height > 0) { 611 a = new Rectangle(t); 612 rectCount++; 613 } 614 615 t.x = rectB.x; t.y = rectA.y; t.width = rectB.width; t.height = rectB.y - rectA.y; 616 if(t.width > 0 && t.height > 0) { 617 b = new Rectangle(t); 618 rectCount++; 619 } 620 621 t.x = rectB.x; t.y = rectB.y + rectB.height; t.width = rectB.width; 622 t.height = rectA.y + rectA.height - (rectB.y + rectB.height); 623 if(t.width > 0 && t.height > 0) { 624 c = new Rectangle(t); 625 rectCount++; 626 } 627 628 t.x = rectB.x + rectB.width; t.y = rectA.y; t.width = rectA.x + rectA.width - (rectB.x + rectB.width); 629 t.height = rectA.height; 630 if(t.width > 0 && t.height > 0) { 631 d = new Rectangle(t); 632 rectCount++; 633 } 634 } else { 635 /* 1 */ 636 if (rectB.x <= rectA.x && rectB.y <= rectA.y) { 637 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) { 638 639 t.x = rectA.x; t.y = rectB.y + rectB.height; 640 t.width = rectA.width; t.height = rectA.y + rectA.height - (rectB.y + rectB.height); 641 if(t.width > 0 && t.height > 0) { 642 a = t; 643 rectCount++; 644 } 645 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) { 646 t.setBounds((rectB.x + rectB.width), rectA.y, 647 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height); 648 if(t.width > 0 && t.height > 0) { 649 a = t; 650 rectCount++; 651 } 652 } else { 653 t.setBounds((rectB.x + rectB.width), rectA.y, 654 (rectA.x + rectA.width) - (rectB.x + rectB.width), 655 (rectB.y + rectB.height) - rectA.y); 656 if(t.width > 0 && t.height > 0) { 657 a = new Rectangle(t); 658 rectCount++; 659 } 660 661 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width, 662 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 663 if(t.width > 0 && t.height > 0) { 664 b = new Rectangle(t); 665 rectCount++; 666 } 667 } 668 } else if (rectB.x <= rectA.x && (rectB.y + rectB.height) >= (rectA.y + rectA.height)) { 669 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) { 670 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 671 if(t.width > 0 && t.height > 0) { 672 a = t; 673 rectCount++; 674 } 675 } else { 676 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 677 if(t.width > 0 && t.height > 0) { 678 a = new Rectangle(t); 679 rectCount++; 680 } 681 t.setBounds((rectB.x + rectB.width), rectB.y, 682 (rectA.x + rectA.width) - (rectB.x + rectB.width), 683 (rectA.y + rectA.height) - rectB.y); 684 if(t.width > 0 && t.height > 0) { 685 b = new Rectangle(t); 686 rectCount++; 687 } 688 } 689 } else if (rectB.x <= rectA.x) { 690 if ((rectB.x + rectB.width) >= (rectA.x + rectA.width)) { 691 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 692 if(t.width>0 && t.height > 0) { 693 a = new Rectangle(t); 694 rectCount++; 695 } 696 697 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width, 698 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 699 if(t.width > 0 && t.height > 0) { 700 b = new Rectangle(t); 701 rectCount++; 702 } 703 } else { 704 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 705 if(t.width > 0 && t.height > 0) { 706 a = new Rectangle(t); 707 rectCount++; 708 } 709 710 t.setBounds((rectB.x + rectB.width), rectB.y, 711 (rectA.x + rectA.width) - (rectB.x + rectB.width), 712 rectB.height); 713 if(t.width > 0 && t.height > 0) { 714 b = new Rectangle(t); 715 rectCount++; 716 } 717 718 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width, 719 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 720 if(t.width > 0 && t.height > 0) { 721 c = new Rectangle(t); 722 rectCount++; 723 } 724 } 725 } else if (rectB.x <= (rectA.x + rectA.width) && (rectB.x + rectB.width) > (rectA.x + rectA.width)) { 726 if (rectB.y <= rectA.y && (rectB.y + rectB.height) > (rectA.y + rectA.height)) { 727 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height); 728 if(t.width > 0 && t.height > 0) { 729 a = t; 730 rectCount++; 731 } 732 } else if (rectB.y <= rectA.y) { 733 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, 734 (rectB.y + rectB.height) - rectA.y); 735 if(t.width > 0 && t.height > 0) { 736 a = new Rectangle(t); 737 rectCount++; 738 } 739 740 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width, 741 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 742 if(t.width > 0 && t.height > 0) { 743 b = new Rectangle(t); 744 rectCount++; 745 } 746 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) { 747 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 748 if(t.width > 0 && t.height > 0) { 749 a = new Rectangle(t); 750 rectCount++; 751 } 752 753 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x, 754 (rectA.y + rectA.height) - rectB.y); 755 if(t.width > 0 && t.height > 0) { 756 b = new Rectangle(t); 757 rectCount++; 758 } 759 } else { 760 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y); 761 if(t.width > 0 && t.height > 0) { 762 a = new Rectangle(t); 763 rectCount++; 764 } 765 766 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x, 767 rectB.height); 768 if(t.width > 0 && t.height > 0) { 769 b = new Rectangle(t); 770 rectCount++; 771 } 772 773 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width, 774 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 775 if(t.width > 0 && t.height > 0) { 776 c = new Rectangle(t); 777 rectCount++; 778 } 779 } 780 } else if (rectB.x >= rectA.x && (rectB.x + rectB.width) <= (rectA.x + rectA.width)) { 781 if (rectB.y <= rectA.y && (rectB.y + rectB.height) > (rectA.y + rectA.height)) { 782 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height); 783 if(t.width > 0 && t.height > 0) { 784 a = new Rectangle(t); 785 rectCount++; 786 } 787 t.setBounds((rectB.x + rectB.width), rectA.y, 788 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height); 789 if(t.width > 0 && t.height > 0) { 790 b = new Rectangle(t); 791 rectCount++; 792 } 793 } else if (rectB.y <= rectA.y) { 794 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height); 795 if(t.width > 0 && t.height > 0) { 796 a = new Rectangle(t); 797 rectCount++; 798 } 799 800 t.setBounds(rectB.x, (rectB.y + rectB.height), 801 rectB.width, 802 (rectA.y + rectA.height) - (rectB.y + rectB.height)); 803 if(t.width > 0 && t.height > 0) { 804 b = new Rectangle(t); 805 rectCount++; 806 } 807 808 t.setBounds((rectB.x + rectB.width), rectA.y, 809 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height); 810 if(t.width > 0 && t.height > 0) { 811 c = new Rectangle(t); 812 rectCount++; 813 } 814 } else { 815 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height); 816 if(t.width > 0 && t.height > 0) { 817 a = new Rectangle(t); 818 rectCount++; 819 } 820 821 t.setBounds(rectB.x, rectA.y, rectB.width, 822 rectB.y - rectA.y); 823 if(t.width > 0 && t.height > 0) { 824 b = new Rectangle(t); 825 rectCount++; 826 } 827 828 t.setBounds((rectB.x + rectB.width), rectA.y, 829 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height); 830 if(t.width > 0 && t.height > 0) { 831 c = new Rectangle(t); 832 rectCount++; 833 } 834 } 835 } 836 } 837 838 result = new Rectangle[rectCount]; 839 rectCount = 0; 840 if(a != null) 841 result[rectCount++] = a; 842 if(b != null) 843 result[rectCount++] = b; 844 if(c != null) 845 result[rectCount++] = c; 846 if(d != null) 847 result[rectCount++] = d; 848 return result; 849 } 850 851 /** 852 * Check whether MouseEvent contains speficied mouse button or 853 * mouse button down mask based on MouseEvent ID. 854 * 855 * @param anEvent a MouseEvent object 856 * @param mouseButton mouse button type 857 * @param mouseButtonDownMask mouse button down mask event modifier 858 * 859 * @return true if the anEvent contains speficied mouseButton or 860 * mouseButtonDownMask based on MouseEvent ID. 861 */ 862 private static boolean checkMouseButton(MouseEvent anEvent, 863 int mouseButton, 864 int mouseButtonDownMask) 865 { 866 switch (anEvent.getID()) { 867 case MouseEvent.MOUSE_PRESSED: 868 case MouseEvent.MOUSE_RELEASED: 869 case MouseEvent.MOUSE_CLICKED: 870 return (anEvent.getButton() == mouseButton); 871 872 case MouseEvent.MOUSE_ENTERED: 873 case MouseEvent.MOUSE_EXITED: 874 case MouseEvent.MOUSE_DRAGGED: 875 return ((anEvent.getModifiersEx() & mouseButtonDownMask) != 0); 876 877 default: 878 return ((anEvent.getModifiersEx() & mouseButtonDownMask) != 0 || 879 anEvent.getButton() == mouseButton); 880 } 881 } 882 883 /** 884 * Returns true if the mouse event specifies the left mouse button. 885 * 886 * @param anEvent a MouseEvent object 887 * @return true if the left mouse button was active 888 */ 889 public static boolean isLeftMouseButton(MouseEvent anEvent) { 890 return checkMouseButton(anEvent, MouseEvent.BUTTON1, 891 InputEvent.BUTTON1_DOWN_MASK); 892 } 893 894 /** 895 * Returns true if the mouse event specifies the middle mouse button. 896 * 897 * @param anEvent a MouseEvent object 898 * @return true if the middle mouse button was active 899 */ 900 public static boolean isMiddleMouseButton(MouseEvent anEvent) { 901 return checkMouseButton(anEvent, MouseEvent.BUTTON2, 902 InputEvent.BUTTON2_DOWN_MASK); 903 } 904 905 /** 906 * Returns true if the mouse event specifies the right mouse button. 907 * 908 * @param anEvent a MouseEvent object 909 * @return true if the right mouse button was active 910 */ 911 public static boolean isRightMouseButton(MouseEvent anEvent) { 912 return checkMouseButton(anEvent, MouseEvent.BUTTON3, 913 InputEvent.BUTTON3_DOWN_MASK); 914 } 915 916 /** 917 * Compute the width of the string using a font with the specified 918 * "metrics" (sizes). 919 * 920 * @param fm a FontMetrics object to compute with 921 * @param str the String to compute 922 * @return an int containing the string width 923 */ 924 public static int computeStringWidth(FontMetrics fm,String str) { 925 // You can't assume that a string's width is the sum of its 926 // characters' widths in Java2D -- it may be smaller due to 927 // kerning, etc. 928 return SwingUtilities2.stringWidth(null, fm, str); 929 } 930 931 /** 932 * Compute and return the location of the icons origin, the 933 * location of origin of the text baseline, and a possibly clipped 934 * version of the compound labels string. Locations are computed 935 * relative to the viewR rectangle. 936 * The JComponents orientation (LEADING/TRAILING) will also be taken 937 * into account and translated into LEFT/RIGHT values accordingly. 938 * 939 * @param c the component 940 * @param fm the instance of {@code FontMetrics} 941 * @param text the text 942 * @param icon the icon 943 * @param verticalAlignment the vertical alignment 944 * @param horizontalAlignment the horizontal alignment 945 * @param verticalTextPosition the vertical text position 946 * @param horizontalTextPosition the horizontal text position 947 * @param viewR the available rectangle 948 * @param iconR the rectangle for the icon 949 * @param textR the rectangle for the text 950 * @param textIconGap the gap between text and icon 951 * 952 * @return the possibly clipped version of the compound labels string 953 */ 954 public static String layoutCompoundLabel(JComponent c, 955 FontMetrics fm, 956 String text, 957 Icon icon, 958 int verticalAlignment, 959 int horizontalAlignment, 960 int verticalTextPosition, 961 int horizontalTextPosition, 962 Rectangle viewR, 963 Rectangle iconR, 964 Rectangle textR, 965 int textIconGap) 966 { 967 boolean orientationIsLeftToRight = true; 968 int hAlign = horizontalAlignment; 969 int hTextPos = horizontalTextPosition; 970 971 if (c != null) { 972 if (!(c.getComponentOrientation().isLeftToRight())) { 973 orientationIsLeftToRight = false; 974 } 975 } 976 977 // Translate LEADING/TRAILING values in horizontalAlignment 978 // to LEFT/RIGHT values depending on the components orientation 979 switch (horizontalAlignment) { 980 case LEADING: 981 hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT; 982 break; 983 case TRAILING: 984 hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT; 985 break; 986 } 987 988 // Translate LEADING/TRAILING values in horizontalTextPosition 989 // to LEFT/RIGHT values depending on the components orientation 990 switch (horizontalTextPosition) { 991 case LEADING: 992 hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT; 993 break; 994 case TRAILING: 995 hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT; 996 break; 997 } 998 999 return layoutCompoundLabelImpl(c, 1000 fm, 1001 text, 1002 icon, 1003 verticalAlignment, 1004 hAlign, 1005 verticalTextPosition, 1006 hTextPos, 1007 viewR, 1008 iconR, 1009 textR, 1010 textIconGap); 1011 } 1012 1013 /** 1014 * Compute and return the location of the icons origin, the 1015 * location of origin of the text baseline, and a possibly clipped 1016 * version of the compound labels string. Locations are computed 1017 * relative to the viewR rectangle. 1018 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING 1019 * values in horizontalTextPosition (they will default to RIGHT) and in 1020 * horizontalAlignment (they will default to CENTER). 1021 * Use the other version of layoutCompoundLabel() instead. 1022 * 1023 * @param fm the instance of {@code FontMetrics} 1024 * @param text the text 1025 * @param icon the icon 1026 * @param verticalAlignment the vertical alignment 1027 * @param horizontalAlignment the horizontal alignment 1028 * @param verticalTextPosition the vertical text position 1029 * @param horizontalTextPosition the horizontal text position 1030 * @param viewR the available rectangle 1031 * @param iconR the rectangle for the icon 1032 * @param textR the rectangle for the text 1033 * @param textIconGap the gap between text and icon 1034 * 1035 * @return the possibly clipped version of the compound labels string 1036 */ 1037 public static String layoutCompoundLabel( 1038 FontMetrics fm, 1039 String text, 1040 Icon icon, 1041 int verticalAlignment, 1042 int horizontalAlignment, 1043 int verticalTextPosition, 1044 int horizontalTextPosition, 1045 Rectangle viewR, 1046 Rectangle iconR, 1047 Rectangle textR, 1048 int textIconGap) 1049 { 1050 return layoutCompoundLabelImpl(null, fm, text, icon, 1051 verticalAlignment, 1052 horizontalAlignment, 1053 verticalTextPosition, 1054 horizontalTextPosition, 1055 viewR, iconR, textR, textIconGap); 1056 } 1057 1058 /** 1059 * Compute and return the location of the icons origin, the 1060 * location of origin of the text baseline, and a possibly clipped 1061 * version of the compound labels string. Locations are computed 1062 * relative to the viewR rectangle. 1063 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING 1064 * values in horizontalTextPosition (they will default to RIGHT) and in 1065 * horizontalAlignment (they will default to CENTER). 1066 * Use the other version of layoutCompoundLabel() instead. 1067 */ 1068 private static String layoutCompoundLabelImpl( 1069 JComponent c, 1070 FontMetrics fm, 1071 String text, 1072 Icon icon, 1073 int verticalAlignment, 1074 int horizontalAlignment, 1075 int verticalTextPosition, 1076 int horizontalTextPosition, 1077 Rectangle viewR, 1078 Rectangle iconR, 1079 Rectangle textR, 1080 int textIconGap) 1081 { 1082 /* Initialize the icon bounds rectangle iconR. 1083 */ 1084 1085 if (icon != null) { 1086 iconR.width = icon.getIconWidth(); 1087 iconR.height = icon.getIconHeight(); 1088 } 1089 else { 1090 iconR.width = iconR.height = 0; 1091 } 1092 1093 /* Initialize the text bounds rectangle textR. If a null 1094 * or and empty String was specified we substitute "" here 1095 * and use 0,0,0,0 for textR. 1096 */ 1097 1098 boolean textIsEmpty = (text == null) || text.equals(""); 1099 int lsb = 0; 1100 int rsb = 0; 1101 /* Unless both text and icon are non-null, we effectively ignore 1102 * the value of textIconGap. 1103 */ 1104 int gap; 1105 1106 View v; 1107 if (textIsEmpty) { 1108 textR.width = textR.height = 0; 1109 text = ""; 1110 gap = 0; 1111 } 1112 else { 1113 int availTextWidth; 1114 gap = (icon == null) ? 0 : textIconGap; 1115 1116 if (horizontalTextPosition == CENTER) { 1117 availTextWidth = viewR.width; 1118 } 1119 else { 1120 availTextWidth = viewR.width - (iconR.width + gap); 1121 } 1122 v = (c != null) ? (View) c.getClientProperty("html") : null; 1123 if (v != null) { 1124 textR.width = Math.min(availTextWidth, 1125 (int) v.getPreferredSpan(View.X_AXIS)); 1126 textR.height = (int) v.getPreferredSpan(View.Y_AXIS); 1127 } else { 1128 textR.width = SwingUtilities2.stringWidth(c, fm, text); 1129 lsb = SwingUtilities2.getLeftSideBearing(c, fm, text); 1130 if (lsb < 0) { 1131 // If lsb is negative, add it to the width and later 1132 // adjust the x location. This gives more space than is 1133 // actually needed. 1134 // This is done like this for two reasons: 1135 // 1. If we set the width to the actual bounds all 1136 // callers would have to account for negative lsb 1137 // (pref size calculations ONLY look at width of 1138 // textR) 1139 // 2. You can do a drawString at the returned location 1140 // and the text won't be clipped. 1141 textR.width -= lsb; 1142 } 1143 if (textR.width > availTextWidth) { 1144 text = SwingUtilities2.clipString(c, fm, text, 1145 availTextWidth); 1146 textR.width = SwingUtilities2.stringWidth(c, fm, text); 1147 } 1148 textR.height = fm.getHeight(); 1149 } 1150 } 1151 1152 1153 /* Compute textR.x,y given the verticalTextPosition and 1154 * horizontalTextPosition properties 1155 */ 1156 1157 if (verticalTextPosition == TOP) { 1158 if (horizontalTextPosition != CENTER) { 1159 textR.y = 0; 1160 } 1161 else { 1162 textR.y = -(textR.height + gap); 1163 } 1164 } 1165 else if (verticalTextPosition == CENTER) { 1166 textR.y = (iconR.height / 2) - (textR.height / 2); 1167 } 1168 else { // (verticalTextPosition == BOTTOM) 1169 if (horizontalTextPosition != CENTER) { 1170 textR.y = iconR.height - textR.height; 1171 } 1172 else { 1173 textR.y = (iconR.height + gap); 1174 } 1175 } 1176 1177 if (horizontalTextPosition == LEFT) { 1178 textR.x = -(textR.width + gap); 1179 } 1180 else if (horizontalTextPosition == CENTER) { 1181 textR.x = (iconR.width / 2) - (textR.width / 2); 1182 } 1183 else { // (horizontalTextPosition == RIGHT) 1184 textR.x = (iconR.width + gap); 1185 } 1186 1187 // WARNING: DefaultTreeCellEditor uses a shortened version of 1188 // this algorithm to position it's Icon. If you change how this 1189 // is calculated, be sure and update DefaultTreeCellEditor too. 1190 1191 /* labelR is the rectangle that contains iconR and textR. 1192 * Move it to its proper position given the labelAlignment 1193 * properties. 1194 * 1195 * To avoid actually allocating a Rectangle, Rectangle.union 1196 * has been inlined below. 1197 */ 1198 int labelR_x = Math.min(iconR.x, textR.x); 1199 int labelR_width = Math.max(iconR.x + iconR.width, 1200 textR.x + textR.width) - labelR_x; 1201 int labelR_y = Math.min(iconR.y, textR.y); 1202 int labelR_height = Math.max(iconR.y + iconR.height, 1203 textR.y + textR.height) - labelR_y; 1204 1205 int dx, dy; 1206 1207 if (verticalAlignment == TOP) { 1208 dy = viewR.y - labelR_y; 1209 } 1210 else if (verticalAlignment == CENTER) { 1211 dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2)); 1212 } 1213 else { // (verticalAlignment == BOTTOM) 1214 dy = (viewR.y + viewR.height) - (labelR_y + labelR_height); 1215 } 1216 1217 if (horizontalAlignment == LEFT) { 1218 dx = viewR.x - labelR_x; 1219 } 1220 else if (horizontalAlignment == RIGHT) { 1221 dx = (viewR.x + viewR.width) - (labelR_x + labelR_width); 1222 } 1223 else { // (horizontalAlignment == CENTER) 1224 dx = (viewR.x + (viewR.width / 2)) - 1225 (labelR_x + (labelR_width / 2)); 1226 } 1227 1228 /* Translate textR and glypyR by dx,dy. 1229 */ 1230 1231 textR.x += dx; 1232 textR.y += dy; 1233 1234 iconR.x += dx; 1235 iconR.y += dy; 1236 1237 if (lsb < 0) { 1238 // lsb is negative. Shift the x location so that the text is 1239 // visually drawn at the right location. 1240 textR.x -= lsb; 1241 1242 textR.width += lsb; 1243 } 1244 if (rsb > 0) { 1245 textR.width -= rsb; 1246 } 1247 1248 return text; 1249 } 1250 1251 1252 /** 1253 * Paints a component to the specified <code>Graphics</code>. 1254 * This method is primarily useful to render 1255 * <code>Component</code>s that don't exist as part of the visible 1256 * containment hierarchy, but are used for rendering. For 1257 * example, if you are doing your own rendering and want to render 1258 * some text (or even HTML), you could make use of 1259 * <code>JLabel</code>'s text rendering support and have it paint 1260 * directly by way of this method, without adding the label to the 1261 * visible containment hierarchy. 1262 * <p> 1263 * This method makes use of <code>CellRendererPane</code> to handle 1264 * the actual painting, and is only recommended if you use one 1265 * component for rendering. If you make use of multiple components 1266 * to handle the rendering, as <code>JTable</code> does, use 1267 * <code>CellRendererPane</code> directly. Otherwise, as described 1268 * below, you could end up with a <code>CellRendererPane</code> 1269 * per <code>Component</code>. 1270 * <p> 1271 * If <code>c</code>'s parent is not a <code>CellRendererPane</code>, 1272 * a new <code>CellRendererPane</code> is created, <code>c</code> is 1273 * added to it, and the <code>CellRendererPane</code> is added to 1274 * <code>p</code>. If <code>c</code>'s parent is a 1275 * <code>CellRendererPane</code> and the <code>CellRendererPane</code>s 1276 * parent is not <code>p</code>, it is added to <code>p</code>. 1277 * <p> 1278 * The component should either descend from <code>JComponent</code> 1279 * or be another kind of lightweight component. 1280 * A lightweight component is one whose "lightweight" property 1281 * (returned by the <code>Component</code> 1282 * <code>isLightweight</code> method) 1283 * is true. If the Component is not lightweight, bad things map happen: 1284 * crashes, exceptions, painting problems... 1285 * 1286 * @param g the <code>Graphics</code> object to draw on 1287 * @param c the <code>Component</code> to draw 1288 * @param p the intermediate <code>Container</code> 1289 * @param x an int specifying the left side of the area draw in, in pixels, 1290 * measured from the left edge of the graphics context 1291 * @param y an int specifying the top of the area to draw in, in pixels 1292 * measured down from the top edge of the graphics context 1293 * @param w an int specifying the width of the area draw in, in pixels 1294 * @param h an int specifying the height of the area draw in, in pixels 1295 * 1296 * @see CellRendererPane 1297 * @see java.awt.Component#isLightweight 1298 */ 1299 public static void paintComponent(Graphics g, Component c, Container p, int x, int y, int w, int h) { 1300 getCellRendererPane(c, p).paintComponent(g, c, p, x, y, w, h,false); 1301 } 1302 1303 /** 1304 * Paints a component to the specified <code>Graphics</code>. This 1305 * is a cover method for 1306 * {@link #paintComponent(Graphics,Component,Container,int,int,int,int)}. 1307 * Refer to it for more information. 1308 * 1309 * @param g the <code>Graphics</code> object to draw on 1310 * @param c the <code>Component</code> to draw 1311 * @param p the intermediate <code>Container</code> 1312 * @param r the <code>Rectangle</code> to draw in 1313 * 1314 * @see #paintComponent(Graphics,Component,Container,int,int,int,int) 1315 * @see CellRendererPane 1316 */ 1317 public static void paintComponent(Graphics g, Component c, Container p, Rectangle r) { 1318 paintComponent(g, c, p, r.x, r.y, r.width, r.height); 1319 } 1320 1321 1322 /* 1323 * Ensures that cell renderer <code>c</code> has a 1324 * <code>ComponentShell</code> parent and that 1325 * the shell's parent is p. 1326 */ 1327 private static CellRendererPane getCellRendererPane(Component c, Container p) { 1328 Container shell = c.getParent(); 1329 if (shell instanceof CellRendererPane) { 1330 if (shell.getParent() != p) { 1331 p.add(shell); 1332 } 1333 } else { 1334 shell = new CellRendererPane(); 1335 shell.add(c); 1336 p.add(shell); 1337 } 1338 return (CellRendererPane)shell; 1339 } 1340 1341 /** 1342 * A simple minded look and feel change: ask each node in the tree 1343 * to <code>updateUI()</code> -- that is, to initialize its UI property 1344 * with the current look and feel. 1345 * 1346 * @param c the component 1347 */ 1348 public static void updateComponentTreeUI(Component c) { 1349 updateComponentTreeUI0(c); 1350 c.invalidate(); 1351 c.validate(); 1352 c.repaint(); 1353 } 1354 1355 private static void updateComponentTreeUI0(Component c) { 1356 if (c instanceof JComponent) { 1357 JComponent jc = (JComponent) c; 1358 jc.updateUI(); 1359 JPopupMenu jpm =jc.getComponentPopupMenu(); 1360 if(jpm != null) { 1361 updateComponentTreeUI(jpm); 1362 } 1363 } 1364 Component[] children = null; 1365 if (c instanceof JMenu) { 1366 children = ((JMenu)c).getMenuComponents(); 1367 } 1368 else if (c instanceof Container) { 1369 children = ((Container)c).getComponents(); 1370 } 1371 if (children != null) { 1372 for (Component child : children) { 1373 updateComponentTreeUI0(child); 1374 } 1375 } 1376 } 1377 1378 1379 /** 1380 * Causes <i>doRun.run()</i> to be executed asynchronously on the 1381 * AWT event dispatching thread. This will happen after all 1382 * pending AWT events have been processed. This method should 1383 * be used when an application thread needs to update the GUI. 1384 * In the following example the <code>invokeLater</code> call queues 1385 * the <code>Runnable</code> object <code>doHelloWorld</code> 1386 * on the event dispatching thread and 1387 * then prints a message. 1388 * <pre> 1389 * Runnable doHelloWorld = new Runnable() { 1390 * public void run() { 1391 * System.out.println("Hello World on " + Thread.currentThread()); 1392 * } 1393 * }; 1394 * 1395 * SwingUtilities.invokeLater(doHelloWorld); 1396 * System.out.println("This might well be displayed before the other message."); 1397 * </pre> 1398 * If invokeLater is called from the event dispatching thread -- 1399 * for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will 1400 * still be deferred until all pending events have been processed. 1401 * Note that if the <i>doRun.run()</i> throws an uncaught exception 1402 * the event dispatching thread will unwind (not the current thread). 1403 * <p> 1404 * Additional documentation and examples for this method can be 1405 * found in 1406 * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency in Swing</a>. 1407 * <p> 1408 * As of 1.3 this method is just a cover for <code>java.awt.EventQueue.invokeLater()</code>. 1409 * <p> 1410 * Unlike the rest of Swing, this method can be invoked from any thread. 1411 * 1412 * @param doRun the instance of {@code Runnable} 1413 * @see #invokeAndWait 1414 */ 1415 public static void invokeLater(Runnable doRun) { 1416 EventQueue.invokeLater(doRun); 1417 } 1418 1419 1420 /** 1421 * Causes <code>doRun.run()</code> to be executed synchronously on the 1422 * AWT event dispatching thread. This call blocks until 1423 * all pending AWT events have been processed and (then) 1424 * <code>doRun.run()</code> returns. This method should 1425 * be used when an application thread needs to update the GUI. 1426 * It shouldn't be called from the event dispatching thread. 1427 * Here's an example that creates a new application thread 1428 * that uses <code>invokeAndWait</code> to print a string from the event 1429 * dispatching thread and then, when that's finished, print 1430 * a string from the application thread. 1431 * <pre> 1432 * final Runnable doHelloWorld = new Runnable() { 1433 * public void run() { 1434 * System.out.println("Hello World on " + Thread.currentThread()); 1435 * } 1436 * }; 1437 * 1438 * Thread appThread = new Thread() { 1439 * public void run() { 1440 * try { 1441 * SwingUtilities.invokeAndWait(doHelloWorld); 1442 * } 1443 * catch (Exception e) { 1444 * e.printStackTrace(); 1445 * } 1446 * System.out.println("Finished on " + Thread.currentThread()); 1447 * } 1448 * }; 1449 * appThread.start(); 1450 * </pre> 1451 * Note that if the <code>Runnable.run</code> method throws an 1452 * uncaught exception 1453 * (on the event dispatching thread) it's caught and rethrown, as 1454 * an <code>InvocationTargetException</code>, on the caller's thread. 1455 * <p> 1456 * Additional documentation and examples for this method can be 1457 * found in 1458 * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency in Swing</a>. 1459 * <p> 1460 * As of 1.3 this method is just a cover for 1461 * <code>java.awt.EventQueue.invokeAndWait()</code>. 1462 * 1463 * @param doRun the instance of {@code Runnable} 1464 * @exception InterruptedException if we're interrupted while waiting for 1465 * the event dispatching thread to finish executing 1466 * <code>doRun.run()</code> 1467 * @exception InvocationTargetException if an exception is thrown 1468 * while running <code>doRun</code> 1469 * 1470 * @see #invokeLater 1471 */ 1472 public static void invokeAndWait(final Runnable doRun) 1473 throws InterruptedException, InvocationTargetException 1474 { 1475 EventQueue.invokeAndWait(doRun); 1476 } 1477 1478 /** 1479 * Returns true if the current thread is an AWT event dispatching thread. 1480 * <p> 1481 * As of 1.3 this method is just a cover for 1482 * <code>java.awt.EventQueue.isDispatchThread()</code>. 1483 * 1484 * @return true if the current thread is an AWT event dispatching thread 1485 */ 1486 public static boolean isEventDispatchThread() 1487 { 1488 return EventQueue.isDispatchThread(); 1489 } 1490 1491 1492 /* 1493 * --- Accessibility Support --- 1494 * 1495 */ 1496 1497 /** 1498 * Get the index of this object in its accessible parent.<p> 1499 * 1500 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1501 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1502 * of using this method. 1503 * 1504 * @param c the component 1505 * @return -1 of this object does not have an accessible parent. 1506 * Otherwise, the index of the child in its accessible parent. 1507 */ 1508 public static int getAccessibleIndexInParent(Component c) { 1509 return c.getAccessibleContext().getAccessibleIndexInParent(); 1510 } 1511 1512 /** 1513 * Returns the <code>Accessible</code> child contained at the 1514 * local coordinate <code>Point</code>, if one exists. 1515 * Otherwise returns <code>null</code>. 1516 * 1517 * @param c the component 1518 * @param p the local coordinate 1519 * @return the <code>Accessible</code> at the specified location, 1520 * if it exists; otherwise <code>null</code> 1521 */ 1522 public static Accessible getAccessibleAt(Component c, Point p) { 1523 if (c instanceof Container) { 1524 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p); 1525 } else if (c instanceof Accessible) { 1526 Accessible a = (Accessible) c; 1527 if (a != null) { 1528 AccessibleContext ac = a.getAccessibleContext(); 1529 if (ac != null) { 1530 AccessibleComponent acmp; 1531 Point location; 1532 int nchildren = ac.getAccessibleChildrenCount(); 1533 for (int i=0; i < nchildren; i++) { 1534 a = ac.getAccessibleChild(i); 1535 if ((a != null)) { 1536 ac = a.getAccessibleContext(); 1537 if (ac != null) { 1538 acmp = ac.getAccessibleComponent(); 1539 if ((acmp != null) && (acmp.isShowing())) { 1540 location = acmp.getLocation(); 1541 Point np = new Point(p.x-location.x, 1542 p.y-location.y); 1543 if (acmp.contains(np)){ 1544 return a; 1545 } 1546 } 1547 } 1548 } 1549 } 1550 } 1551 } 1552 return (Accessible) c; 1553 } 1554 return null; 1555 } 1556 1557 /** 1558 * Get the state of this object. <p> 1559 * 1560 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1561 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1562 * of using this method. 1563 * 1564 * @param c the component 1565 * @return an instance of AccessibleStateSet containing the current state 1566 * set of the object 1567 * @see AccessibleState 1568 */ 1569 public static AccessibleStateSet getAccessibleStateSet(Component c) { 1570 return c.getAccessibleContext().getAccessibleStateSet(); 1571 } 1572 1573 /** 1574 * Returns the number of accessible children in the object. If all 1575 * of the children of this object implement Accessible, than this 1576 * method should return the number of children of this object. <p> 1577 * 1578 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1579 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1580 * of using this method. 1581 * 1582 * @param c the component 1583 * @return the number of accessible children in the object. 1584 */ 1585 public static int getAccessibleChildrenCount(Component c) { 1586 return c.getAccessibleContext().getAccessibleChildrenCount(); 1587 } 1588 1589 /** 1590 * Return the nth Accessible child of the object. <p> 1591 * 1592 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1593 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1594 * of using this method. 1595 * 1596 * @param c the component 1597 * @param i zero-based index of child 1598 * @return the nth Accessible child of the object 1599 */ 1600 public static Accessible getAccessibleChild(Component c, int i) { 1601 return c.getAccessibleContext().getAccessibleChild(i); 1602 } 1603 1604 /** 1605 * Return the child <code>Component</code> of the specified 1606 * <code>Component</code> that is the focus owner, if any. 1607 * 1608 * @param c the root of the <code>Component</code> hierarchy to 1609 * search for the focus owner 1610 * @return the focus owner, or <code>null</code> if there is no focus 1611 * owner, or if the focus owner is not <code>comp</code>, or a 1612 * descendant of <code>comp</code> 1613 * 1614 * @see java.awt.KeyboardFocusManager#getFocusOwner 1615 * @deprecated As of 1.4, replaced by 1616 * <code>KeyboardFocusManager.getFocusOwner()</code>. 1617 */ 1618 @Deprecated 1619 public static Component findFocusOwner(Component c) { 1620 Component focusOwner = KeyboardFocusManager. 1621 getCurrentKeyboardFocusManager().getFocusOwner(); 1622 1623 // verify focusOwner is a descendant of c 1624 for (Component temp = focusOwner; temp != null; 1625 temp = (temp instanceof Window) ? null : temp.getParent()) 1626 { 1627 if (temp == c) { 1628 return focusOwner; 1629 } 1630 } 1631 1632 return null; 1633 } 1634 1635 /** 1636 * If c is a JRootPane descendant return its JRootPane ancestor. 1637 * If c is a RootPaneContainer then return its JRootPane. 1638 * 1639 * @param c the component 1640 * @return the JRootPane for Component c or {@code null}. 1641 */ 1642 public static JRootPane getRootPane(Component c) { 1643 if (c instanceof RootPaneContainer) { 1644 return ((RootPaneContainer)c).getRootPane(); 1645 } 1646 for( ; c != null; c = c.getParent()) { 1647 if (c instanceof JRootPane) { 1648 return (JRootPane)c; 1649 } 1650 } 1651 return null; 1652 } 1653 1654 1655 /** 1656 * Returns the root component for the current component tree. 1657 * 1658 * @param c the component 1659 * @return the first ancestor of c that's a Window or the last Applet ancestor 1660 */ 1661 @SuppressWarnings("deprecation") 1662 public static Component getRoot(Component c) { 1663 Component applet = null; 1664 for(Component p = c; p != null; p = p.getParent()) { 1665 if (p instanceof Window) { 1666 return p; 1667 } 1668 if (p instanceof Applet) { 1669 applet = p; 1670 } 1671 } 1672 return applet; 1673 } 1674 1675 static JComponent getPaintingOrigin(JComponent c) { 1676 Container p = c; 1677 while ((p = p.getParent()) instanceof JComponent) { 1678 JComponent jp = (JComponent) p; 1679 if (jp.isPaintingOrigin()) { 1680 return jp; 1681 } 1682 } 1683 return null; 1684 } 1685 1686 /** 1687 * Process the key bindings for the <code>Component</code> associated with 1688 * <code>event</code>. This method is only useful if 1689 * <code>event.getComponent()</code> does not descend from 1690 * <code>JComponent</code>, or your are not invoking 1691 * <code>super.processKeyEvent</code> from within your 1692 * <code>JComponent</code> subclass. <code>JComponent</code> 1693 * automatically processes bindings from within its 1694 * <code>processKeyEvent</code> method, hence you rarely need 1695 * to directly invoke this method. 1696 * 1697 * @param event KeyEvent used to identify which bindings to process, as 1698 * well as which Component has focus. 1699 * @return true if a binding has found and processed 1700 * @since 1.4 1701 */ 1702 @SuppressWarnings("deprecation") 1703 public static boolean processKeyBindings(KeyEvent event) { 1704 if (event != null) { 1705 if (event.isConsumed()) { 1706 return false; 1707 } 1708 1709 Component component = event.getComponent(); 1710 boolean pressed = (event.getID() == KeyEvent.KEY_PRESSED); 1711 1712 if (!isValidKeyEventForKeyBindings(event)) { 1713 return false; 1714 } 1715 // Find the first JComponent in the ancestor hierarchy, and 1716 // invoke processKeyBindings on it 1717 while (component != null) { 1718 if (component instanceof JComponent) { 1719 return ((JComponent)component).processKeyBindings( 1720 event, pressed); 1721 } 1722 if ((component instanceof Applet) || 1723 (component instanceof Window)) { 1724 // No JComponents, if Window or Applet parent, process 1725 // WHEN_IN_FOCUSED_WINDOW bindings. 1726 return JComponent.processKeyBindingsForAllComponents( 1727 event, (Container)component, pressed); 1728 } 1729 component = component.getParent(); 1730 } 1731 } 1732 return false; 1733 } 1734 1735 /** 1736 * Returns true if the <code>e</code> is a valid KeyEvent to use in 1737 * processing the key bindings associated with JComponents. 1738 */ 1739 static boolean isValidKeyEventForKeyBindings(KeyEvent e) { 1740 return true; 1741 } 1742 1743 /** 1744 * Invokes {@code actionPerformed} on {@code action} if {@code action} 1745 * is non-{@code null} and accepts the sender object. 1746 * The command for the ActionEvent is determined by: 1747 * <ol> 1748 * <li>If the action was registered via 1749 * <code>registerKeyboardAction</code>, then the command string 1750 * passed in ({@code null} will be used if {@code null} was passed in). 1751 * <li>Action value with name Action.ACTION_COMMAND_KEY, unless {@code null}. 1752 * <li>String value of the KeyEvent, unless <code>getKeyChar</code> 1753 * returns KeyEvent.CHAR_UNDEFINED.. 1754 * </ol> 1755 * This will return true if <code>action</code> is non-{@code null} and 1756 * actionPerformed is invoked on it. 1757 * 1758 * @param action an action 1759 * @param ks a key stroke 1760 * @param event a key event 1761 * @param sender a sender 1762 * @param modifiers action modifiers 1763 * @return {@code true} if {@code action} is non-{@code null} and 1764 * actionPerformed is invoked on it. 1765 * @see javax.swing.Action#accept 1766 * 1767 * @since 1.3 1768 */ 1769 public static boolean notifyAction(Action action, KeyStroke ks, 1770 KeyEvent event, Object sender, 1771 int modifiers) { 1772 1773 if (action == null || !action.accept(sender)) { 1774 return false; 1775 } 1776 1777 Object commandO; 1778 boolean stayNull; 1779 1780 // Get the command object. 1781 commandO = action.getValue(Action.ACTION_COMMAND_KEY); 1782 if (commandO == null && (action instanceof JComponent.ActionStandin)) { 1783 // ActionStandin is used for historical reasons to support 1784 // registerKeyboardAction with a null value. 1785 stayNull = true; 1786 } 1787 else { 1788 stayNull = false; 1789 } 1790 1791 // Convert it to a string. 1792 String command; 1793 1794 if (commandO != null) { 1795 command = commandO.toString(); 1796 } 1797 else if (!stayNull && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { 1798 command = String.valueOf(event.getKeyChar()); 1799 } 1800 else { 1801 // Do null for undefined chars, or if registerKeyboardAction 1802 // was called with a null. 1803 command = null; 1804 } 1805 action.actionPerformed(new ActionEvent(sender, 1806 ActionEvent.ACTION_PERFORMED, command, event.getWhen(), 1807 modifiers)); 1808 return true; 1809 } 1810 1811 1812 /** 1813 * Convenience method to change the UI InputMap for <code>component</code> 1814 * to <code>uiInputMap</code>. If <code>uiInputMap</code> is {@code null}, 1815 * this removes any previously installed UI InputMap. 1816 * 1817 * @param component a component 1818 * @param type a type 1819 * @param uiInputMap an {@code InputMap} 1820 * @since 1.3 1821 */ 1822 public static void replaceUIInputMap(JComponent component, int type, 1823 InputMap uiInputMap) { 1824 InputMap map = component.getInputMap(type, (uiInputMap != null)); 1825 1826 while (map != null) { 1827 InputMap parent = map.getParent(); 1828 if (parent == null || (parent instanceof UIResource)) { 1829 map.setParent(uiInputMap); 1830 return; 1831 } 1832 map = parent; 1833 } 1834 } 1835 1836 1837 /** 1838 * Convenience method to change the UI ActionMap for <code>component</code> 1839 * to <code>uiActionMap</code>. If <code>uiActionMap</code> is {@code null}, 1840 * this removes any previously installed UI ActionMap. 1841 * 1842 * @param component a component 1843 * @param uiActionMap an {@code ActionMap} 1844 * @since 1.3 1845 */ 1846 public static void replaceUIActionMap(JComponent component, 1847 ActionMap uiActionMap) { 1848 ActionMap map = component.getActionMap((uiActionMap != null)); 1849 1850 while (map != null) { 1851 ActionMap parent = map.getParent(); 1852 if (parent == null || (parent instanceof UIResource)) { 1853 map.setParent(uiActionMap); 1854 return; 1855 } 1856 map = parent; 1857 } 1858 } 1859 1860 1861 /** 1862 * Returns the InputMap provided by the UI for condition 1863 * <code>condition</code> in component <code>component</code>. 1864 * <p>This will return {@code null} if the UI has not installed an InputMap 1865 * of the specified type. 1866 * 1867 * @param component a component 1868 * @param condition a condition 1869 * @return the {@code ActionMap} provided by the UI for {@code condition} 1870 * in the component, or {@code null} if the UI has not installed 1871 * an InputMap of the specified type. 1872 * @since 1.3 1873 */ 1874 public static InputMap getUIInputMap(JComponent component, int condition) { 1875 InputMap map = component.getInputMap(condition, false); 1876 while (map != null) { 1877 InputMap parent = map.getParent(); 1878 if (parent instanceof UIResource) { 1879 return parent; 1880 } 1881 map = parent; 1882 } 1883 return null; 1884 } 1885 1886 /** 1887 * Returns the ActionMap provided by the UI 1888 * in component <code>component</code>. 1889 * <p>This will return {@code null} if the UI has not installed an ActionMap. 1890 * 1891 * @param component a component 1892 * @return the {@code ActionMap} provided by the UI in the component, 1893 * or {@code null} if the UI has not installed an ActionMap. 1894 * @since 1.3 1895 */ 1896 public static ActionMap getUIActionMap(JComponent component) { 1897 ActionMap map = component.getActionMap(false); 1898 while (map != null) { 1899 ActionMap parent = map.getParent(); 1900 if (parent instanceof UIResource) { 1901 return parent; 1902 } 1903 map = parent; 1904 } 1905 return null; 1906 } 1907 1908 1909 // Don't use String, as it's not guaranteed to be unique in a Hashtable. 1910 private static final Object sharedOwnerFrameKey = 1911 new StringBuffer("SwingUtilities.sharedOwnerFrame"); 1912 1913 @SuppressWarnings("serial") // JDK-implementation class 1914 static class SharedOwnerFrame extends Frame implements WindowListener { 1915 public void addNotify() { 1916 super.addNotify(); 1917 installListeners(); 1918 } 1919 1920 /** 1921 * Install window listeners on owned windows to watch for displayability changes 1922 */ 1923 void installListeners() { 1924 Window[] windows = getOwnedWindows(); 1925 for (Window window : windows) { 1926 if (window != null) { 1927 window.removeWindowListener(this); 1928 window.addWindowListener(this); 1929 } 1930 } 1931 } 1932 1933 /** 1934 * Watches for displayability changes and disposes shared instance if there are no 1935 * displayable children left. 1936 */ 1937 public void windowClosed(WindowEvent e) { 1938 synchronized(getTreeLock()) { 1939 Window[] windows = getOwnedWindows(); 1940 for (Window window : windows) { 1941 if (window != null) { 1942 if (window.isDisplayable()) { 1943 return; 1944 } 1945 window.removeWindowListener(this); 1946 } 1947 } 1948 dispose(); 1949 } 1950 } 1951 public void windowOpened(WindowEvent e) { 1952 } 1953 public void windowClosing(WindowEvent e) { 1954 } 1955 public void windowIconified(WindowEvent e) { 1956 } 1957 public void windowDeiconified(WindowEvent e) { 1958 } 1959 public void windowActivated(WindowEvent e) { 1960 } 1961 public void windowDeactivated(WindowEvent e) { 1962 } 1963 1964 @SuppressWarnings("deprecation") 1965 public void show() { 1966 // This frame can never be shown 1967 } 1968 public void dispose() { 1969 try { 1970 getToolkit().getSystemEventQueue(); 1971 super.dispose(); 1972 } catch (Exception e) { 1973 // untrusted code not allowed to dispose 1974 } 1975 } 1976 } 1977 1978 /** 1979 * Returns a toolkit-private, shared, invisible Frame 1980 * to be the owner for JDialogs and JWindows created with 1981 * {@code null} owners. 1982 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 1983 * returns true. 1984 * @see java.awt.GraphicsEnvironment#isHeadless 1985 */ 1986 static Frame getSharedOwnerFrame() throws HeadlessException { 1987 Frame sharedOwnerFrame = 1988 (Frame)SwingUtilities.appContextGet(sharedOwnerFrameKey); 1989 if (sharedOwnerFrame == null) { 1990 sharedOwnerFrame = new SharedOwnerFrame(); 1991 SwingUtilities.appContextPut(sharedOwnerFrameKey, 1992 sharedOwnerFrame); 1993 } 1994 return sharedOwnerFrame; 1995 } 1996 1997 /** 1998 * Returns a SharedOwnerFrame's shutdown listener to dispose the SharedOwnerFrame 1999 * if it has no more displayable children. 2000 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 2001 * returns true. 2002 * @see java.awt.GraphicsEnvironment#isHeadless 2003 */ 2004 static WindowListener getSharedOwnerFrameShutdownListener() throws HeadlessException { 2005 Frame sharedOwnerFrame = getSharedOwnerFrame(); 2006 return (WindowListener)sharedOwnerFrame; 2007 } 2008 2009 /* Don't make these AppContext accessors public or protected -- 2010 * since AppContext is in sun.awt in 1.2, we shouldn't expose it 2011 * even indirectly with a public API. 2012 */ 2013 // REMIND(aim): phase out use of 4 methods below since they 2014 // are just private covers for AWT methods (?) 2015 2016 static Object appContextGet(Object key) { 2017 return AppContext.getAppContext().get(key); 2018 } 2019 2020 static void appContextPut(Object key, Object value) { 2021 AppContext.getAppContext().put(key, value); 2022 } 2023 2024 static void appContextRemove(Object key) { 2025 AppContext.getAppContext().remove(key); 2026 } 2027 2028 2029 static Class<?> loadSystemClass(String className) throws ClassNotFoundException { 2030 ReflectUtil.checkPackageAccess(className); 2031 return Class.forName(className, true, Thread.currentThread(). 2032 getContextClassLoader()); 2033 } 2034 2035 2036 /* 2037 * Convenience function for determining ComponentOrientation. Helps us 2038 * avoid having Munge directives throughout the code. 2039 */ 2040 static boolean isLeftToRight( Component c ) { 2041 return c.getComponentOrientation().isLeftToRight(); 2042 } 2043 private SwingUtilities() { 2044 throw new Error("SwingUtilities is just a container for static methods"); 2045 } 2046 2047 /** 2048 * Returns true if the Icon <code>icon</code> is an instance of 2049 * ImageIcon, and the image it contains is the same as <code>image</code>. 2050 */ 2051 static boolean doesIconReferenceImage(Icon icon, Image image) { 2052 Image iconImage = (icon != null && (icon instanceof ImageIcon)) ? 2053 ((ImageIcon)icon).getImage() : null; 2054 return (iconImage == image); 2055 } 2056 2057 /** 2058 * Returns index of the first occurrence of <code>mnemonic</code> 2059 * within string <code>text</code>. Matching algorithm is not 2060 * case-sensitive. 2061 * 2062 * @param text The text to search through, may be {@code null} 2063 * @param mnemonic The mnemonic to find the character for. 2064 * @return index into the string if exists, otherwise -1 2065 */ 2066 static int findDisplayedMnemonicIndex(String text, int mnemonic) { 2067 if (text == null || mnemonic == '\0') { 2068 return -1; 2069 } 2070 2071 char uc = Character.toUpperCase((char)mnemonic); 2072 char lc = Character.toLowerCase((char)mnemonic); 2073 2074 int uci = text.indexOf(uc); 2075 int lci = text.indexOf(lc); 2076 2077 if (uci == -1) { 2078 return lci; 2079 } else if(lci == -1) { 2080 return uci; 2081 } else { 2082 return (lci < uci) ? lci : uci; 2083 } 2084 } 2085 2086 /** 2087 * Stores the position and size of 2088 * the inner painting area of the specified component 2089 * in <code>r</code> and returns <code>r</code>. 2090 * The position and size specify the bounds of the component, 2091 * adjusted so as not to include the border area (the insets). 2092 * This method is useful for classes 2093 * that implement painting code. 2094 * 2095 * @param c the JComponent in question; if {@code null}, this method returns {@code null} 2096 * @param r the Rectangle instance to be modified; 2097 * may be {@code null} 2098 * @return {@code null} if the Component is {@code null}; 2099 * otherwise, returns the passed-in rectangle (if non-{@code null}) 2100 * or a new rectangle specifying position and size information 2101 * 2102 * @since 1.4 2103 */ 2104 public static Rectangle calculateInnerArea(JComponent c, Rectangle r) { 2105 if (c == null) { 2106 return null; 2107 } 2108 Rectangle rect = r; 2109 Insets insets = c.getInsets(); 2110 2111 if (rect == null) { 2112 rect = new Rectangle(); 2113 } 2114 2115 rect.x = insets.left; 2116 rect.y = insets.top; 2117 rect.width = c.getWidth() - insets.left - insets.right; 2118 rect.height = c.getHeight() - insets.top - insets.bottom; 2119 2120 return rect; 2121 } 2122 2123 static void updateRendererOrEditorUI(Object rendererOrEditor) { 2124 if (rendererOrEditor == null) { 2125 return; 2126 } 2127 2128 Component component = null; 2129 2130 if (rendererOrEditor instanceof Component) { 2131 component = (Component)rendererOrEditor; 2132 } 2133 if (rendererOrEditor instanceof DefaultCellEditor) { 2134 component = ((DefaultCellEditor)rendererOrEditor).getComponent(); 2135 } 2136 2137 if (component != null) { 2138 SwingUtilities.updateComponentTreeUI(component); 2139 } 2140 } 2141 2142 /** 2143 * Returns the first ancestor of the {@code component} 2144 * which is not an instance of {@link JLayer}. 2145 * 2146 * @param component {@code Component} to get 2147 * the first ancestor of, which is not a {@link JLayer} instance. 2148 * 2149 * @return the first ancestor of the {@code component} 2150 * which is not an instance of {@link JLayer}. 2151 * If such an ancestor can not be found, {@code null} is returned. 2152 * 2153 * @throws NullPointerException if {@code component} is {@code null} 2154 * @see JLayer 2155 * 2156 * @since 1.7 2157 */ 2158 public static Container getUnwrappedParent(Component component) { 2159 Container parent = component.getParent(); 2160 while(parent instanceof JLayer) { 2161 parent = parent.getParent(); 2162 } 2163 return parent; 2164 } 2165 2166 /** 2167 * Returns the first {@code JViewport}'s descendant 2168 * which is not an instance of {@code JLayer}. 2169 * If such a descendant can not be found, {@code null} is returned. 2170 * 2171 * If the {@code viewport}'s view component is not a {@code JLayer}, 2172 * this method is equivalent to {@link JViewport#getView()} 2173 * otherwise {@link JLayer#getView()} will be recursively 2174 * called on all descending {@code JLayer}s. 2175 * 2176 * @param viewport {@code JViewport} to get the first descendant of, 2177 * which in not a {@code JLayer} instance. 2178 * 2179 * @return the first {@code JViewport}'s descendant 2180 * which is not an instance of {@code JLayer}. 2181 * If such a descendant can not be found, {@code null} is returned. 2182 * 2183 * @throws NullPointerException if {@code viewport} is {@code null} 2184 * @see JViewport#getView() 2185 * @see JLayer 2186 * 2187 * @since 1.7 2188 */ 2189 public static Component getUnwrappedView(JViewport viewport) { 2190 Component view = viewport.getView(); 2191 while (view instanceof JLayer) { 2192 view = ((JLayer)view).getView(); 2193 } 2194 return view; 2195 } 2196 2197 /** 2198 * Retrieves the validate root of a given container. 2199 * 2200 * If the container is contained within a {@code CellRendererPane}, this 2201 * method returns {@code null} due to the synthetic nature of the {@code 2202 * CellRendererPane}. 2203 * <p> 2204 * The component hierarchy must be displayable up to the toplevel component 2205 * (either a {@code Frame} or an {@code Applet} object.) Otherwise this 2206 * method returns {@code null}. 2207 * <p> 2208 * If the {@code visibleOnly} argument is {@code true}, the found validate 2209 * root and all its parents up to the toplevel component must also be 2210 * visible. Otherwise this method returns {@code null}. 2211 * 2212 * @return the validate root of the given container or null 2213 * @see java.awt.Component#isDisplayable() 2214 * @see java.awt.Component#isVisible() 2215 * @since 1.7 2216 */ 2217 @SuppressWarnings("deprecation") 2218 static Container getValidateRoot(Container c, boolean visibleOnly) { 2219 Container root = null; 2220 2221 for (; c != null; c = c.getParent()) 2222 { 2223 if (!c.isDisplayable() || c instanceof CellRendererPane) { 2224 return null; 2225 } 2226 if (c.isValidateRoot()) { 2227 root = c; 2228 break; 2229 } 2230 } 2231 2232 if (root == null) { 2233 return null; 2234 } 2235 2236 for (; c != null; c = c.getParent()) { 2237 if (!c.isDisplayable() || (visibleOnly && !c.isVisible())) { 2238 return null; 2239 } 2240 if (c instanceof Window || c instanceof Applet) { 2241 return root; 2242 } 2243 } 2244 2245 return null; 2246 } 2247 }