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