001/* SwingUtilities.java -- 002 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing; 040 041import java.applet.Applet; 042import java.awt.Component; 043import java.awt.Container; 044import java.awt.FontMetrics; 045import java.awt.Frame; 046import java.awt.Graphics; 047import java.awt.Insets; 048import java.awt.KeyboardFocusManager; 049import java.awt.Point; 050import java.awt.Rectangle; 051import java.awt.Shape; 052import java.awt.Window; 053import java.awt.event.ActionEvent; 054import java.awt.event.InputEvent; 055import java.awt.event.KeyEvent; 056import java.awt.event.MouseEvent; 057import java.lang.reflect.InvocationTargetException; 058 059import javax.accessibility.Accessible; 060import javax.accessibility.AccessibleStateSet; 061import javax.swing.plaf.ActionMapUIResource; 062import javax.swing.plaf.InputMapUIResource; 063import javax.swing.plaf.basic.BasicHTML; 064import javax.swing.text.View; 065 066/** 067 * A number of static utility functions which are 068 * useful when drawing swing components, dispatching events, or calculating 069 * regions which need painting. 070 * 071 * @author Graydon Hoare (graydon@redhat.com) 072 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 073 */ 074public class SwingUtilities 075 implements SwingConstants 076{ 077 /** 078 * This frame should be used as parent for JWindow or JDialog 079 * that doesn't an owner 080 */ 081 private static OwnerFrame ownerFrame; 082 083 private SwingUtilities() 084 { 085 // Do nothing. 086 } 087 088 /** 089 * Calculates the portion of the component's bounds which is inside the 090 * component's border insets. This area is usually the area a component 091 * should confine its painting to. The coordinates are returned in terms 092 * of the <em>component's</em> coordinate system, where (0,0) is the 093 * upper left corner of the component's bounds. 094 * 095 * @param c the component to measure the bounds of (if <code>null</code>, 096 * this method returns <code>null</code>). 097 * @param r a carrier to store the return value in (if <code>null</code>, a 098 * new <code>Rectangle</code> instance is created). 099 * 100 * @return The calculated area inside the component and its border insets. 101 */ 102 public static Rectangle calculateInnerArea(JComponent c, Rectangle r) 103 { 104 if (c == null) 105 return null; 106 r = c.getBounds(r); 107 Insets i = c.getInsets(); 108 r.x = i.left; 109 r.width = r.width - i.left - i.right; 110 r.y = i.top; 111 r.height = r.height - i.top - i.bottom; 112 return r; 113 } 114 115 /** 116 * Returns the focus owner or <code>null</code> if <code>comp</code> is not 117 * the focus owner or a parent of it. 118 * 119 * @param comp the focus owner or a parent of it 120 * 121 * @return the focus owner, or <code>null</code> 122 * 123 * @deprecated 1.4 Replaced by 124 * <code>KeyboardFocusManager.getFocusOwner()</code>. 125 */ 126 public static Component findFocusOwner(Component comp) 127 { 128 // Get real focus owner. 129 Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager() 130 .getFocusOwner(); 131 132 // Check if comp is the focus owner or a parent of it. 133 Component tmp = focusOwner; 134 135 while (tmp != null) 136 { 137 if (tmp == comp) 138 return focusOwner; 139 140 tmp = tmp.getParent(); 141 } 142 143 return null; 144 } 145 146 /** 147 * Returns the <code>Accessible</code> child of the specified component 148 * which appears at the supplied <code>Point</code>. If there is no 149 * child located at that particular pair of co-ordinates, null is returned 150 * instead. 151 * 152 * @param c the component whose children may be found at the specified 153 * point. 154 * @param p the point at which to look for the existence of children 155 * of the specified component. 156 * @return the <code>Accessible</code> child at the point, <code>p</code>, 157 * or null if there is no child at this point. 158 * @see javax.accessibility.AccessibleComponent#getAccessibleAt 159 */ 160 public static Accessible getAccessibleAt(Component c, Point p) 161 { 162 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p); 163 } 164 165 /** 166 * <p> 167 * Returns the <code>Accessible</code> child of the specified component 168 * that has the supplied index within the parent component. The indexing 169 * of the children is zero-based, making the first child have an index of 170 * 0. 171 * </p> 172 * <p> 173 * Caution is advised when using this method, as its operation relies 174 * on the behaviour of varying implementations of an abstract method. 175 * For greater surety, direct use of the AWT component implementation 176 * of this method is advised. 177 * </p> 178 * 179 * @param c the component whose child should be returned. 180 * @param i the index of the child within the parent component. 181 * @return the <code>Accessible</code> child at index <code>i</code> 182 * in the component, <code>c</code>. 183 * @see javax.accessibility.AccessibleContext#getAccessibleChild 184 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild 185 */ 186 public static Accessible getAccessibleChild(Component c, int i) 187 { 188 return c.getAccessibleContext().getAccessibleChild(i); 189 } 190 191 /** 192 * <p> 193 * Returns the number of <code>Accessible</code> children within 194 * the supplied component. 195 * </p> 196 * <p> 197 * Caution is advised when using this method, as its operation relies 198 * on the behaviour of varying implementations of an abstract method. 199 * For greater surety, direct use of the AWT component implementation 200 * of this method is advised. 201 * </p> 202 * 203 * @param c the component whose children should be counted. 204 * @return the number of children belonging to the component, 205 * <code>c</code>. 206 * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount 207 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount 208 */ 209 public static int getAccessibleChildrenCount(Component c) 210 { 211 return c.getAccessibleContext().getAccessibleChildrenCount(); 212 } 213 214 /** 215 * <p> 216 * Returns the zero-based index of the specified component 217 * within its parent. If the component doesn't have a parent, 218 * -1 is returned. 219 * </p> 220 * <p> 221 * Caution is advised when using this method, as its operation relies 222 * on the behaviour of varying implementations of an abstract method. 223 * For greater surety, direct use of the AWT component implementation 224 * of this method is advised. 225 * </p> 226 * 227 * @param c the component whose parental index should be found. 228 * @return the index of the component within its parent, or -1 229 * if the component doesn't have a parent. 230 * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent 231 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent 232 */ 233 public static int getAccessibleIndexInParent(Component c) 234 { 235 return c.getAccessibleContext().getAccessibleIndexInParent(); 236 } 237 238 /** 239 * <p> 240 * Returns a set of <code>AccessibleState</code>s, which represent 241 * the state of the supplied component. 242 * </p> 243 * <p> 244 * Caution is advised when using this method, as its operation relies 245 * on the behaviour of varying implementations of an abstract method. 246 * For greater surety, direct use of the AWT component implementation 247 * of this method is advised. 248 * </p> 249 * 250 * @param c the component whose accessible state should be retrieved. 251 * @return a set of <code>AccessibleState</code> objects, which represent 252 * the state of the supplied component. 253 * @see javax.accessibility.AccessibleContext#getAccessibleStateSet 254 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet 255 */ 256 public static AccessibleStateSet getAccessibleStateSet(Component c) 257 { 258 return c.getAccessibleContext().getAccessibleStateSet(); 259 } 260 261 /** 262 * Calculates the bounds of a component in the component's own coordinate 263 * space. The result has the same height and width as the component's 264 * bounds, but its location is set to (0,0). 265 * 266 * @param aComponent The component to measure 267 * 268 * @return The component's bounds in its local coordinate space 269 */ 270 public static Rectangle getLocalBounds(Component aComponent) 271 { 272 Rectangle bounds = aComponent.getBounds(); 273 return new Rectangle(0, 0, bounds.width, bounds.height); 274 } 275 276 /** 277 * If <code>comp</code> is a RootPaneContainer, return its JRootPane. 278 * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>. 279 * 280 * @param comp The component to get the JRootPane of 281 * 282 * @return a suitable JRootPane for <code>comp</code>, or <code>null</code> 283 * 284 * @see javax.swing.RootPaneContainer#getRootPane 285 * @see #getAncestorOfClass 286 */ 287 public static JRootPane getRootPane(Component comp) 288 { 289 if (comp instanceof RootPaneContainer) 290 return ((RootPaneContainer)comp).getRootPane(); 291 else 292 return (JRootPane) getAncestorOfClass(JRootPane.class, comp); 293 } 294 295 /** 296 * Returns the least ancestor of <code>comp</code> which has the 297 * specified name. 298 * 299 * @param name The name to search for 300 * @param comp The component to search the ancestors of 301 * 302 * @return The nearest ancestor of <code>comp</code> with the given 303 * name, or <code>null</code> if no such ancestor exists 304 * 305 * @see java.awt.Component#getName 306 * @see #getAncestorOfClass 307 */ 308 public static Container getAncestorNamed(String name, Component comp) 309 { 310 while (comp != null && (comp.getName() != name)) 311 comp = comp.getParent(); 312 return (Container) comp; 313 } 314 315 /** 316 * Returns the least ancestor of <code>comp</code> which is an instance 317 * of the specified class. 318 * 319 * @param c The class to search for 320 * @param comp The component to search the ancestors of 321 * 322 * @return The nearest ancestor of <code>comp</code> which is an instance 323 * of the given class, or <code>null</code> if no such ancestor exists 324 * 325 * @see #getAncestorOfClass 326 * @see #windowForComponent 327 */ 328 public static Container getAncestorOfClass(Class<?> c, Component comp) 329 { 330 while (comp != null && (! c.isInstance(comp))) 331 comp = comp.getParent(); 332 return (Container) comp; 333 } 334 335 /** 336 * Returns the first ancestor of <code>comp</code> that is a {@link Window} 337 * or <code>null</code> if <code>comp</code> is not contained in a 338 * {@link Window}. 339 * 340 * This is equivalent to calling 341 * <code>getAncestorOfClass(Window, comp)</code> or 342 * <code>windowForComponent(comp)</code>. 343 * 344 * @param comp the component for which we are searching the ancestor Window 345 * 346 * @return the first ancestor Window of <code>comp</code> or 347 * <code>null</code> if <code>comp</code> is not contained in a Window 348 */ 349 public static Window getWindowAncestor(Component comp) 350 { 351 return (Window) getAncestorOfClass(Window.class, comp); 352 } 353 354 /** 355 * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>. 356 * 357 * @param comp The component to search for an ancestor window 358 * 359 * @return An ancestral window, or <code>null</code> if none exists 360 */ 361 public static Window windowForComponent(Component comp) 362 { 363 return (Window) getAncestorOfClass(Window.class, comp); 364 } 365 366 /** 367 * Returns the "root" of the component tree containint <code>comp</code> 368 * The root is defined as either the <em>least</em> ancestor of 369 * <code>comp</code> which is a {@link Window}, or the <em>greatest</em> 370 * ancestor of <code>comp</code> which is a {@link Applet} if no {@link 371 * Window} ancestors are found. 372 * 373 * @param comp The component to search for a root 374 * 375 * @return The root of the component's tree, or <code>null</code> 376 */ 377 public static Component getRoot(Component comp) 378 { 379 Applet app = null; 380 Window win = null; 381 382 while (comp != null) 383 { 384 if (win == null && comp instanceof Window) 385 win = (Window) comp; 386 else if (comp instanceof Applet) 387 app = (Applet) comp; 388 comp = comp.getParent(); 389 } 390 391 if (win != null) 392 return win; 393 return app; 394 } 395 396 /** 397 * Return true if a descends from b, in other words if b is an ancestor of a. 398 * 399 * @param a The child to search the ancestry of 400 * @param b The potential ancestor to search for 401 * @return true if a is a descendent of b, false otherwise 402 */ 403 public static boolean isDescendingFrom(Component a, Component b) 404 { 405 while (true) 406 { 407 if (a == null || b == null) 408 return false; 409 if (a == b) 410 return true; 411 a = a.getParent(); 412 } 413 } 414 415 /** 416 * Returns the deepest descendent of parent which is both visible and 417 * contains the point <code>(x,y)</code>. Returns parent when either 418 * parent is not a container, or has no children which contain 419 * <code>(x,y)</code>. Returns <code>null</code> when either 420 * <code>(x,y)</code> is outside the bounds of parent, or parent is 421 * <code>null</code>. 422 * 423 * @param parent The component to search the descendents of 424 * @param x Horizontal coordinate to search for 425 * @param y Vertical coordinate to search for 426 * 427 * @return A component containing <code>(x,y)</code>, or 428 * <code>null</code> 429 * 430 * @see java.awt.Container#findComponentAt(int, int) 431 */ 432 public static Component getDeepestComponentAt(Component parent, int x, int y) 433 { 434 if (parent == null || (! parent.contains(x, y))) 435 return null; 436 437 if (! (parent instanceof Container)) 438 return parent; 439 440 Container c = (Container) parent; 441 return c.findComponentAt(x, y); 442 } 443 444 /** 445 * Converts a point from a component's local coordinate space to "screen" 446 * coordinates (such as the coordinate space mouse events are delivered 447 * in). This operation is equivalent to translating the point by the 448 * location of the component (which is the origin of its coordinate 449 * space). 450 * 451 * @param p The point to convert 452 * @param c The component which the point is expressed in terms of 453 * 454 * @see #convertPointFromScreen 455 */ 456 public static void convertPointToScreen(Point p, Component c) 457 { 458 Point c0 = c.getLocationOnScreen(); 459 p.translate(c0.x, c0.y); 460 } 461 462 /** 463 * Converts a point from "screen" coordinates (such as the coordinate 464 * space mouse events are delivered in) to a component's local coordinate 465 * space. This operation is equivalent to translating the point by the 466 * negation of the component's location (which is the origin of its 467 * coordinate space). 468 * 469 * @param p The point to convert 470 * @param c The component which the point should be expressed in terms of 471 */ 472 public static void convertPointFromScreen(Point p, Component c) 473 { 474 Point c0 = c.getLocationOnScreen(); 475 p.translate(-c0.x, -c0.y); 476 } 477 478 /** 479 * Converts a point <code>(x,y)</code> from the coordinate space of one 480 * component to another. This is equivalent to converting the point from 481 * <code>source</code> space to screen space, then back from screen space 482 * to <code>destination</code> space. If exactly one of the two 483 * Components is <code>null</code>, it is taken to refer to the root 484 * ancestor of the other component. If both are <code>null</code>, no 485 * transformation is done. 486 * 487 * @param source The component which the point is expressed in terms of 488 * @param x Horizontal coordinate of point to transform 489 * @param y Vertical coordinate of point to transform 490 * @param destination The component which the return value will be 491 * expressed in terms of 492 * 493 * @return The point <code>(x,y)</code> converted from the coordinate space of the 494 * source component to the coordinate space of the destination component 495 * 496 * @see #convertPointToScreen 497 * @see #convertPointFromScreen 498 * @see #convertRectangle 499 * @see #getRoot 500 */ 501 public static Point convertPoint(Component source, int x, int y, 502 Component destination) 503 { 504 Point pt = new Point(x, y); 505 506 if (source == null && destination == null) 507 return pt; 508 509 if (source == null) 510 source = getRoot(destination); 511 512 if (destination == null) 513 destination = getRoot(source); 514 515 if (source.isShowing() && destination.isShowing()) 516 { 517 convertPointToScreen(pt, source); 518 convertPointFromScreen(pt, destination); 519 } 520 521 return pt; 522 } 523 524 public static Point convertPoint(Component source, Point aPoint, Component destination) 525 { 526 return convertPoint(source, aPoint.x, aPoint.y, destination); 527 } 528 529 /** 530 * Converts a rectangle from the coordinate space of one component to 531 * another. This is equivalent to converting the rectangle from 532 * <code>source</code> space to screen space, then back from screen space 533 * to <code>destination</code> space. If exactly one of the two 534 * Components is <code>null</code>, it is taken to refer to the root 535 * ancestor of the other component. If both are <code>null</code>, no 536 * transformation is done. 537 * 538 * @param source The component which the rectangle is expressed in terms of 539 * @param rect The rectangle to convert 540 * @param destination The component which the return value will be 541 * expressed in terms of 542 * 543 * @return A new rectangle, equal in size to the input rectangle, but 544 * with its position converted from the coordinate space of the source 545 * component to the coordinate space of the destination component 546 * 547 * @see #convertPointToScreen 548 * @see #convertPointFromScreen 549 * @see #convertPoint(Component, int, int, Component) 550 * @see #getRoot 551 */ 552 public static Rectangle convertRectangle(Component source, 553 Rectangle rect, 554 Component destination) 555 { 556 Point pt = convertPoint(source, rect.x, rect.y, destination); 557 return new Rectangle(pt.x, pt.y, rect.width, rect.height); 558 } 559 560 /** 561 * Convert a mouse event which refrers to one component to another. This 562 * includes changing the mouse event's coordinate space, as well as the 563 * source property of the event. If <code>source</code> is 564 * <code>null</code>, it is taken to refer to <code>destination</code>'s 565 * root component. If <code>destination</code> is <code>null</code>, the 566 * new event will remain expressed in <code>source</code>'s coordinate 567 * system. 568 * 569 * @param source The component the mouse event currently refers to 570 * @param sourceEvent The mouse event to convert 571 * @param destination The component the new mouse event should refer to 572 * 573 * @return A new mouse event expressed in terms of the destination 574 * component's coordinate space, and with the destination component as 575 * its source 576 * 577 * @see #convertPoint(Component, int, int, Component) 578 */ 579 public static MouseEvent convertMouseEvent(Component source, 580 MouseEvent sourceEvent, 581 Component destination) 582 { 583 Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(), 584 destination); 585 586 return new MouseEvent(destination, sourceEvent.getID(), 587 sourceEvent.getWhen(), sourceEvent.getModifiersEx(), 588 newpt.x, newpt.y, sourceEvent.getClickCount(), 589 sourceEvent.isPopupTrigger(), sourceEvent.getButton()); 590 } 591 592 /** 593 * Recursively walk the component tree under <code>comp</code> calling 594 * <code>updateUI</code> on each {@link JComponent} found. This causes 595 * the entire tree to re-initialize its UI delegates. 596 * 597 * @param comp The component to walk the children of, calling <code>updateUI</code> 598 */ 599 public static void updateComponentTreeUI(Component comp) 600 { 601 updateComponentTreeUIImpl(comp); 602 if (comp instanceof JComponent) 603 { 604 JComponent jc = (JComponent) comp; 605 jc.revalidate(); 606 } 607 else 608 { 609 comp.invalidate(); 610 comp.validate(); 611 } 612 comp.repaint(); 613 } 614 615 /** 616 * Performs the actual work for {@link #updateComponentTreeUI(Component)}. 617 * This calls updateUI() on c if it is a JComponent, and then walks down 618 * the component tree and calls this method on each child component. 619 * 620 * @param c the component to update the UI 621 */ 622 private static void updateComponentTreeUIImpl(Component c) 623 { 624 if (c instanceof JComponent) 625 { 626 JComponent jc = (JComponent) c; 627 jc.updateUI(); 628 } 629 630 Component[] components = null; 631 if (c instanceof JMenu) 632 components = ((JMenu) c).getMenuComponents(); 633 else if (c instanceof Container) 634 components = ((Container) c).getComponents(); 635 if (components != null) 636 { 637 for (int i = 0; i < components.length; ++i) 638 updateComponentTreeUIImpl(components[i]); 639 } 640 } 641 642 /** 643 * <p>Layout a "compound label" consisting of a text string and an icon 644 * which is to be placed near the rendered text. Once the text and icon 645 * are laid out, the text rectangle and icon rectangle parameters are 646 * altered to store the calculated positions.</p> 647 * 648 * <p>The size of the text is calculated from the provided font metrics 649 * object. This object should be the metrics of the font you intend to 650 * paint the label with.</p> 651 * 652 * <p>The position values control where the text is placed relative to 653 * the icon. The horizontal position value should be one of the constants 654 * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>, 655 * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value 656 * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code> 657 * or <code>CENTER</code>.</p> 658 * 659 * <p>The text-icon gap value controls the number of pixels between the 660 * icon and the text.</p> 661 * 662 * <p>The alignment values control where the text and icon are placed, as 663 * a combined unit, within the view rectangle. The horizontal alignment 664 * value should be one of the constants <code>LEADING</code>, 665 * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or 666 * <code>CENTER</code>. The vertical alignment valus should be one of the 667 * constants <code>TOP</code>, <code>BOTTOM</code> or 668 * <code>CENTER</code>.</p> 669 * 670 * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are 671 * given for horizontal alignment or horizontal text position, they are 672 * interpreted relative to the provided component's orientation property, 673 * a constant in the {@link java.awt.ComponentOrientation} class. For 674 * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>, 675 * then the <code>LEADING</code> value is a synonym for <code>LEFT</code> 676 * and the <code>TRAILING</code> value is a synonym for 677 * <code>RIGHT</code></p> 678 * 679 * <p>If the text and icon are equal to or larger than the view 680 * rectangle, the horizontal and vertical alignment values have no 681 * affect.</p> 682 * 683 * @param c A component used for its orientation value 684 * @param fm The font metrics used to measure the text 685 * @param text The text to place in the compound label 686 * @param icon The icon to place next to the text 687 * @param verticalAlignment The vertical alignment of the label relative 688 * to its component 689 * @param horizontalAlignment The horizontal alignment of the label 690 * relative to its component 691 * @param verticalTextPosition The vertical position of the label's text 692 * relative to its icon 693 * @param horizontalTextPosition The horizontal position of the label's 694 * text relative to its icon 695 * @param viewR The view rectangle, specifying the area which layout is 696 * constrained to 697 * @param iconR A rectangle which is modified to hold the laid-out 698 * position of the icon 699 * @param textR A rectangle which is modified to hold the laid-out 700 * position of the text 701 * @param textIconGap The distance between text and icon 702 * 703 * @return The string of characters, possibly truncated with an elipsis, 704 * which is laid out in this label 705 */ 706 707 public static String layoutCompoundLabel(JComponent c, 708 FontMetrics fm, 709 String text, 710 Icon icon, 711 int verticalAlignment, 712 int horizontalAlignment, 713 int verticalTextPosition, 714 int horizontalTextPosition, 715 Rectangle viewR, 716 Rectangle iconR, 717 Rectangle textR, 718 int textIconGap) 719 { 720 721 // Fix up the orientation-based horizontal positions. 722 723 boolean ltr = true; 724 if (c != null && ! c.getComponentOrientation().isLeftToRight()) 725 ltr = false; 726 727 switch (horizontalTextPosition) 728 { 729 case LEADING: 730 horizontalTextPosition = ltr ? LEFT : RIGHT; 731 break; 732 case TRAILING: 733 horizontalTextPosition = ltr ? RIGHT : LEFT; 734 break; 735 default: 736 // Nothing to do in the other cases. 737 } 738 739 // Fix up the orientation-based alignments. 740 switch (horizontalAlignment) 741 { 742 case LEADING: 743 horizontalAlignment = ltr ? LEFT : RIGHT; 744 break; 745 case TRAILING: 746 horizontalAlignment = ltr ? RIGHT : LEFT; 747 break; 748 default: 749 // Nothing to do in the other cases. 750 } 751 752 return layoutCompoundLabelImpl(c, fm, text, icon, 753 verticalAlignment, 754 horizontalAlignment, 755 verticalTextPosition, 756 horizontalTextPosition, 757 viewR, iconR, textR, textIconGap); 758 } 759 760 /** 761 * <p>Layout a "compound label" consisting of a text string and an icon 762 * which is to be placed near the rendered text. Once the text and icon 763 * are laid out, the text rectangle and icon rectangle parameters are 764 * altered to store the calculated positions.</p> 765 * 766 * <p>The size of the text is calculated from the provided font metrics 767 * object. This object should be the metrics of the font you intend to 768 * paint the label with.</p> 769 * 770 * <p>The position values control where the text is placed relative to 771 * the icon. The horizontal position value should be one of the constants 772 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The 773 * vertical position value should be one fo the constants 774 * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p> 775 * 776 * <p>The text-icon gap value controls the number of pixels between the 777 * icon and the text.</p> 778 * 779 * <p>The alignment values control where the text and icon are placed, as 780 * a combined unit, within the view rectangle. The horizontal alignment 781 * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or 782 * <code>CENTER</code>. The vertical alignment valus should be one of the 783 * constants <code>TOP</code>, <code>BOTTOM</code> or 784 * <code>CENTER</code>.</p> 785 * 786 * <p>If the text and icon are equal to or larger than the view 787 * rectangle, the horizontal and vertical alignment values have no 788 * affect.</p> 789 * 790 * <p>Note that this method does <em>not</em> know how to deal with 791 * horizontal alignments or positions given as <code>LEADING</code> or 792 * <code>TRAILING</code> values. Use the other overloaded variant of this 793 * method if you wish to use such values. 794 * 795 * @param fm The font metrics used to measure the text 796 * @param text The text to place in the compound label 797 * @param icon The icon to place next to the text 798 * @param verticalAlignment The vertical alignment of the label relative 799 * to its component 800 * @param horizontalAlignment The horizontal alignment of the label 801 * relative to its component 802 * @param verticalTextPosition The vertical position of the label's text 803 * relative to its icon 804 * @param horizontalTextPosition The horizontal position of the label's 805 * text relative to its icon 806 * @param viewR The view rectangle, specifying the area which layout is 807 * constrained to 808 * @param iconR A rectangle which is modified to hold the laid-out 809 * position of the icon 810 * @param textR A rectangle which is modified to hold the laid-out 811 * position of the text 812 * @param textIconGap The distance between text and icon 813 * 814 * @return The string of characters, possibly truncated with an elipsis, 815 * which is laid out in this label 816 */ 817 818 public static String layoutCompoundLabel(FontMetrics fm, 819 String text, 820 Icon icon, 821 int verticalAlignment, 822 int horizontalAlignment, 823 int verticalTextPosition, 824 int horizontalTextPosition, 825 Rectangle viewR, 826 Rectangle iconR, 827 Rectangle textR, 828 int textIconGap) 829 { 830 return layoutCompoundLabelImpl(null, fm, text, icon, verticalAlignment, 831 horizontalAlignment, verticalTextPosition, 832 horizontalTextPosition, viewR, iconR, textR, 833 textIconGap); 834 } 835 836 /** 837 * <p>Layout a "compound label" consisting of a text string and an icon 838 * which is to be placed near the rendered text. Once the text and icon 839 * are laid out, the text rectangle and icon rectangle parameters are 840 * altered to store the calculated positions.</p> 841 * 842 * <p>The size of the text is calculated from the provided font metrics 843 * object. This object should be the metrics of the font you intend to 844 * paint the label with.</p> 845 * 846 * <p>The position values control where the text is placed relative to 847 * the icon. The horizontal position value should be one of the constants 848 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The 849 * vertical position value should be one fo the constants 850 * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p> 851 * 852 * <p>The text-icon gap value controls the number of pixels between the 853 * icon and the text.</p> 854 * 855 * <p>The alignment values control where the text and icon are placed, as 856 * a combined unit, within the view rectangle. The horizontal alignment 857 * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or 858 * <code>CENTER</code>. The vertical alignment valus should be one of the 859 * constants <code>TOP</code>, <code>BOTTOM</code> or 860 * <code>CENTER</code>.</p> 861 * 862 * <p>If the text and icon are equal to or larger than the view 863 * rectangle, the horizontal and vertical alignment values have no 864 * affect.</p> 865 * 866 * <p>Note that this method does <em>not</em> know how to deal with 867 * horizontal alignments or positions given as <code>LEADING</code> or 868 * <code>TRAILING</code> values. Use the other overloaded variant of this 869 * method if you wish to use such values. 870 * 871 * @param fm The font metrics used to measure the text 872 * @param text The text to place in the compound label 873 * @param icon The icon to place next to the text 874 * @param verticalAlignment The vertical alignment of the label relative 875 * to its component 876 * @param horizontalAlignment The horizontal alignment of the label 877 * relative to its component 878 * @param verticalTextPosition The vertical position of the label's text 879 * relative to its icon 880 * @param horizontalTextPosition The horizontal position of the label's 881 * text relative to its icon 882 * @param viewR The view rectangle, specifying the area which layout is 883 * constrained to 884 * @param iconR A rectangle which is modified to hold the laid-out 885 * position of the icon 886 * @param textR A rectangle which is modified to hold the laid-out 887 * position of the text 888 * @param textIconGap The distance between text and icon 889 * 890 * @return The string of characters, possibly truncated with an elipsis, 891 * which is laid out in this label 892 */ 893 private static String layoutCompoundLabelImpl(JComponent c, 894 FontMetrics fm, 895 String text, 896 Icon icon, 897 int verticalAlignment, 898 int horizontalAlignment, 899 int verticalTextPosition, 900 int horizontalTextPosition, 901 Rectangle viewR, 902 Rectangle iconR, 903 Rectangle textR, 904 int textIconGap) 905 { 906 907 // Work out basic height and width. 908 909 if (icon == null) 910 { 911 textIconGap = 0; 912 iconR.width = 0; 913 iconR.height = 0; 914 } 915 else 916 { 917 iconR.width = icon.getIconWidth(); 918 iconR.height = icon.getIconHeight(); 919 } 920 921 if (text == null || text.equals("")) 922 { 923 textIconGap = 0; 924 textR.width = 0; 925 textR.height = 0; 926 text = ""; 927 } 928 else 929 { 930 int availableWidth = viewR.width; 931 if (horizontalTextPosition != CENTER) 932 availableWidth -= iconR.width + textIconGap; 933 View html = c == null ? null 934 : (View) c.getClientProperty(BasicHTML.propertyKey); 935 if (html != null) 936 { 937 textR.width = (int) html.getPreferredSpan(View.X_AXIS); 938 textR.width = Math.min(availableWidth, textR.width); 939 textR.height = (int) html.getPreferredSpan(View.Y_AXIS); 940 } 941 else 942 { 943 int fromIndex = 0; 944 textR.width = fm.stringWidth(text); 945 textR.height = fm.getHeight(); 946 if (textR.width > availableWidth) 947 { 948 text = clipString(c, fm, text, availableWidth); 949 textR.width = fm.stringWidth(text); 950 } 951 } 952 } 953 954 // Work out the position of text, assuming the top-left coord 955 // starts at (0,0). We will fix that up momentarily, after these 956 // "position" decisions are made and we look at alignment. 957 958 switch (verticalTextPosition) 959 { 960 case TOP: 961 textR.y = horizontalTextPosition == CENTER ? 962 - textR.height - textIconGap : 0; 963 break; 964 case BOTTOM: 965 textR.y = horizontalTextPosition == CENTER ? 966 iconR.height + textIconGap : iconR.height - textR.height; 967 break; 968 case CENTER: 969 textR.y = iconR.height / 2 - textR.height / 2; 970 break; 971 } 972 973 switch (horizontalTextPosition) 974 { 975 case LEFT: 976 textR.x = -(textR.width + textIconGap); 977 break; 978 case RIGHT: 979 textR.x = iconR.width + textIconGap; 980 break; 981 case CENTER: 982 textR.x = iconR.width / 2 - textR.width / 2; 983 break; 984 } 985 986 // The two rectangles are laid out correctly now, but only assuming 987 // that their upper left corner is at (0,0). If we have any alignment other 988 // than TOP and LEFT, we need to adjust them. 989 990 // These coordinates specify the rectangle that contains both the 991 // icon and text. Move it so that it fullfills the alignment properties. 992 int lx = Math.min(iconR.x, textR.x); 993 int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx; 994 int ly = Math.min(iconR.y, textR.y); 995 int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly; 996 int horizontalAdjustment = 0; 997 int verticalAdjustment = 0; 998 switch (verticalAlignment) 999 { 1000 case TOP: 1001 verticalAdjustment = viewR.y - ly; 1002 break; 1003 case BOTTOM: 1004 verticalAdjustment = viewR.y + viewR.height - ly - lh; 1005 break; 1006 case CENTER: 1007 verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2; 1008 break; 1009 } 1010 switch (horizontalAlignment) 1011 { 1012 case LEFT: 1013 horizontalAdjustment = viewR.x - lx; 1014 break; 1015 case RIGHT: 1016 horizontalAdjustment = viewR.x + viewR.width - lx - lw; 1017 break; 1018 case CENTER: 1019 horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2)); 1020 break; 1021 } 1022 iconR.x += horizontalAdjustment; 1023 iconR.y += verticalAdjustment; 1024 1025 textR.x += horizontalAdjustment; 1026 textR.y += verticalAdjustment; 1027 1028 return text; 1029 } 1030 1031 /** 1032 * The method clips the specified string so that it fits into the 1033 * available width. It is only called when the text really doesn't fit, 1034 * so we don't need to check that again. 1035 * 1036 * @param c the component 1037 * @param fm the font metrics 1038 * @param text the text 1039 * @param availableWidth the available width 1040 * 1041 * @return the clipped string 1042 */ 1043 private static String clipString(JComponent c, FontMetrics fm, String text, 1044 int availableWidth) 1045 { 1046 String dots = "..."; 1047 int dotsWidth = fm.stringWidth(dots); 1048 char[] string = text.toCharArray(); 1049 int endIndex = string.length; 1050 while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth 1051 && endIndex > 0) 1052 endIndex--; 1053 String clipped; 1054 if (string.length >= endIndex + 3) 1055 { 1056 string[endIndex] = '.'; 1057 string[endIndex + 1] = '.'; 1058 string[endIndex + 2] = '.'; 1059 clipped = new String(string, 0, endIndex + 3); 1060 } 1061 else 1062 { 1063 char[] clippedChars = new char[string.length + 3]; 1064 System.arraycopy(string, 0, clippedChars, 0, string.length); 1065 clippedChars[endIndex] = '.'; 1066 clippedChars[endIndex + 1] = '.'; 1067 clippedChars[endIndex + 2] = '.'; 1068 clipped = new String(clippedChars, 0, endIndex + 3); 1069 } 1070 return clipped; 1071 } 1072 1073 /** 1074 * Calls {@link java.awt.EventQueue#invokeLater} with the 1075 * specified {@link Runnable}. 1076 */ 1077 public static void invokeLater(Runnable doRun) 1078 { 1079 java.awt.EventQueue.invokeLater(doRun); 1080 } 1081 1082 /** 1083 * Calls {@link java.awt.EventQueue#invokeAndWait} with the 1084 * specified {@link Runnable}. 1085 */ 1086 public static void invokeAndWait(Runnable doRun) 1087 throws InterruptedException, 1088 InvocationTargetException 1089 { 1090 java.awt.EventQueue.invokeAndWait(doRun); 1091 } 1092 1093 /** 1094 * Calls {@link java.awt.EventQueue#isDispatchThread()}. 1095 * 1096 * @return <code>true</code> if the current thread is the current AWT event 1097 * dispatch thread. 1098 */ 1099 public static boolean isEventDispatchThread() 1100 { 1101 return java.awt.EventQueue.isDispatchThread(); 1102 } 1103 1104 /** 1105 * This method paints the given component at the given position and size. 1106 * The component will be reparented to the container given. 1107 * 1108 * @param g The Graphics object to draw with. 1109 * @param c The Component to draw 1110 * @param p The Container to reparent to. 1111 * @param x The x coordinate to draw at. 1112 * @param y The y coordinate to draw at. 1113 * @param w The width of the drawing area. 1114 * @param h The height of the drawing area. 1115 */ 1116 public static void paintComponent(Graphics g, Component c, Container p, 1117 int x, int y, int w, int h) 1118 { 1119 Container parent = c.getParent(); 1120 if (parent != null) 1121 parent.remove(c); 1122 if (p != null) 1123 p.add(c); 1124 1125 Shape savedClip = g.getClip(); 1126 1127 g.setClip(x, y, w, h); 1128 g.translate(x, y); 1129 1130 c.paint(g); 1131 1132 g.translate(-x, -y); 1133 g.setClip(savedClip); 1134 } 1135 1136 /** 1137 * This method paints the given component in the given rectangle. 1138 * The component will be reparented to the container given. 1139 * 1140 * @param g The Graphics object to draw with. 1141 * @param c The Component to draw 1142 * @param p The Container to reparent to. 1143 * @param r The rectangle that describes the drawing area. 1144 */ 1145 public static void paintComponent(Graphics g, Component c, 1146 Container p, Rectangle r) 1147 { 1148 paintComponent(g, c, p, r.x, r.y, r.width, r.height); 1149 } 1150 1151 /** 1152 * This method returns the common Frame owner used in JDialogs or 1153 * JWindow when no owner is provided. 1154 * 1155 * @return The common Frame 1156 */ 1157 static Window getOwnerFrame(Window owner) 1158 { 1159 Window result = owner; 1160 if (result == null) 1161 { 1162 if (ownerFrame == null) 1163 ownerFrame = new OwnerFrame(); 1164 result = ownerFrame; 1165 } 1166 return result; 1167 } 1168 1169 /** 1170 * Checks if left mouse button was clicked. 1171 * 1172 * @param event the event to check 1173 * 1174 * @return true if left mouse was clicked, false otherwise. 1175 */ 1176 public static boolean isLeftMouseButton(MouseEvent event) 1177 { 1178 return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0); 1179 } 1180 1181 /** 1182 * Checks if middle mouse button was clicked. 1183 * 1184 * @param event the event to check 1185 * 1186 * @return true if middle mouse was clicked, false otherwise. 1187 */ 1188 public static boolean isMiddleMouseButton(MouseEvent event) 1189 { 1190 return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) 1191 == InputEvent.BUTTON2_DOWN_MASK); 1192 } 1193 1194 /** 1195 * Checks if right mouse button was clicked. 1196 * 1197 * @param event the event to check 1198 * 1199 * @return true if right mouse was clicked, false otherwise. 1200 */ 1201 public static boolean isRightMouseButton(MouseEvent event) 1202 { 1203 return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) 1204 == InputEvent.BUTTON3_DOWN_MASK); 1205 } 1206 1207 /** 1208 * This frame should be used when constructing a Window/JDialog without 1209 * a parent. In this case, we are forced to use this frame as a window's 1210 * parent, because we simply cannot pass null instead of parent to Window 1211 * constructor, since doing it will result in NullPointerException. 1212 */ 1213 private static class OwnerFrame extends Frame 1214 { 1215 public void setVisible(boolean b) 1216 { 1217 // Do nothing here. 1218 } 1219 1220 public boolean isShowing() 1221 { 1222 return true; 1223 } 1224 } 1225 1226 public static boolean notifyAction(Action action, 1227 KeyStroke ks, 1228 KeyEvent event, 1229 Object sender, 1230 int modifiers) 1231 { 1232 if (action != null && action.isEnabled()) 1233 { 1234 String name = (String) action.getValue(Action.ACTION_COMMAND_KEY); 1235 if (name == null 1236 && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) 1237 name = new String(new char[] {event.getKeyChar()}); 1238 action.actionPerformed(new ActionEvent(sender, 1239 ActionEvent.ACTION_PERFORMED, 1240 name, modifiers)); 1241 return true; 1242 } 1243 return false; 1244 } 1245 1246 /** 1247 * <p>Change the shared, UI-managed {@link ActionMap} for a given 1248 * component. ActionMaps are arranged in a hierarchy, in order to 1249 * encourage sharing of common actions between components. The hierarchy 1250 * unfortunately places UI-managed ActionMaps at the <em>end</em> of the 1251 * parent-pointer chain, as illustrated:</p> 1252 * 1253 * <pre> 1254 * [{@link javax.swing.JComponent#getActionMap()}] 1255 * --> [{@link javax.swing.ActionMap}] 1256 * parent --> [{@link javax.swing.text.JTextComponent.KeymapActionMap}] 1257 * parent --> [{@link javax.swing.plaf.ActionMapUIResource}] 1258 * </pre> 1259 * 1260 * <p>Our goal with this method is to replace the first ActionMap along 1261 * this chain which is an instance of {@link ActionMapUIResource}, since 1262 * these are the ActionMaps which are supposed to be shared between 1263 * components.</p> 1264 * 1265 * <p>If the provided ActionMap is <code>null</code>, we interpret the 1266 * call as a request to remove the UI-managed ActionMap from the 1267 * component's ActionMap parent chain.</p> 1268 */ 1269 public static void replaceUIActionMap(JComponent component, 1270 ActionMap uiActionMap) 1271 { 1272 ActionMap child = component.getActionMap(); 1273 if (child == null) 1274 component.setActionMap(uiActionMap); 1275 else 1276 { 1277 ActionMap parent = child.getParent(); 1278 while (parent != null && !(parent instanceof ActionMapUIResource)) 1279 { 1280 child = parent; 1281 parent = child.getParent(); 1282 } 1283 // Sanity check to avoid loops. 1284 if (child != uiActionMap) 1285 child.setParent(uiActionMap); 1286 } 1287 } 1288 1289 /** 1290 * <p>Change the shared, UI-managed {@link InputMap} for a given 1291 * component. InputMaps are arranged in a hierarchy, in order to 1292 * encourage sharing of common input mappings between components. The 1293 * hierarchy unfortunately places UI-managed InputMaps at the 1294 * <em>end</em> of the parent-pointer chain, as illustrated:</p> 1295 * 1296 * <pre> 1297 * [{@link javax.swing.JComponent#getInputMap()}] 1298 * --> [{@link javax.swing.InputMap}] 1299 * parent --> [{@link javax.swing.text.JTextComponent.KeymapWrapper}] 1300 * parent --> [{@link javax.swing.plaf.InputMapUIResource}] 1301 * </pre> 1302 * 1303 * <p>Our goal with this method is to replace the first InputMap along 1304 * this chain which is an instance of {@link InputMapUIResource}, since 1305 * these are the InputMaps which are supposed to be shared between 1306 * components.</p> 1307 * 1308 * <p>If the provided InputMap is <code>null</code>, we interpret the 1309 * call as a request to remove the UI-managed InputMap from the 1310 * component's InputMap parent chain.</p> 1311 */ 1312 public static void replaceUIInputMap(JComponent component, 1313 int condition, 1314 InputMap uiInputMap) 1315 { 1316 InputMap child = component.getInputMap(condition); 1317 if (child == null) 1318 component.setInputMap(condition, uiInputMap); 1319 else 1320 { 1321 InputMap parent = child.getParent(); 1322 while (parent != null && !(parent instanceof InputMapUIResource)) 1323 { 1324 child = parent; 1325 parent = parent.getParent(); 1326 } 1327 // Sanity check to avoid loops. 1328 if (child != uiInputMap) 1329 child.setParent(uiInputMap); 1330 } 1331 } 1332 1333 /** 1334 * Subtracts a rectangle from another and return the area as an array 1335 * of rectangles. 1336 * Returns the areas of rectA which are not covered by rectB. 1337 * If the rectangles do not overlap, or if either parameter is 1338 * <code>null</code>, a zero-size array is returned. 1339 * @param rectA The first rectangle 1340 * @param rectB The rectangle to subtract from the first 1341 * @return An array of rectangles representing the area in rectA 1342 * not overlapped by rectB 1343 */ 1344 public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB) 1345 { 1346 if (rectA == null || rectB == null) 1347 return new Rectangle[0]; 1348 1349 Rectangle[] r = new Rectangle[4]; 1350 int x1 = rectA.x; 1351 int y1 = rectA.y; 1352 int w1 = rectA.width; 1353 int h1 = rectA.height; 1354 int x2 = rectB.x; 1355 int y2 = rectB.y; 1356 int w2 = rectB.width; 1357 int h2 = rectB.height; 1358 1359 // (outer box = rectA) 1360 // ------------- 1361 // |_____0_____| 1362 // | |rectB| | 1363 // |_1|_____|_2| 1364 // | 3 | 1365 // ------------- 1366 int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0 1367 int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3 1368 int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1 1369 int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2 1370 int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2 1371 1372 if (H0 > 0) 1373 r[0] = new Rectangle(x1, y1, w1, H0); 1374 else 1375 r[0] = null; 1376 1377 if (W1 > 0 && H12 > 0) 1378 r[1] = new Rectangle(x1, y1 + H0, W1, H12); 1379 else 1380 r[1] = null; 1381 1382 if (W2 > 0 && H12 > 0) 1383 r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12); 1384 else 1385 r[2] = null; 1386 1387 if (H3 > 0) 1388 r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3); 1389 else 1390 r[3] = null; 1391 1392 // sort out null objects 1393 int n = 0; 1394 for (int i = 0; i < 4; i++) 1395 if (r[i] != null) 1396 n++; 1397 Rectangle[] out = new Rectangle[n]; 1398 for (int i = 3; i >= 0; i--) 1399 if (r[i] != null) 1400 out[--n] = r[i]; 1401 1402 return out; 1403 } 1404 1405 /** 1406 * Calculates the intersection of two rectangles. The result is stored 1407 * in <code>rect</code>. This is basically the same 1408 * like {@link Rectangle#intersection(Rectangle)}, only that it does not 1409 * create new Rectangle instances. The tradeoff is that you loose any data in 1410 * <code>rect</code>. 1411 * 1412 * @param x upper-left x coodinate of first rectangle 1413 * @param y upper-left y coodinate of first rectangle 1414 * @param w width of first rectangle 1415 * @param h height of first rectangle 1416 * @param rect a Rectangle object of the second rectangle 1417 * 1418 * @throws NullPointerException if rect is null 1419 * 1420 * @return a rectangle corresponding to the intersection of the 1421 * two rectangles. An empty rectangle is returned if the rectangles 1422 * do not overlap 1423 */ 1424 public static Rectangle computeIntersection(int x, int y, int w, int h, 1425 Rectangle rect) 1426 { 1427 int x2 = (int) rect.x; 1428 int y2 = (int) rect.y; 1429 int w2 = (int) rect.width; 1430 int h2 = (int) rect.height; 1431 1432 int dx = (x > x2) ? x : x2; 1433 int dy = (y > y2) ? y : y2; 1434 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx); 1435 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); 1436 1437 if (dw >= 0 && dh >= 0) 1438 rect.setBounds(dx, dy, dw, dh); 1439 else 1440 rect.setBounds(0, 0, 0, 0); 1441 1442 return rect; 1443 } 1444 1445 /** 1446 * Calculates the width of a given string. 1447 * 1448 * @param fm the <code>FontMetrics</code> object to use 1449 * @param str the string 1450 * 1451 * @return the width of the the string. 1452 */ 1453 public static int computeStringWidth(FontMetrics fm, String str) 1454 { 1455 return fm.stringWidth(str); 1456 } 1457 1458 /** 1459 * Calculates the union of two rectangles. The result is stored in 1460 * <code>rect</code>. This is basically the same as 1461 * {@link Rectangle#union(Rectangle)} except that it avoids creation of new 1462 * Rectangle objects. The tradeoff is that you loose any data in 1463 * <code>rect</code>. 1464 * 1465 * @param x upper-left x coodinate of first rectangle 1466 * @param y upper-left y coodinate of first rectangle 1467 * @param w width of first rectangle 1468 * @param h height of first rectangle 1469 * @param rect a Rectangle object of the second rectangle 1470 * 1471 * @throws NullPointerException if rect is null 1472 * 1473 * @return a rectangle corresponding to the union of the 1474 * two rectangles; a rectangle encompassing both is returned if the 1475 * rectangles do not overlap 1476 */ 1477 public static Rectangle computeUnion(int x, int y, int w, int h, 1478 Rectangle rect) 1479 { 1480 int x2 = (int) rect.x; 1481 int y2 = (int) rect.y; 1482 int w2 = (int) rect.width; 1483 int h2 = (int) rect.height; 1484 1485 int dx = (x < x2) ? x : x2; 1486 int dy = (y < y2) ? y : y2; 1487 int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx); 1488 int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); 1489 1490 if (dw >= 0 && dh >= 0) 1491 rect.setBounds(dx, dy, dw, dh); 1492 else 1493 rect.setBounds(0, 0, 0, 0); 1494 return rect; 1495 } 1496 1497 /** 1498 * Tests if a rectangle contains another. 1499 * @param a first rectangle 1500 * @param b second rectangle 1501 * @return true if a contains b, false otherwise 1502 * @throws NullPointerException 1503 */ 1504 public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b) 1505 { 1506 // Note: zero-size rects inclusive, differs from Rectangle.contains() 1507 return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0 1508 && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y 1509 && b.y + b.height <= a.y + a.height; 1510 } 1511 1512 /** 1513 * Returns the InputMap that is provided by the ComponentUI of 1514 * <code>component</code> for the specified condition. 1515 * 1516 * @param component the component for which the InputMap is returned 1517 * @param cond the condition that specifies which of the three input 1518 * maps should be returned, may be 1519 * {@link JComponent#WHEN_IN_FOCUSED_WINDOW}, 1520 * {@link JComponent#WHEN_FOCUSED} or 1521 * {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} 1522 * 1523 * @return The input map. 1524 */ 1525 public static InputMap getUIInputMap(JComponent component, int cond) 1526 { 1527 if (UIManager.getUI(component) != null) 1528 // we assume here that the UI class sets the parent of the component's 1529 // InputMap, which is the correct behaviour. If it's not, then 1530 // this can be considered a bug 1531 return component.getInputMap(cond).getParent(); 1532 else 1533 return null; 1534 } 1535 1536 /** 1537 * Returns the ActionMap that is provided by the ComponentUI of 1538 * <code>component</code>. 1539 * 1540 * @param component the component for which the ActionMap is returned 1541 */ 1542 public static ActionMap getUIActionMap(JComponent component) 1543 { 1544 if (UIManager.getUI(component) != null) 1545 // we assume here that the UI class sets the parent of the component's 1546 // ActionMap, which is the correct behaviour. If it's not, then 1547 // this can be considered a bug 1548 return component.getActionMap().getParent(); 1549 else 1550 return null; 1551 } 1552 1553 /** 1554 * Processes key bindings for the component that is associated with the 1555 * key event. Note that this method does not make sense for 1556 * JComponent-derived components, except when 1557 * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is 1558 * not called. 1559 * 1560 * This method searches through the component hierarchy of the component's 1561 * top-level container to find a <code>JComponent</code> that has a binding 1562 * for the key event in the WHEN_IN_FOCUSED_WINDOW scope. 1563 * 1564 * @param ev the key event 1565 * 1566 * @return <code>true</code> if a binding has been found and processed, 1567 * <code>false</code> otherwise 1568 * 1569 * @since 1.4 1570 */ 1571 public static boolean processKeyBindings(KeyEvent ev) 1572 { 1573 Component c = ev.getComponent(); 1574 KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev); 1575 KeyboardManager km = KeyboardManager.getManager(); 1576 return km.processKeyStroke(c, s, ev); 1577 } 1578 1579 /** 1580 * Returns a string representing one of the horizontal alignment codes 1581 * defined in the {@link SwingConstants} interface. The following table 1582 * lists the constants and return values: 1583 * <p> 1584 * <table border="0"> 1585 * <tr> 1586 * <th>Code:</th><th>Returned String:</th> 1587 * </tr> 1588 * <tr> 1589 * <td>{@link SwingConstants#CENTER}</td> 1590 * <td><code>"CENTER"</code></td> 1591 * </tr> 1592 * <tr> 1593 * <td>{@link SwingConstants#LEFT}</td> 1594 * <td><code>"LEFT"</code></td> 1595 * </tr> 1596 * <tr> 1597 * <td>{@link SwingConstants#RIGHT}</td> 1598 * <td><code>"RIGHT"</code></td> 1599 * </tr> 1600 * <tr> 1601 * <td>{@link SwingConstants#LEADING}</td> 1602 * <td><code>"LEADING"</code></td> 1603 * </tr> 1604 * <tr> 1605 * <td>{@link SwingConstants#TRAILING}</td> 1606 * <td><code>"TRAILING"</code></td> 1607 * </tr> 1608 * </table> 1609 * </p> 1610 * If the supplied code is not one of those listed, this methods will throw 1611 * an {@link IllegalArgumentException}. 1612 * 1613 * @param code the code. 1614 * 1615 * @return A string representing the given code. 1616 */ 1617 static String convertHorizontalAlignmentCodeToString(int code) 1618 { 1619 switch (code) 1620 { 1621 case SwingConstants.CENTER: 1622 return "CENTER"; 1623 case SwingConstants.LEFT: 1624 return "LEFT"; 1625 case SwingConstants.RIGHT: 1626 return "RIGHT"; 1627 case SwingConstants.LEADING: 1628 return "LEADING"; 1629 case SwingConstants.TRAILING: 1630 return "TRAILING"; 1631 default: 1632 throw new IllegalArgumentException("Unrecognised code: " + code); 1633 } 1634 } 1635 1636 /** 1637 * Returns a string representing one of the vertical alignment codes 1638 * defined in the {@link SwingConstants} interface. The following table 1639 * lists the constants and return values: 1640 * <p> 1641 * <table border="0"> 1642 * <tr> 1643 * <th>Code:</th><th>Returned String:</th> 1644 * </tr> 1645 * <tr> 1646 * <td>{@link SwingConstants#CENTER}</td> 1647 * <td><code>"CENTER"</code></td> 1648 * </tr> 1649 * <tr> 1650 * <td>{@link SwingConstants#TOP}</td> 1651 * <td><code>"TOP"</code></td> 1652 * </tr> 1653 * <tr> 1654 * <td>{@link SwingConstants#BOTTOM}</td> 1655 * <td><code>"BOTTOM"</code></td> 1656 * </tr> 1657 * </table> 1658 * </p> 1659 * If the supplied code is not one of those listed, this methods will throw 1660 * an {@link IllegalArgumentException}. 1661 * 1662 * @param code the code. 1663 * 1664 * @return A string representing the given code. 1665 */ 1666 static String convertVerticalAlignmentCodeToString(int code) 1667 { 1668 switch (code) 1669 { 1670 case SwingConstants.CENTER: 1671 return "CENTER"; 1672 case SwingConstants.TOP: 1673 return "TOP"; 1674 case SwingConstants.BOTTOM: 1675 return "BOTTOM"; 1676 default: 1677 throw new IllegalArgumentException("Unrecognised code: " + code); 1678 } 1679 } 1680 1681 /** 1682 * Returns a string representing one of the default operation codes 1683 * defined in the {@link WindowConstants} interface. The following table 1684 * lists the constants and return values: 1685 * <p> 1686 * <table border="0"> 1687 * <tr> 1688 * <th>Code:</th><th>Returned String:</th> 1689 * </tr> 1690 * <tr> 1691 * <td>{@link WindowConstants#DO_NOTHING_ON_CLOSE}</td> 1692 * <td><code>"DO_NOTHING_ON_CLOSE"</code></td> 1693 * </tr> 1694 * <tr> 1695 * <td>{@link WindowConstants#HIDE_ON_CLOSE}</td> 1696 * <td><code>"HIDE_ON_CLOSE"</code></td> 1697 * </tr> 1698 * <tr> 1699 * <td>{@link WindowConstants#DISPOSE_ON_CLOSE}</td> 1700 * <td><code>"DISPOSE_ON_CLOSE"</code></td> 1701 * </tr> 1702 * <tr> 1703 * <td>{@link WindowConstants#EXIT_ON_CLOSE}</td> 1704 * <td><code>"EXIT_ON_CLOSE"</code></td> 1705 * </tr> 1706 * </table> 1707 * </p> 1708 * If the supplied code is not one of those listed, this method will throw 1709 * an {@link IllegalArgumentException}. 1710 * 1711 * @param code the code. 1712 * 1713 * @return A string representing the given code. 1714 */ 1715 static String convertWindowConstantToString(int code) 1716 { 1717 switch (code) 1718 { 1719 case WindowConstants.DO_NOTHING_ON_CLOSE: 1720 return "DO_NOTHING_ON_CLOSE"; 1721 case WindowConstants.HIDE_ON_CLOSE: 1722 return "HIDE_ON_CLOSE"; 1723 case WindowConstants.DISPOSE_ON_CLOSE: 1724 return "DISPOSE_ON_CLOSE"; 1725 case WindowConstants.EXIT_ON_CLOSE: 1726 return "EXIT_ON_CLOSE"; 1727 default: 1728 throw new IllegalArgumentException("Unrecognised code: " + code); 1729 } 1730 } 1731 1732 /** 1733 * Converts a rectangle in the coordinate system of a child component into 1734 * a rectangle of one of it's Ancestors. The result is stored in the input 1735 * rectangle. 1736 * 1737 * @param comp the child component 1738 * @param r the rectangle to convert 1739 * @param ancestor the ancestor component 1740 */ 1741 static void convertRectangleToAncestor(Component comp, Rectangle r, 1742 Component ancestor) 1743 { 1744 if (comp == ancestor) 1745 return; 1746 1747 r.x += comp.getX(); 1748 r.y += comp.getY(); 1749 1750 Component parent = comp.getParent(); 1751 if (parent != null && parent != ancestor) 1752 convertRectangleToAncestor(parent, r, ancestor); 1753 } 1754}