Frames | No Frames |
1: /* SwingUtilities.java -- 2: Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.applet.Applet; 42: import java.awt.Component; 43: import java.awt.Container; 44: import java.awt.FontMetrics; 45: import java.awt.Frame; 46: import java.awt.Graphics; 47: import java.awt.Insets; 48: import java.awt.KeyboardFocusManager; 49: import java.awt.Point; 50: import java.awt.Rectangle; 51: import java.awt.Shape; 52: import java.awt.Window; 53: import java.awt.event.ActionEvent; 54: import java.awt.event.InputEvent; 55: import java.awt.event.KeyEvent; 56: import java.awt.event.MouseEvent; 57: import java.lang.reflect.InvocationTargetException; 58: 59: import javax.accessibility.Accessible; 60: import javax.accessibility.AccessibleStateSet; 61: import javax.swing.plaf.ActionMapUIResource; 62: import javax.swing.plaf.InputMapUIResource; 63: import javax.swing.plaf.basic.BasicHTML; 64: import javax.swing.text.View; 65: 66: /** 67: * A number of static utility functions which are 68: * useful when drawing swing components, dispatching events, or calculating 69: * regions which need painting. 70: * 71: * @author Graydon Hoare (graydon@redhat.com) 72: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 73: */ 74: public class SwingUtilities 75: implements SwingConstants 76: { 77: /** 78: * This frame should be used as parent for JWindow or JDialog 79: * that doesn't an owner 80: */ 81: private static OwnerFrame ownerFrame; 82: 83: private SwingUtilities() 84: { 85: // Do nothing. 86: } 87: 88: /** 89: * Calculates the portion of the component's bounds which is inside the 90: * component's border insets. This area is usually the area a component 91: * should confine its painting to. The coordinates are returned in terms 92: * of the <em>component's</em> coordinate system, where (0,0) is the 93: * upper left corner of the component's bounds. 94: * 95: * @param c the component to measure the bounds of (if <code>null</code>, 96: * this method returns <code>null</code>). 97: * @param r a carrier to store the return value in (if <code>null</code>, a 98: * new <code>Rectangle</code> instance is created). 99: * 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: }