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