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