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.stringWidth(c, fm, text); 1094 lsb = SwingUtilities2.getLeftSideBearing(c, fm, text); 1095 if (lsb < 0) { 1096 // If lsb is negative, add it to the width and later 1097 // adjust the x location. This gives more space than is 1098 // actually needed. 1099 // This is done like this for two reasons: 1100 // 1. If we set the width to the actual bounds all 1101 // callers would have to account for negative lsb 1102 // (pref size calculations ONLY look at width of 1103 // textR) 1104 // 2. You can do a drawString at the returned location 1105 // and the text won't be clipped. 1106 textR.width -= lsb; 1107 } 1108 if (textR.width > availTextWidth) { 1109 text = SwingUtilities2.clipString(c, fm, text, 1110 availTextWidth); 1111 textR.width = SwingUtilities2.stringWidth(c, fm, text); 1112 } 1113 textR.height = fm.getHeight(); 1114 } 1115 } 1116 1117 1118 /* Compute textR.x,y given the verticalTextPosition and 1119 * horizontalTextPosition properties 1120 */ 1121 1122 if (verticalTextPosition == TOP) { 1123 if (horizontalTextPosition != CENTER) { 1124 textR.y = 0; 1125 } 1126 else { 1127 textR.y = -(textR.height + gap); 1128 } 1129 } 1130 else if (verticalTextPosition == CENTER) { 1131 textR.y = (iconR.height / 2) - (textR.height / 2); 1132 } 1133 else { // (verticalTextPosition == BOTTOM) 1134 if (horizontalTextPosition != CENTER) { 1135 textR.y = iconR.height - textR.height; 1136 } 1137 else { 1138 textR.y = (iconR.height + gap); 1139 } 1140 } 1141 1142 if (horizontalTextPosition == LEFT) { 1143 textR.x = -(textR.width + gap); 1144 } 1145 else if (horizontalTextPosition == CENTER) { 1146 textR.x = (iconR.width / 2) - (textR.width / 2); 1147 } 1148 else { // (horizontalTextPosition == RIGHT) 1149 textR.x = (iconR.width + gap); 1150 } 1151 1152 // WARNING: DefaultTreeCellEditor uses a shortened version of 1153 // this algorithm to position it's Icon. If you change how this 1154 // is calculated, be sure and update DefaultTreeCellEditor too. 1155 1156 /* labelR is the rectangle that contains iconR and textR. 1157 * Move it to its proper position given the labelAlignment 1158 * properties. 1159 * 1160 * To avoid actually allocating a Rectangle, Rectangle.union 1161 * has been inlined below. 1162 */ 1163 int labelR_x = Math.min(iconR.x, textR.x); 1164 int labelR_width = Math.max(iconR.x + iconR.width, 1165 textR.x + textR.width) - labelR_x; 1166 int labelR_y = Math.min(iconR.y, textR.y); 1167 int labelR_height = Math.max(iconR.y + iconR.height, 1168 textR.y + textR.height) - labelR_y; 1169 1170 int dx, dy; 1171 1172 if (verticalAlignment == TOP) { 1173 dy = viewR.y - labelR_y; 1174 } 1175 else if (verticalAlignment == CENTER) { 1176 dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2)); 1177 } 1178 else { // (verticalAlignment == BOTTOM) 1179 dy = (viewR.y + viewR.height) - (labelR_y + labelR_height); 1180 } 1181 1182 if (horizontalAlignment == LEFT) { 1183 dx = viewR.x - labelR_x; 1184 } 1185 else if (horizontalAlignment == RIGHT) { 1186 dx = (viewR.x + viewR.width) - (labelR_x + labelR_width); 1187 } 1188 else { // (horizontalAlignment == CENTER) 1189 dx = (viewR.x + (viewR.width / 2)) - 1190 (labelR_x + (labelR_width / 2)); 1191 } 1192 1193 /* Translate textR and glypyR by dx,dy. 1194 */ 1195 1196 textR.x += dx; 1197 textR.y += dy; 1198 1199 iconR.x += dx; 1200 iconR.y += dy; 1201 1202 if (lsb < 0) { 1203 // lsb is negative. Shift the x location so that the text is 1204 // visually drawn at the right location. 1205 textR.x -= lsb; 1206 1207 textR.width += lsb; 1208 } 1209 if (rsb > 0) { 1210 textR.width -= rsb; 1211 } 1212 1213 return text; 1214 } 1215 1216 1217 /** 1218 * Paints a component to the specified <code>Graphics</code>. 1219 * This method is primarily useful to render 1220 * <code>Component</code>s that don't exist as part of the visible 1221 * containment hierarchy, but are used for rendering. For 1222 * example, if you are doing your own rendering and want to render 1223 * some text (or even HTML), you could make use of 1224 * <code>JLabel</code>'s text rendering support and have it paint 1225 * directly by way of this method, without adding the label to the 1226 * visible containment hierarchy. 1227 * <p> 1228 * This method makes use of <code>CellRendererPane</code> to handle 1229 * the actual painting, and is only recommended if you use one 1230 * component for rendering. If you make use of multiple components 1231 * to handle the rendering, as <code>JTable</code> does, use 1232 * <code>CellRendererPane</code> directly. Otherwise, as described 1233 * below, you could end up with a <code>CellRendererPane</code> 1234 * per <code>Component</code>. 1235 * <p> 1236 * If <code>c</code>'s parent is not a <code>CellRendererPane</code>, 1237 * a new <code>CellRendererPane</code> is created, <code>c</code> is 1238 * added to it, and the <code>CellRendererPane</code> is added to 1239 * <code>p</code>. If <code>c</code>'s parent is a 1240 * <code>CellRendererPane</code> and the <code>CellRendererPane</code>s 1241 * parent is not <code>p</code>, it is added to <code>p</code>. 1242 * <p> 1243 * The component should either descend from <code>JComponent</code> 1244 * or be another kind of lightweight component. 1245 * A lightweight component is one whose "lightweight" property 1246 * (returned by the <code>Component</code> 1247 * <code>isLightweight</code> method) 1248 * is true. If the Component is not lightweight, bad things map happen: 1249 * crashes, exceptions, painting problems... 1250 * 1251 * @param g the <code>Graphics</code> object to draw on 1252 * @param c the <code>Component</code> to draw 1253 * @param p the intermediate <code>Container</code> 1254 * @param x an int specifying the left side of the area draw in, in pixels, 1255 * measured from the left edge of the graphics context 1256 * @param y an int specifying the top of the area to draw in, in pixels 1257 * measured down from the top edge of the graphics context 1258 * @param w an int specifying the width of the area draw in, in pixels 1259 * @param h an int specifying the height of the area draw in, in pixels 1260 * 1261 * @see CellRendererPane 1262 * @see java.awt.Component#isLightweight 1263 */ 1264 public static void paintComponent(Graphics g, Component c, Container p, int x, int y, int w, int h) { 1265 getCellRendererPane(c, p).paintComponent(g, c, p, x, y, w, h,false); 1266 } 1267 1268 /** 1269 * Paints a component to the specified <code>Graphics</code>. This 1270 * is a cover method for 1271 * {@link #paintComponent(Graphics,Component,Container,int,int,int,int)}. 1272 * Refer to it for more information. 1273 * 1274 * @param g the <code>Graphics</code> object to draw on 1275 * @param c the <code>Component</code> to draw 1276 * @param p the intermediate <code>Container</code> 1277 * @param r the <code>Rectangle</code> to draw in 1278 * 1279 * @see #paintComponent(Graphics,Component,Container,int,int,int,int) 1280 * @see CellRendererPane 1281 */ 1282 public static void paintComponent(Graphics g, Component c, Container p, Rectangle r) { 1283 paintComponent(g, c, p, r.x, r.y, r.width, r.height); 1284 } 1285 1286 1287 /* 1288 * Ensures that cell renderer <code>c</code> has a 1289 * <code>ComponentShell</code> parent and that 1290 * the shell's parent is p. 1291 */ 1292 private static CellRendererPane getCellRendererPane(Component c, Container p) { 1293 Container shell = c.getParent(); 1294 if (shell instanceof CellRendererPane) { 1295 if (shell.getParent() != p) { 1296 p.add(shell); 1297 } 1298 } else { 1299 shell = new CellRendererPane(); 1300 shell.add(c); 1301 p.add(shell); 1302 } 1303 return (CellRendererPane)shell; 1304 } 1305 1306 /** 1307 * A simple minded look and feel change: ask each node in the tree 1308 * to <code>updateUI()</code> -- that is, to initialize its UI property 1309 * with the current look and feel. 1310 * 1311 * @param c the component 1312 */ 1313 public static void updateComponentTreeUI(Component c) { 1314 updateComponentTreeUI0(c); 1315 c.invalidate(); 1316 c.validate(); 1317 c.repaint(); 1318 } 1319 1320 private static void updateComponentTreeUI0(Component c) { 1321 if (c instanceof JComponent) { 1322 JComponent jc = (JComponent) c; 1323 jc.updateUI(); 1324 JPopupMenu jpm =jc.getComponentPopupMenu(); 1325 if(jpm != null) { 1326 updateComponentTreeUI(jpm); 1327 } 1328 } 1329 Component[] children = null; 1330 if (c instanceof JMenu) { 1331 children = ((JMenu)c).getMenuComponents(); 1332 } 1333 else if (c instanceof Container) { 1334 children = ((Container)c).getComponents(); 1335 } 1336 if (children != null) { 1337 for (Component child : children) { 1338 updateComponentTreeUI0(child); 1339 } 1340 } 1341 } 1342 1343 1344 /** 1345 * Causes <i>doRun.run()</i> to be executed asynchronously on the 1346 * AWT event dispatching thread. This will happen after all 1347 * pending AWT events have been processed. This method should 1348 * be used when an application thread needs to update the GUI. 1349 * In the following example the <code>invokeLater</code> call queues 1350 * the <code>Runnable</code> object <code>doHelloWorld</code> 1351 * on the event dispatching thread and 1352 * then prints a message. 1353 * <pre> 1354 * Runnable doHelloWorld = new Runnable() { 1355 * public void run() { 1356 * System.out.println("Hello World on " + Thread.currentThread()); 1357 * } 1358 * }; 1359 * 1360 * SwingUtilities.invokeLater(doHelloWorld); 1361 * System.out.println("This might well be displayed before the other message."); 1362 * </pre> 1363 * If invokeLater is called from the event dispatching thread -- 1364 * for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will 1365 * still be deferred until all pending events have been processed. 1366 * Note that if the <i>doRun.run()</i> throws an uncaught exception 1367 * the event dispatching thread will unwind (not the current thread). 1368 * <p> 1369 * Additional documentation and examples for this method can be 1370 * found in 1371 * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency in Swing</a>. 1372 * <p> 1373 * As of 1.3 this method is just a cover for <code>java.awt.EventQueue.invokeLater()</code>. 1374 * <p> 1375 * Unlike the rest of Swing, this method can be invoked from any thread. 1376 * 1377 * @param doRun the instance of {@code Runnable} 1378 * @see #invokeAndWait 1379 */ 1380 public static void invokeLater(Runnable doRun) { 1381 EventQueue.invokeLater(doRun); 1382 } 1383 1384 1385 /** 1386 * Causes <code>doRun.run()</code> to be executed synchronously on the 1387 * AWT event dispatching thread. This call blocks until 1388 * all pending AWT events have been processed and (then) 1389 * <code>doRun.run()</code> returns. This method should 1390 * be used when an application thread needs to update the GUI. 1391 * It shouldn't be called from the event dispatching thread. 1392 * Here's an example that creates a new application thread 1393 * that uses <code>invokeAndWait</code> to print a string from the event 1394 * dispatching thread and then, when that's finished, print 1395 * a string from the application thread. 1396 * <pre> 1397 * final Runnable doHelloWorld = new Runnable() { 1398 * public void run() { 1399 * System.out.println("Hello World on " + Thread.currentThread()); 1400 * } 1401 * }; 1402 * 1403 * Thread appThread = new Thread() { 1404 * public void run() { 1405 * try { 1406 * SwingUtilities.invokeAndWait(doHelloWorld); 1407 * } 1408 * catch (Exception e) { 1409 * e.printStackTrace(); 1410 * } 1411 * System.out.println("Finished on " + Thread.currentThread()); 1412 * } 1413 * }; 1414 * appThread.start(); 1415 * </pre> 1416 * Note that if the <code>Runnable.run</code> method throws an 1417 * uncaught exception 1418 * (on the event dispatching thread) it's caught and rethrown, as 1419 * an <code>InvocationTargetException</code>, on the caller's thread. 1420 * <p> 1421 * Additional documentation and examples for this method can be 1422 * found in 1423 * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency in Swing</a>. 1424 * <p> 1425 * As of 1.3 this method is just a cover for 1426 * <code>java.awt.EventQueue.invokeAndWait()</code>. 1427 * 1428 * @param doRun the instance of {@code Runnable} 1429 * @exception InterruptedException if we're interrupted while waiting for 1430 * the event dispatching thread to finish executing 1431 * <code>doRun.run()</code> 1432 * @exception InvocationTargetException if an exception is thrown 1433 * while running <code>doRun</code> 1434 * 1435 * @see #invokeLater 1436 */ 1437 public static void invokeAndWait(final Runnable doRun) 1438 throws InterruptedException, InvocationTargetException 1439 { 1440 EventQueue.invokeAndWait(doRun); 1441 } 1442 1443 /** 1444 * Returns true if the current thread is an AWT event dispatching thread. 1445 * <p> 1446 * As of 1.3 this method is just a cover for 1447 * <code>java.awt.EventQueue.isDispatchThread()</code>. 1448 * 1449 * @return true if the current thread is an AWT event dispatching thread 1450 */ 1451 public static boolean isEventDispatchThread() 1452 { 1453 return EventQueue.isDispatchThread(); 1454 } 1455 1456 1457 /* 1458 * --- Accessibility Support --- 1459 * 1460 */ 1461 1462 /** 1463 * Get the index of this object in its accessible parent.<p> 1464 * 1465 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1466 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1467 * of using this method. 1468 * 1469 * @param c the component 1470 * @return -1 of this object does not have an accessible parent. 1471 * Otherwise, the index of the child in its accessible parent. 1472 */ 1473 public static int getAccessibleIndexInParent(Component c) { 1474 return c.getAccessibleContext().getAccessibleIndexInParent(); 1475 } 1476 1477 /** 1478 * Returns the <code>Accessible</code> child contained at the 1479 * local coordinate <code>Point</code>, if one exists. 1480 * Otherwise returns <code>null</code>. 1481 * 1482 * @param c the component 1483 * @param p the local coordinate 1484 * @return the <code>Accessible</code> at the specified location, 1485 * if it exists; otherwise <code>null</code> 1486 */ 1487 public static Accessible getAccessibleAt(Component c, Point p) { 1488 if (c instanceof Container) { 1489 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p); 1490 } else if (c instanceof Accessible) { 1491 Accessible a = (Accessible) c; 1492 if (a != null) { 1493 AccessibleContext ac = a.getAccessibleContext(); 1494 if (ac != null) { 1495 AccessibleComponent acmp; 1496 Point location; 1497 int nchildren = ac.getAccessibleChildrenCount(); 1498 for (int i=0; i < nchildren; i++) { 1499 a = ac.getAccessibleChild(i); 1500 if ((a != null)) { 1501 ac = a.getAccessibleContext(); 1502 if (ac != null) { 1503 acmp = ac.getAccessibleComponent(); 1504 if ((acmp != null) && (acmp.isShowing())) { 1505 location = acmp.getLocation(); 1506 Point np = new Point(p.x-location.x, 1507 p.y-location.y); 1508 if (acmp.contains(np)){ 1509 return a; 1510 } 1511 } 1512 } 1513 } 1514 } 1515 } 1516 } 1517 return (Accessible) c; 1518 } 1519 return null; 1520 } 1521 1522 /** 1523 * Get the state of this object. <p> 1524 * 1525 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1526 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1527 * of using this method. 1528 * 1529 * @param c the component 1530 * @return an instance of AccessibleStateSet containing the current state 1531 * set of the object 1532 * @see AccessibleState 1533 */ 1534 public static AccessibleStateSet getAccessibleStateSet(Component c) { 1535 return c.getAccessibleContext().getAccessibleStateSet(); 1536 } 1537 1538 /** 1539 * Returns the number of accessible children in the object. If all 1540 * of the children of this object implement Accessible, than this 1541 * method should return the number of children of this object. <p> 1542 * 1543 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1544 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1545 * of using this method. 1546 * 1547 * @param c the component 1548 * @return the number of accessible children in the object. 1549 */ 1550 public static int getAccessibleChildrenCount(Component c) { 1551 return c.getAccessibleContext().getAccessibleChildrenCount(); 1552 } 1553 1554 /** 1555 * Return the nth Accessible child of the object. <p> 1556 * 1557 * Note: as of the Java 2 platform v1.3, it is recommended that developers call 1558 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead 1559 * of using this method. 1560 * 1561 * @param c the component 1562 * @param i zero-based index of child 1563 * @return the nth Accessible child of the object 1564 */ 1565 public static Accessible getAccessibleChild(Component c, int i) { 1566 return c.getAccessibleContext().getAccessibleChild(i); 1567 } 1568 1569 /** 1570 * Return the child <code>Component</code> of the specified 1571 * <code>Component</code> that is the focus owner, if any. 1572 * 1573 * @param c the root of the <code>Component</code> hierarchy to 1574 * search for the focus owner 1575 * @return the focus owner, or <code>null</code> if there is no focus 1576 * owner, or if the focus owner is not <code>comp</code>, or a 1577 * descendant of <code>comp</code> 1578 * 1579 * @see java.awt.KeyboardFocusManager#getFocusOwner 1580 * @deprecated As of 1.4, replaced by 1581 * <code>KeyboardFocusManager.getFocusOwner()</code>. 1582 */ 1583 @Deprecated 1584 public static Component findFocusOwner(Component c) { 1585 Component focusOwner = KeyboardFocusManager. 1586 getCurrentKeyboardFocusManager().getFocusOwner(); 1587 1588 // verify focusOwner is a descendant of c 1589 for (Component temp = focusOwner; temp != null; 1590 temp = (temp instanceof Window) ? null : temp.getParent()) 1591 { 1592 if (temp == c) { 1593 return focusOwner; 1594 } 1595 } 1596 1597 return null; 1598 } 1599 1600 /** 1601 * If c is a JRootPane descendant return its JRootPane ancestor. 1602 * If c is a RootPaneContainer then return its JRootPane. 1603 * 1604 * @param c the component 1605 * @return the JRootPane for Component c or {@code null}. 1606 */ 1607 public static JRootPane getRootPane(Component c) { 1608 if (c instanceof RootPaneContainer) { 1609 return ((RootPaneContainer)c).getRootPane(); 1610 } 1611 for( ; c != null; c = c.getParent()) { 1612 if (c instanceof JRootPane) { 1613 return (JRootPane)c; 1614 } 1615 } 1616 return null; 1617 } 1618 1619 1620 /** 1621 * Returns the root component for the current component tree. 1622 * 1623 * @param c the component 1624 * @return the first ancestor of c that's a Window or the last Applet ancestor 1625 */ 1626 public static Component getRoot(Component c) { 1627 Component applet = null; 1628 for(Component p = c; p != null; p = p.getParent()) { 1629 if (p instanceof Window) { 1630 return p; 1631 } 1632 if (p instanceof Applet) { 1633 applet = p; 1634 } 1635 } 1636 return applet; 1637 } 1638 1639 static JComponent getPaintingOrigin(JComponent c) { 1640 Container p = c; 1641 while ((p = p.getParent()) instanceof JComponent) { 1642 JComponent jp = (JComponent) p; 1643 if (jp.isPaintingOrigin()) { 1644 return jp; 1645 } 1646 } 1647 return null; 1648 } 1649 1650 /** 1651 * Process the key bindings for the <code>Component</code> associated with 1652 * <code>event</code>. This method is only useful if 1653 * <code>event.getComponent()</code> does not descend from 1654 * <code>JComponent</code>, or your are not invoking 1655 * <code>super.processKeyEvent</code> from within your 1656 * <code>JComponent</code> subclass. <code>JComponent</code> 1657 * automatically processes bindings from within its 1658 * <code>processKeyEvent</code> method, hence you rarely need 1659 * to directly invoke this method. 1660 * 1661 * @param event KeyEvent used to identify which bindings to process, as 1662 * well as which Component has focus. 1663 * @return true if a binding has found and processed 1664 * @since 1.4 1665 */ 1666 public static boolean processKeyBindings(KeyEvent event) { 1667 if (event != null) { 1668 if (event.isConsumed()) { 1669 return false; 1670 } 1671 1672 Component component = event.getComponent(); 1673 boolean pressed = (event.getID() == KeyEvent.KEY_PRESSED); 1674 1675 if (!isValidKeyEventForKeyBindings(event)) { 1676 return false; 1677 } 1678 // Find the first JComponent in the ancestor hierarchy, and 1679 // invoke processKeyBindings on it 1680 while (component != null) { 1681 if (component instanceof JComponent) { 1682 return ((JComponent)component).processKeyBindings( 1683 event, pressed); 1684 } 1685 if ((component instanceof Applet) || 1686 (component instanceof Window)) { 1687 // No JComponents, if Window or Applet parent, process 1688 // WHEN_IN_FOCUSED_WINDOW bindings. 1689 return JComponent.processKeyBindingsForAllComponents( 1690 event, (Container)component, pressed); 1691 } 1692 component = component.getParent(); 1693 } 1694 } 1695 return false; 1696 } 1697 1698 /** 1699 * Returns true if the <code>e</code> is a valid KeyEvent to use in 1700 * processing the key bindings associated with JComponents. 1701 */ 1702 static boolean isValidKeyEventForKeyBindings(KeyEvent e) { 1703 return true; 1704 } 1705 1706 /** 1707 * Invokes <code>actionPerformed</code> on <code>action</code> if 1708 * <code>action</code> is enabled (and non-{@code null}). The command for the 1709 * ActionEvent is determined by: 1710 * <ol> 1711 * <li>If the action was registered via 1712 * <code>registerKeyboardAction</code>, then the command string 1713 * passed in ({@code null} will be used if {@code null} was passed in). 1714 * <li>Action value with name Action.ACTION_COMMAND_KEY, unless {@code null}. 1715 * <li>String value of the KeyEvent, unless <code>getKeyChar</code> 1716 * returns KeyEvent.CHAR_UNDEFINED.. 1717 * </ol> 1718 * This will return true if <code>action</code> is non-{@code null} and 1719 * actionPerformed is invoked on it. 1720 * 1721 * @param action an action 1722 * @param ks a key stroke 1723 * @param event a key event 1724 * @param sender a sender 1725 * @param modifiers action modifiers 1726 * @return {@code true} if {@code action} is non-{@code null} and 1727 * actionPerformed is invoked on it. 1728 * 1729 * @since 1.3 1730 */ 1731 public static boolean notifyAction(Action action, KeyStroke ks, 1732 KeyEvent event, Object sender, 1733 int modifiers) { 1734 if (action == null) { 1735 return false; 1736 } 1737 if (action instanceof UIAction) { 1738 if (!((UIAction)action).isEnabled(sender)) { 1739 return false; 1740 } 1741 } 1742 else if (!action.isEnabled()) { 1743 return false; 1744 } 1745 Object commandO; 1746 boolean stayNull; 1747 1748 // Get the command object. 1749 commandO = action.getValue(Action.ACTION_COMMAND_KEY); 1750 if (commandO == null && (action instanceof JComponent.ActionStandin)) { 1751 // ActionStandin is used for historical reasons to support 1752 // registerKeyboardAction with a null value. 1753 stayNull = true; 1754 } 1755 else { 1756 stayNull = false; 1757 } 1758 1759 // Convert it to a string. 1760 String command; 1761 1762 if (commandO != null) { 1763 command = commandO.toString(); 1764 } 1765 else if (!stayNull && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { 1766 command = String.valueOf(event.getKeyChar()); 1767 } 1768 else { 1769 // Do null for undefined chars, or if registerKeyboardAction 1770 // was called with a null. 1771 command = null; 1772 } 1773 action.actionPerformed(new ActionEvent(sender, 1774 ActionEvent.ACTION_PERFORMED, command, event.getWhen(), 1775 modifiers)); 1776 return true; 1777 } 1778 1779 1780 /** 1781 * Convenience method to change the UI InputMap for <code>component</code> 1782 * to <code>uiInputMap</code>. If <code>uiInputMap</code> is {@code null}, 1783 * this removes any previously installed UI InputMap. 1784 * 1785 * @param component a component 1786 * @param type a type 1787 * @param uiInputMap an {@code InputMap} 1788 * @since 1.3 1789 */ 1790 public static void replaceUIInputMap(JComponent component, int type, 1791 InputMap uiInputMap) { 1792 InputMap map = component.getInputMap(type, (uiInputMap != null)); 1793 1794 while (map != null) { 1795 InputMap parent = map.getParent(); 1796 if (parent == null || (parent instanceof UIResource)) { 1797 map.setParent(uiInputMap); 1798 return; 1799 } 1800 map = parent; 1801 } 1802 } 1803 1804 1805 /** 1806 * Convenience method to change the UI ActionMap for <code>component</code> 1807 * to <code>uiActionMap</code>. If <code>uiActionMap</code> is {@code null}, 1808 * this removes any previously installed UI ActionMap. 1809 * 1810 * @param component a component 1811 * @param uiActionMap an {@code ActionMap} 1812 * @since 1.3 1813 */ 1814 public static void replaceUIActionMap(JComponent component, 1815 ActionMap uiActionMap) { 1816 ActionMap map = component.getActionMap((uiActionMap != null)); 1817 1818 while (map != null) { 1819 ActionMap parent = map.getParent(); 1820 if (parent == null || (parent instanceof UIResource)) { 1821 map.setParent(uiActionMap); 1822 return; 1823 } 1824 map = parent; 1825 } 1826 } 1827 1828 1829 /** 1830 * Returns the InputMap provided by the UI for condition 1831 * <code>condition</code> in component <code>component</code>. 1832 * <p>This will return {@code null} if the UI has not installed an InputMap 1833 * of the specified type. 1834 * 1835 * @param component a component 1836 * @param condition a condition 1837 * @return the {@code ActionMap} provided by the UI for {@code condition} 1838 * in the component, or {@code null} if the UI has not installed 1839 * an InputMap of the specified type. 1840 * @since 1.3 1841 */ 1842 public static InputMap getUIInputMap(JComponent component, int condition) { 1843 InputMap map = component.getInputMap(condition, false); 1844 while (map != null) { 1845 InputMap parent = map.getParent(); 1846 if (parent instanceof UIResource) { 1847 return parent; 1848 } 1849 map = parent; 1850 } 1851 return null; 1852 } 1853 1854 /** 1855 * Returns the ActionMap provided by the UI 1856 * in component <code>component</code>. 1857 * <p>This will return {@code null} if the UI has not installed an ActionMap. 1858 * 1859 * @param component a component 1860 * @return the {@code ActionMap} provided by the UI in the component, 1861 * or {@code null} if the UI has not installed an ActionMap. 1862 * @since 1.3 1863 */ 1864 public static ActionMap getUIActionMap(JComponent component) { 1865 ActionMap map = component.getActionMap(false); 1866 while (map != null) { 1867 ActionMap parent = map.getParent(); 1868 if (parent instanceof UIResource) { 1869 return parent; 1870 } 1871 map = parent; 1872 } 1873 return null; 1874 } 1875 1876 1877 // Don't use String, as it's not guaranteed to be unique in a Hashtable. 1878 private static final Object sharedOwnerFrameKey = 1879 new StringBuffer("SwingUtilities.sharedOwnerFrame"); 1880 1881 @SuppressWarnings("serial") // JDK-implementation class 1882 static class SharedOwnerFrame extends Frame implements WindowListener { 1883 public void addNotify() { 1884 super.addNotify(); 1885 installListeners(); 1886 } 1887 1888 /** 1889 * Install window listeners on owned windows to watch for displayability changes 1890 */ 1891 void installListeners() { 1892 Window[] windows = getOwnedWindows(); 1893 for (Window window : windows) { 1894 if (window != null) { 1895 window.removeWindowListener(this); 1896 window.addWindowListener(this); 1897 } 1898 } 1899 } 1900 1901 /** 1902 * Watches for displayability changes and disposes shared instance if there are no 1903 * displayable children left. 1904 */ 1905 public void windowClosed(WindowEvent e) { 1906 synchronized(getTreeLock()) { 1907 Window[] windows = getOwnedWindows(); 1908 for (Window window : windows) { 1909 if (window != null) { 1910 if (window.isDisplayable()) { 1911 return; 1912 } 1913 window.removeWindowListener(this); 1914 } 1915 } 1916 dispose(); 1917 } 1918 } 1919 public void windowOpened(WindowEvent e) { 1920 } 1921 public void windowClosing(WindowEvent e) { 1922 } 1923 public void windowIconified(WindowEvent e) { 1924 } 1925 public void windowDeiconified(WindowEvent e) { 1926 } 1927 public void windowActivated(WindowEvent e) { 1928 } 1929 public void windowDeactivated(WindowEvent e) { 1930 } 1931 1932 @SuppressWarnings("deprecation") 1933 public void show() { 1934 // This frame can never be shown 1935 } 1936 public void dispose() { 1937 try { 1938 getToolkit().getSystemEventQueue(); 1939 super.dispose(); 1940 } catch (Exception e) { 1941 // untrusted code not allowed to dispose 1942 } 1943 } 1944 } 1945 1946 /** 1947 * Returns a toolkit-private, shared, invisible Frame 1948 * to be the owner for JDialogs and JWindows created with 1949 * {@code null} owners. 1950 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 1951 * returns true. 1952 * @see java.awt.GraphicsEnvironment#isHeadless 1953 */ 1954 static Frame getSharedOwnerFrame() throws HeadlessException { 1955 Frame sharedOwnerFrame = 1956 (Frame)SwingUtilities.appContextGet(sharedOwnerFrameKey); 1957 if (sharedOwnerFrame == null) { 1958 sharedOwnerFrame = new SharedOwnerFrame(); 1959 SwingUtilities.appContextPut(sharedOwnerFrameKey, 1960 sharedOwnerFrame); 1961 } 1962 return sharedOwnerFrame; 1963 } 1964 1965 /** 1966 * Returns a SharedOwnerFrame's shutdown listener to dispose the SharedOwnerFrame 1967 * if it has no more displayable children. 1968 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 1969 * returns true. 1970 * @see java.awt.GraphicsEnvironment#isHeadless 1971 */ 1972 static WindowListener getSharedOwnerFrameShutdownListener() throws HeadlessException { 1973 Frame sharedOwnerFrame = getSharedOwnerFrame(); 1974 return (WindowListener)sharedOwnerFrame; 1975 } 1976 1977 /* Don't make these AppContext accessors public or protected -- 1978 * since AppContext is in sun.awt in 1.2, we shouldn't expose it 1979 * even indirectly with a public API. 1980 */ 1981 // REMIND(aim): phase out use of 4 methods below since they 1982 // are just private covers for AWT methods (?) 1983 1984 static Object appContextGet(Object key) { 1985 return AppContext.getAppContext().get(key); 1986 } 1987 1988 static void appContextPut(Object key, Object value) { 1989 AppContext.getAppContext().put(key, value); 1990 } 1991 1992 static void appContextRemove(Object key) { 1993 AppContext.getAppContext().remove(key); 1994 } 1995 1996 1997 static Class<?> loadSystemClass(String className) throws ClassNotFoundException { 1998 ReflectUtil.checkPackageAccess(className); 1999 return Class.forName(className, true, Thread.currentThread(). 2000 getContextClassLoader()); 2001 } 2002 2003 2004 /* 2005 * Convenience function for determining ComponentOrientation. Helps us 2006 * avoid having Munge directives throughout the code. 2007 */ 2008 static boolean isLeftToRight( Component c ) { 2009 return c.getComponentOrientation().isLeftToRight(); 2010 } 2011 private SwingUtilities() { 2012 throw new Error("SwingUtilities is just a container for static methods"); 2013 } 2014 2015 /** 2016 * Returns true if the Icon <code>icon</code> is an instance of 2017 * ImageIcon, and the image it contains is the same as <code>image</code>. 2018 */ 2019 static boolean doesIconReferenceImage(Icon icon, Image image) { 2020 Image iconImage = (icon != null && (icon instanceof ImageIcon)) ? 2021 ((ImageIcon)icon).getImage() : null; 2022 return (iconImage == image); 2023 } 2024 2025 /** 2026 * Returns index of the first occurrence of <code>mnemonic</code> 2027 * within string <code>text</code>. Matching algorithm is not 2028 * case-sensitive. 2029 * 2030 * @param text The text to search through, may be {@code null} 2031 * @param mnemonic The mnemonic to find the character for. 2032 * @return index into the string if exists, otherwise -1 2033 */ 2034 static int findDisplayedMnemonicIndex(String text, int mnemonic) { 2035 if (text == null || mnemonic == '\0') { 2036 return -1; 2037 } 2038 2039 char uc = Character.toUpperCase((char)mnemonic); 2040 char lc = Character.toLowerCase((char)mnemonic); 2041 2042 int uci = text.indexOf(uc); 2043 int lci = text.indexOf(lc); 2044 2045 if (uci == -1) { 2046 return lci; 2047 } else if(lci == -1) { 2048 return uci; 2049 } else { 2050 return (lci < uci) ? lci : uci; 2051 } 2052 } 2053 2054 /** 2055 * Stores the position and size of 2056 * the inner painting area of the specified component 2057 * in <code>r</code> and returns <code>r</code>. 2058 * The position and size specify the bounds of the component, 2059 * adjusted so as not to include the border area (the insets). 2060 * This method is useful for classes 2061 * that implement painting code. 2062 * 2063 * @param c the JComponent in question; if {@code null}, this method returns {@code null} 2064 * @param r the Rectangle instance to be modified; 2065 * may be {@code null} 2066 * @return {@code null} if the Component is {@code null}; 2067 * otherwise, returns the passed-in rectangle (if non-{@code null}) 2068 * or a new rectangle specifying position and size information 2069 * 2070 * @since 1.4 2071 */ 2072 public static Rectangle calculateInnerArea(JComponent c, Rectangle r) { 2073 if (c == null) { 2074 return null; 2075 } 2076 Rectangle rect = r; 2077 Insets insets = c.getInsets(); 2078 2079 if (rect == null) { 2080 rect = new Rectangle(); 2081 } 2082 2083 rect.x = insets.left; 2084 rect.y = insets.top; 2085 rect.width = c.getWidth() - insets.left - insets.right; 2086 rect.height = c.getHeight() - insets.top - insets.bottom; 2087 2088 return rect; 2089 } 2090 2091 static void updateRendererOrEditorUI(Object rendererOrEditor) { 2092 if (rendererOrEditor == null) { 2093 return; 2094 } 2095 2096 Component component = null; 2097 2098 if (rendererOrEditor instanceof Component) { 2099 component = (Component)rendererOrEditor; 2100 } 2101 if (rendererOrEditor instanceof DefaultCellEditor) { 2102 component = ((DefaultCellEditor)rendererOrEditor).getComponent(); 2103 } 2104 2105 if (component != null) { 2106 SwingUtilities.updateComponentTreeUI(component); 2107 } 2108 } 2109 2110 /** 2111 * Returns the first ancestor of the {@code component} 2112 * which is not an instance of {@link JLayer}. 2113 * 2114 * @param component {@code Component} to get 2115 * the first ancestor of, which is not a {@link JLayer} instance. 2116 * 2117 * @return the first ancestor of the {@code component} 2118 * which is not an instance of {@link JLayer}. 2119 * If such an ancestor can not be found, {@code null} is returned. 2120 * 2121 * @throws NullPointerException if {@code component} is {@code null} 2122 * @see JLayer 2123 * 2124 * @since 1.7 2125 */ 2126 public static Container getUnwrappedParent(Component component) { 2127 Container parent = component.getParent(); 2128 while(parent instanceof JLayer) { 2129 parent = parent.getParent(); 2130 } 2131 return parent; 2132 } 2133 2134 /** 2135 * Returns the first {@code JViewport}'s descendant 2136 * which is not an instance of {@code JLayer}. 2137 * If such a descendant can not be found, {@code null} is returned. 2138 * 2139 * If the {@code viewport}'s view component is not a {@code JLayer}, 2140 * this method is equivalent to {@link JViewport#getView()} 2141 * otherwise {@link JLayer#getView()} will be recursively 2142 * called on all descending {@code JLayer}s. 2143 * 2144 * @param viewport {@code JViewport} to get the first descendant of, 2145 * which in not a {@code JLayer} instance. 2146 * 2147 * @return the first {@code JViewport}'s descendant 2148 * which is not an instance of {@code JLayer}. 2149 * If such a descendant can not be found, {@code null} is returned. 2150 * 2151 * @throws NullPointerException if {@code viewport} is {@code null} 2152 * @see JViewport#getView() 2153 * @see JLayer 2154 * 2155 * @since 1.7 2156 */ 2157 public static Component getUnwrappedView(JViewport viewport) { 2158 Component view = viewport.getView(); 2159 while (view instanceof JLayer) { 2160 view = ((JLayer)view).getView(); 2161 } 2162 return view; 2163 } 2164 2165 /** 2166 * Retrieves the validate root of a given container. 2167 * 2168 * If the container is contained within a {@code CellRendererPane}, this 2169 * method returns {@code null} due to the synthetic nature of the {@code 2170 * CellRendererPane}. 2171 * <p> 2172 * The component hierarchy must be displayable up to the toplevel component 2173 * (either a {@code Frame} or an {@code Applet} object.) Otherwise this 2174 * method returns {@code null}. 2175 * <p> 2176 * If the {@code visibleOnly} argument is {@code true}, the found validate 2177 * root and all its parents up to the toplevel component must also be 2178 * visible. Otherwise this method returns {@code null}. 2179 * 2180 * @return the validate root of the given container or null 2181 * @see java.awt.Component#isDisplayable() 2182 * @see java.awt.Component#isVisible() 2183 * @since 1.7 2184 */ 2185 static Container getValidateRoot(Container c, boolean visibleOnly) { 2186 Container root = null; 2187 2188 for (; c != null; c = c.getParent()) 2189 { 2190 if (!c.isDisplayable() || c instanceof CellRendererPane) { 2191 return null; 2192 } 2193 if (c.isValidateRoot()) { 2194 root = c; 2195 break; 2196 } 2197 } 2198 2199 if (root == null) { 2200 return null; 2201 } 2202 2203 for (; c != null; c = c.getParent()) { 2204 if (!c.isDisplayable() || (visibleOnly && !c.isVisible())) { 2205 return null; 2206 } 2207 if (c instanceof Window || c instanceof Applet) { 2208 return root; 2209 } 2210 } 2211 2212 return null; 2213 } 2214 }