Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / SwingUtilities.java
blob2d859b7448eb3c51b969e49f99a36669ba50243b
1 /* SwingUtilities.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
39 package javax.swing;
41 import java.applet.Applet;
42 import java.awt.Component;
43 import java.awt.ComponentOrientation;
44 import java.awt.Container;
45 import java.awt.FontMetrics;
46 import java.awt.Frame;
47 import java.awt.Graphics;
48 import java.awt.Insets;
49 import java.awt.KeyboardFocusManager;
50 import java.awt.Point;
51 import java.awt.Rectangle;
52 import java.awt.Shape;
53 import java.awt.Window;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.InputEvent;
56 import java.awt.event.KeyEvent;
57 import java.awt.event.MouseEvent;
58 import java.lang.reflect.InvocationTargetException;
60 import javax.accessibility.Accessible;
61 import javax.accessibility.AccessibleStateSet;
62 import javax.swing.plaf.ActionMapUIResource;
63 import javax.swing.plaf.InputMapUIResource;
65 /**
66 * A number of static utility functions which are
67 * useful when drawing swing components, dispatching events, or calculating
68 * regions which need painting.
70 * @author Graydon Hoare (graydon@redhat.com)
71 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
73 public class SwingUtilities
74 implements SwingConstants
76 /**
77 * This frame should be used as parent for JWindow or JDialog
78 * that doesn't an owner
80 private static OwnerFrame ownerFrame;
82 private SwingUtilities()
84 // Do nothing.
87 /**
88 * Calculates the portion of the base rectangle which is inside the
89 * insets.
91 * @param base The rectangle to apply the insets to
92 * @param insets The insets to apply to the base rectangle
93 * @param ret A rectangle to use for storing the return value, or
94 * <code>null</code>
96 * @return The calculated area inside the base rectangle and its insets,
97 * either stored in ret or a new Rectangle if ret is <code>null</code>
99 * @see #calculateInnerArea
101 public static Rectangle calculateInsetArea(Rectangle base, Insets insets,
102 Rectangle ret)
104 if (ret == null)
105 ret = new Rectangle();
106 ret.setBounds(base.x + insets.left, base.y + insets.top,
107 base.width - (insets.left + insets.right),
108 base.height - (insets.top + insets.bottom));
109 return ret;
113 * Calculates the portion of the component's bounds which is inside the
114 * component's border insets. This area is usually the area a component
115 * should confine its painting to. The coordinates are returned in terms
116 * of the <em>component's</em> coordinate system, where (0,0) is the
117 * upper left corner of the component's bounds.
119 * @param c The component to measure the bounds of
120 * @param r A Rectangle to store the return value in, or
121 * <code>null</code>
123 * @return The calculated area inside the component and its border
124 * insets
126 * @see #calculateInsetArea
128 public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
130 Rectangle b = getLocalBounds(c);
131 return calculateInsetArea(b, c.getInsets(), r);
135 * Returns the focus owner or <code>null</code> if <code>comp</code> is not
136 * the focus owner or a parent of it.
138 * @param comp the focus owner or a parent of it
140 * @return the focus owner, or <code>null</code>
142 * @deprecated 1.4 Replaced by
143 * <code>KeyboardFocusManager.getFocusOwner()</code>.
145 public static Component findFocusOwner(Component comp)
147 // Get real focus owner.
148 Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
149 .getFocusOwner();
151 // Check if comp is the focus owner or a parent of it.
152 Component tmp = focusOwner;
154 while (tmp != null)
156 if (tmp == comp)
157 return focusOwner;
159 tmp = tmp.getParent();
162 return null;
166 * Returns the <code>Accessible</code> child of the specified component
167 * which appears at the supplied <code>Point</code>. If there is no
168 * child located at that particular pair of co-ordinates, null is returned
169 * instead.
171 * @param c the component whose children may be found at the specified
172 * point.
173 * @param p the point at which to look for the existence of children
174 * of the specified component.
175 * @return the <code>Accessible</code> child at the point, <code>p</code>,
176 * or null if there is no child at this point.
177 * @see javax.accessibility.AccessibleComponent#getAccessibleAt
179 public static Accessible getAccessibleAt(Component c, Point p)
181 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
185 * <p>
186 * Returns the <code>Accessible</code> child of the specified component
187 * that has the supplied index within the parent component. The indexing
188 * of the children is zero-based, making the first child have an index of
189 * 0.
190 * </p>
191 * <p>
192 * Caution is advised when using this method, as its operation relies
193 * on the behaviour of varying implementations of an abstract method.
194 * For greater surety, direct use of the AWT component implementation
195 * of this method is advised.
196 * </p>
198 * @param c the component whose child should be returned.
199 * @param i the index of the child within the parent component.
200 * @return the <code>Accessible</code> child at index <code>i</code>
201 * in the component, <code>c</code>.
202 * @see javax.accessibility.AccessibleContext#getAccessibleChild
203 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild
205 public static Accessible getAccessibleChild(Component c, int i)
207 return c.getAccessibleContext().getAccessibleChild(i);
211 * <p>
212 * Returns the number of <code>Accessible</code> children within
213 * the supplied component.
214 * </p>
215 * <p>
216 * Caution is advised when using this method, as its operation relies
217 * on the behaviour of varying implementations of an abstract method.
218 * For greater surety, direct use of the AWT component implementation
219 * of this method is advised.
220 * </p>
222 * @param c the component whose children should be counted.
223 * @return the number of children belonging to the component,
224 * <code>c</code>.
225 * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount
226 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount
228 public static int getAccessibleChildrenCount(Component c)
230 return c.getAccessibleContext().getAccessibleChildrenCount();
234 * <p>
235 * Returns the zero-based index of the specified component
236 * within its parent. If the component doesn't have a parent,
237 * -1 is returned.
238 * </p>
239 * <p>
240 * Caution is advised when using this method, as its operation relies
241 * on the behaviour of varying implementations of an abstract method.
242 * For greater surety, direct use of the AWT component implementation
243 * of this method is advised.
244 * </p>
246 * @param c the component whose parental index should be found.
247 * @return the index of the component within its parent, or -1
248 * if the component doesn't have a parent.
249 * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent
250 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent
252 public static int getAccessibleIndexInParent(Component c)
254 return c.getAccessibleContext().getAccessibleIndexInParent();
258 * <p>
259 * Returns a set of <code>AccessibleState</code>s, which represent
260 * the state of the supplied component.
261 * </p>
262 * <p>
263 * Caution is advised when using this method, as its operation relies
264 * on the behaviour of varying implementations of an abstract method.
265 * For greater surety, direct use of the AWT component implementation
266 * of this method is advised.
267 * </p>
269 * @param c the component whose accessible state should be retrieved.
270 * @return a set of <code>AccessibleState</code> objects, which represent
271 * the state of the supplied component.
272 * @see javax.accessibility.AccessibleContext#getAccessibleStateSet
273 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet
275 public static AccessibleStateSet getAccessibleStateSet(Component c)
277 return c.getAccessibleContext().getAccessibleStateSet();
281 * Calculates the bounds of a component in the component's own coordinate
282 * space. The result has the same height and width as the component's
283 * bounds, but its location is set to (0,0).
285 * @param aComponent The component to measure
287 * @return The component's bounds in its local coordinate space
289 public static Rectangle getLocalBounds(Component aComponent)
291 Rectangle bounds = aComponent.getBounds();
292 return new Rectangle(0, 0, bounds.width, bounds.height);
296 * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
297 * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
299 * @param comp The component to get the JRootPane of
301 * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
303 * @see javax.swing.RootPaneContainer#getRootPane
304 * @see #getAncestorOfClass
306 public static JRootPane getRootPane(Component comp)
308 if (comp instanceof RootPaneContainer)
309 return ((RootPaneContainer)comp).getRootPane();
310 else
311 return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
315 * Returns the least ancestor of <code>comp</code> which has the
316 * specified name.
318 * @param name The name to search for
319 * @param comp The component to search the ancestors of
321 * @return The nearest ancestor of <code>comp</code> with the given
322 * name, or <code>null</code> if no such ancestor exists
324 * @see java.awt.Component#getName
325 * @see #getAncestorOfClass
327 public static Container getAncestorNamed(String name, Component comp)
329 while (comp != null && (comp.getName() != name))
330 comp = comp.getParent();
331 return (Container) comp;
335 * Returns the least ancestor of <code>comp</code> which is an instance
336 * of the specified class.
338 * @param c The class to search for
339 * @param comp The component to search the ancestors of
341 * @return The nearest ancestor of <code>comp</code> which is an instance
342 * of the given class, or <code>null</code> if no such ancestor exists
344 * @see #getAncestorOfClass
345 * @see #windowForComponent
347 public static Container getAncestorOfClass(Class c, Component comp)
349 while (comp != null && (! c.isInstance(comp)))
350 comp = comp.getParent();
351 return (Container) comp;
355 * Returns the first ancestor of <code>comp</code> that is a {@link Window}
356 * or <code>null</code> if <code>comp</code> is not contained in a
357 * {@link Window}.
359 * This is equivalent to calling
360 * <code>getAncestorOfClass(Window, comp)</code> or
361 * <code>windowForComponent(comp)</code>.
363 * @param comp the component for which we are searching the ancestor Window
365 * @return the first ancestor Window of <code>comp</code> or
366 * <code>null</code> if <code>comp</code> is not contained in a Window
368 public static Window getWindowAncestor(Component comp)
370 return (Window) getAncestorOfClass(Window.class, comp);
374 * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
376 * @param comp The component to search for an ancestor window
378 * @return An ancestral window, or <code>null</code> if none exists
380 public static Window windowForComponent(Component comp)
382 return (Window) getAncestorOfClass(Window.class, comp);
386 * Returns the "root" of the component tree containint <code>comp</code>
387 * The root is defined as either the <em>least</em> ancestor of
388 * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
389 * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
390 * Window} ancestors are found.
392 * @param comp The component to search for a root
394 * @return The root of the component's tree, or <code>null</code>
396 public static Component getRoot(Component comp)
398 Applet app = null;
399 Window win = null;
401 while (comp != null)
403 if (win == null && comp instanceof Window)
404 win = (Window) comp;
405 else if (comp instanceof Applet)
406 app = (Applet) comp;
407 comp = comp.getParent();
410 if (win != null)
411 return win;
412 else
413 return app;
417 * Return true if a descends from b, in other words if b is an
418 * ancestor of a.
420 * @param a The child to search the ancestry of
421 * @param b The potential ancestor to search for
423 * @return true if a is a descendent of b, false otherwise
425 public static boolean isDescendingFrom(Component a, Component b)
427 while (true)
429 if (a == null || b == null)
430 return false;
431 if (a == b)
432 return true;
433 a = a.getParent();
438 * Returns the deepest descendent of parent which is both visible and
439 * contains the point <code>(x,y)</code>. Returns parent when either
440 * parent is not a container, or has no children which contain
441 * <code>(x,y)</code>. Returns <code>null</code> when either
442 * <code>(x,y)</code> is outside the bounds of parent, or parent is
443 * <code>null</code>.
445 * @param parent The component to search the descendents of
446 * @param x Horizontal coordinate to search for
447 * @param y Vertical coordinate to search for
449 * @return A component containing <code>(x,y)</code>, or
450 * <code>null</code>
452 * @see java.awt.Container#findComponentAt(int, int)
454 public static Component getDeepestComponentAt(Component parent, int x, int y)
456 if (parent == null || (! parent.contains(x, y)))
457 return null;
459 if (! (parent instanceof Container))
460 return parent;
462 Container c = (Container) parent;
463 return c.findComponentAt(x, y);
467 * Converts a point from a component's local coordinate space to "screen"
468 * coordinates (such as the coordinate space mouse events are delivered
469 * in). This operation is equivalent to translating the point by the
470 * location of the component (which is the origin of its coordinate
471 * space).
473 * @param p The point to convert
474 * @param c The component which the point is expressed in terms of
476 * @see #convertPointFromScreen
478 public static void convertPointToScreen(Point p, Component c)
480 Point c0 = c.getLocationOnScreen();
481 p.translate(c0.x, c0.y);
485 * Converts a point from "screen" coordinates (such as the coordinate
486 * space mouse events are delivered in) to a component's local coordinate
487 * space. This operation is equivalent to translating the point by the
488 * negation of the component's location (which is the origin of its
489 * coordinate space).
491 * @param p The point to convert
492 * @param c The component which the point should be expressed in terms of
494 public static void convertPointFromScreen(Point p, Component c)
496 Point c0 = c.getLocationOnScreen();
497 p.translate(-c0.x, -c0.y);
501 * Converts a point <code>(x,y)</code> from the coordinate space of one
502 * component to another. This is equivalent to converting the point from
503 * <code>source</code> space to screen space, then back from screen space
504 * to <code>destination</code> space. If exactly one of the two
505 * Components is <code>null</code>, it is taken to refer to the root
506 * ancestor of the other component. If both are <code>null</code>, no
507 * transformation is done.
509 * @param source The component which the point is expressed in terms of
510 * @param x Horizontal coordinate of point to transform
511 * @param y Vertical coordinate of point to transform
512 * @param destination The component which the return value will be
513 * expressed in terms of
515 * @return The point <code>(x,y)</code> converted from the coordinate space of the
516 * source component to the coordinate space of the destination component
518 * @see #convertPointToScreen
519 * @see #convertPointFromScreen
520 * @see #convertRectangle
521 * @see #getRoot
523 public static Point convertPoint(Component source, int x, int y,
524 Component destination)
526 Point pt = new Point(x, y);
528 if (source == null && destination == null)
529 return pt;
531 if (source == null)
532 source = getRoot(destination);
534 if (destination == null)
535 destination = getRoot(source);
537 if (source.isShowing() && destination.isShowing())
539 convertPointToScreen(pt, source);
540 convertPointFromScreen(pt, destination);
543 return pt;
546 public static Point convertPoint(Component source, Point aPoint, Component destination)
548 return convertPoint(source, aPoint.x, aPoint.y, destination);
552 * Converts a rectangle from the coordinate space of one component to
553 * another. This is equivalent to converting the rectangle from
554 * <code>source</code> space to screen space, then back from screen space
555 * to <code>destination</code> space. If exactly one of the two
556 * Components is <code>null</code>, it is taken to refer to the root
557 * ancestor of the other component. If both are <code>null</code>, no
558 * transformation is done.
560 * @param source The component which the rectangle is expressed in terms of
561 * @param rect The rectangle to convert
562 * @param destination The component which the return value will be
563 * expressed in terms of
565 * @return A new rectangle, equal in size to the input rectangle, but
566 * with its position converted from the coordinate space of the source
567 * component to the coordinate space of the destination component
569 * @see #convertPointToScreen
570 * @see #convertPointFromScreen
571 * @see #convertPoint(Component, int, int, Component)
572 * @see #getRoot
574 public static Rectangle convertRectangle(Component source,
575 Rectangle rect,
576 Component destination)
578 Point pt = convertPoint(source, rect.x, rect.y, destination);
579 return new Rectangle(pt.x, pt.y, rect.width, rect.height);
583 * Convert a mouse event which refrers to one component to another. This
584 * includes changing the mouse event's coordinate space, as well as the
585 * source property of the event. If <code>source</code> is
586 * <code>null</code>, it is taken to refer to <code>destination</code>'s
587 * root component. If <code>destination</code> is <code>null</code>, the
588 * new event will remain expressed in <code>source</code>'s coordinate
589 * system.
591 * @param source The component the mouse event currently refers to
592 * @param sourceEvent The mouse event to convert
593 * @param destination The component the new mouse event should refer to
595 * @return A new mouse event expressed in terms of the destination
596 * component's coordinate space, and with the destination component as
597 * its source
599 * @see #convertPoint(Component, int, int, Component)
601 public static MouseEvent convertMouseEvent(Component source,
602 MouseEvent sourceEvent,
603 Component destination)
605 Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
606 destination);
608 return new MouseEvent(destination, sourceEvent.getID(),
609 sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
610 newpt.x, newpt.y, sourceEvent.getClickCount(),
611 sourceEvent.isPopupTrigger(), sourceEvent.getButton());
615 * Recursively walk the component tree under <code>comp</code> calling
616 * <code>updateUI</code> on each {@link JComponent} found. This causes
617 * the entire tree to re-initialize its UI delegates.
619 * @param comp The component to walk the children of, calling <code>updateUI</code>
621 public static void updateComponentTreeUI(Component comp)
623 if (comp == null)
624 return;
626 if (comp instanceof Container)
628 Component[] children = ((Container)comp).getComponents();
629 for (int i = 0; i < children.length; ++i)
630 updateComponentTreeUI(children[i]);
633 if (comp instanceof JComponent)
634 ((JComponent)comp).updateUI();
639 * <p>Layout a "compound label" consisting of a text string and an icon
640 * which is to be placed near the rendered text. Once the text and icon
641 * are laid out, the text rectangle and icon rectangle parameters are
642 * altered to store the calculated positions.</p>
644 * <p>The size of the text is calculated from the provided font metrics
645 * object. This object should be the metrics of the font you intend to
646 * paint the label with.</p>
648 * <p>The position values control where the text is placed relative to
649 * the icon. The horizontal position value should be one of the constants
650 * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>,
651 * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value
652 * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code>
653 * or <code>CENTER</code>.</p>
655 * <p>The text-icon gap value controls the number of pixels between the
656 * icon and the text.</p>
658 * <p>The alignment values control where the text and icon are placed, as
659 * a combined unit, within the view rectangle. The horizontal alignment
660 * value should be one of the constants <code>LEADING</code>,
661 * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
662 * <code>CENTER</code>. The vertical alignment valus should be one of the
663 * constants <code>TOP</code>, <code>BOTTOM</code> or
664 * <code>CENTER</code>.</p>
666 * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
667 * given for horizontal alignment or horizontal text position, they are
668 * interpreted relative to the provided component's orientation property,
669 * a constant in the {@link java.awt.ComponentOrientation} class. For
670 * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>,
671 * then the <code>LEADING</code> value is a synonym for <code>LEFT</code>
672 * and the <code>TRAILING</code> value is a synonym for
673 * <code>RIGHT</code></p>
675 * <p>If the text and icon are equal to or larger than the view
676 * rectangle, the horizontal and vertical alignment values have no
677 * affect.</p>
679 * @param c A component used for its orientation value
680 * @param fm The font metrics used to measure the text
681 * @param text The text to place in the compound label
682 * @param icon The icon to place next to the text
683 * @param verticalAlignment The vertical alignment of the label relative
684 * to its component
685 * @param horizontalAlignment The horizontal alignment of the label
686 * relative to its component
687 * @param verticalTextPosition The vertical position of the label's text
688 * relative to its icon
689 * @param horizontalTextPosition The horizontal position of the label's
690 * text relative to its icon
691 * @param viewR The view rectangle, specifying the area which layout is
692 * constrained to
693 * @param iconR A rectangle which is modified to hold the laid-out
694 * position of the icon
695 * @param textR A rectangle which is modified to hold the laid-out
696 * position of the text
697 * @param textIconGap The distance between text and icon
699 * @return The string of characters, possibly truncated with an elipsis,
700 * which is laid out in this label
703 public static String layoutCompoundLabel(JComponent c,
704 FontMetrics fm,
705 String text,
706 Icon icon,
707 int verticalAlignment,
708 int horizontalAlignment,
709 int verticalTextPosition,
710 int horizontalTextPosition,
711 Rectangle viewR,
712 Rectangle iconR,
713 Rectangle textR,
714 int textIconGap)
717 // Fix up the orientation-based horizontal positions.
719 if (horizontalTextPosition == LEADING)
721 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
722 horizontalTextPosition = RIGHT;
723 else
724 horizontalTextPosition = LEFT;
726 else if (horizontalTextPosition == TRAILING)
728 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
729 horizontalTextPosition = LEFT;
730 else
731 horizontalTextPosition = RIGHT;
734 // Fix up the orientation-based alignments.
736 if (horizontalAlignment == LEADING)
738 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
739 horizontalAlignment = RIGHT;
740 else
741 horizontalAlignment = LEFT;
743 else if (horizontalAlignment == TRAILING)
745 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
746 horizontalAlignment = LEFT;
747 else
748 horizontalAlignment = RIGHT;
751 return layoutCompoundLabel(fm, text, icon,
752 verticalAlignment,
753 horizontalAlignment,
754 verticalTextPosition,
755 horizontalTextPosition,
756 viewR, iconR, textR, textIconGap);
760 * <p>Layout a "compound label" consisting of a text string and an icon
761 * which is to be placed near the rendered text. Once the text and icon
762 * are laid out, the text rectangle and icon rectangle parameters are
763 * altered to store the calculated positions.</p>
765 * <p>The size of the text is calculated from the provided font metrics
766 * object. This object should be the metrics of the font you intend to
767 * paint the label with.</p>
769 * <p>The position values control where the text is placed relative to
770 * the icon. The horizontal position value should be one of the constants
771 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
772 * vertical position value should be one fo the constants
773 * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
775 * <p>The text-icon gap value controls the number of pixels between the
776 * icon and the text.</p>
778 * <p>The alignment values control where the text and icon are placed, as
779 * a combined unit, within the view rectangle. The horizontal alignment
780 * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
781 * <code>CENTER</code>. The vertical alignment valus should be one of the
782 * constants <code>TOP</code>, <code>BOTTOM</code> or
783 * <code>CENTER</code>.</p>
785 * <p>If the text and icon are equal to or larger than the view
786 * rectangle, the horizontal and vertical alignment values have no
787 * affect.</p>
789 * <p>Note that this method does <em>not</em> know how to deal with
790 * horizontal alignments or positions given as <code>LEADING</code> or
791 * <code>TRAILING</code> values. Use the other overloaded variant of this
792 * method if you wish to use such values.
794 * @param fm The font metrics used to measure the text
795 * @param text The text to place in the compound label
796 * @param icon The icon to place next to the text
797 * @param verticalAlignment The vertical alignment of the label relative
798 * to its component
799 * @param horizontalAlignment The horizontal alignment of the label
800 * relative to its component
801 * @param verticalTextPosition The vertical position of the label's text
802 * relative to its icon
803 * @param horizontalTextPosition The horizontal position of the label's
804 * text relative to its icon
805 * @param viewR The view rectangle, specifying the area which layout is
806 * constrained to
807 * @param iconR A rectangle which is modified to hold the laid-out
808 * position of the icon
809 * @param textR A rectangle which is modified to hold the laid-out
810 * position of the text
811 * @param textIconGap The distance between text and icon
813 * @return The string of characters, possibly truncated with an elipsis,
814 * which is laid out in this label
817 public static String layoutCompoundLabel(FontMetrics fm,
818 String text,
819 Icon icon,
820 int verticalAlignment,
821 int horizontalAlignment,
822 int verticalTextPosition,
823 int horizontalTextPosition,
824 Rectangle viewR,
825 Rectangle iconR,
826 Rectangle textR,
827 int textIconGap)
830 // Work out basic height and width.
832 if (icon == null)
834 textIconGap = 0;
835 iconR.width = 0;
836 iconR.height = 0;
838 else
840 iconR.width = icon.getIconWidth();
841 iconR.height = icon.getIconHeight();
843 if (text == null || text.equals(""))
845 textIconGap = 0;
846 textR.width = 0;
847 textR.height = 0;
849 else
851 int fromIndex = 0;
852 textR.width = fm.stringWidth(text);
853 textR.height = fm.getHeight();
854 while (text.indexOf('\n', fromIndex) != -1)
856 textR.height += fm.getHeight();
857 fromIndex = text.indexOf('\n', fromIndex) + 1;
861 // Work out the position of text and icon, assuming the top-left coord
862 // starts at (0,0). We will fix that up momentarily, after these
863 // "position" decisions are made and we look at alignment.
865 switch (horizontalTextPosition)
867 case LEFT:
868 textR.x = 0;
869 iconR.x = textR.width + textIconGap;
870 break;
871 case RIGHT:
872 iconR.x = 0;
873 textR.x = iconR.width + textIconGap;
874 break;
875 case CENTER:
876 int centerLine = Math.max(textR.width, iconR.width) / 2;
877 textR.x = centerLine - textR.width/2;
878 iconR.x = centerLine - iconR.width/2;
879 break;
882 switch (verticalTextPosition)
884 case TOP:
885 textR.y = 0;
886 iconR.y = (horizontalTextPosition == CENTER
887 ? textR.height + textIconGap : 0);
888 break;
889 case BOTTOM:
890 iconR.y = 0;
891 textR.y = (horizontalTextPosition == CENTER
892 ? iconR.height + textIconGap
893 : Math.max(iconR.height - textR.height, 0));
894 break;
895 case CENTER:
896 int centerLine = Math.max(textR.height, iconR.height) / 2;
897 textR.y = centerLine - textR.height/2;
898 iconR.y = centerLine - iconR.height/2;
899 break;
901 // The two rectangles are laid out correctly now, but only assuming
902 // that their upper left corner is at (0,0). If we have any alignment other
903 // than TOP and LEFT, we need to adjust them.
905 Rectangle u = textR.union(iconR);
906 int horizontalAdjustment = viewR.x;
907 int verticalAdjustment = viewR.y;
908 switch (verticalAlignment)
910 case TOP:
911 break;
912 case BOTTOM:
913 verticalAdjustment += (viewR.height - u.height);
914 break;
915 case CENTER:
916 verticalAdjustment += ((viewR.height/2) - (u.height/2));
917 break;
919 switch (horizontalAlignment)
921 case LEFT:
922 break;
923 case RIGHT:
924 horizontalAdjustment += (viewR.width - u.width);
925 break;
926 case CENTER:
927 horizontalAdjustment += ((viewR.width/2) - (u.width/2));
928 break;
931 iconR.x += horizontalAdjustment;
932 iconR.y += verticalAdjustment;
934 textR.x += horizontalAdjustment;
935 textR.y += verticalAdjustment;
937 return text;
940 /**
941 * Calls {@link java.awt.EventQueue#invokeLater} with the
942 * specified {@link Runnable}.
944 public static void invokeLater(Runnable doRun)
946 java.awt.EventQueue.invokeLater(doRun);
949 /**
950 * Calls {@link java.awt.EventQueue#invokeAndWait} with the
951 * specified {@link Runnable}.
953 public static void invokeAndWait(Runnable doRun)
954 throws InterruptedException,
955 InvocationTargetException
957 java.awt.EventQueue.invokeAndWait(doRun);
960 /**
961 * Calls {@link java.awt.EventQueue#isDispatchThread()}.
963 * @return <code>true</code> if the current thread is the current AWT event
964 * dispatch thread.
966 public static boolean isEventDispatchThread()
968 return java.awt.EventQueue.isDispatchThread();
972 * This method paints the given component at the given position and size.
973 * The component will be reparented to the container given.
975 * @param g The Graphics object to draw with.
976 * @param c The Component to draw
977 * @param p The Container to reparent to.
978 * @param x The x coordinate to draw at.
979 * @param y The y coordinate to draw at.
980 * @param w The width of the drawing area.
981 * @param h The height of the drawing area.
983 public static void paintComponent(Graphics g, Component c, Container p,
984 int x, int y, int w, int h)
986 Container parent = c.getParent();
987 if (parent != null)
988 parent.remove(c);
989 if (p != null)
990 p.add(c);
992 Shape savedClip = g.getClip();
994 g.setClip(x, y, w, h);
995 g.translate(x, y);
997 c.paint(g);
999 g.translate(-x, -y);
1000 g.setClip(savedClip);
1004 * This method paints the given component in the given rectangle.
1005 * The component will be reparented to the container given.
1007 * @param g The Graphics object to draw with.
1008 * @param c The Component to draw
1009 * @param p The Container to reparent to.
1010 * @param r The rectangle that describes the drawing area.
1012 public static void paintComponent(Graphics g, Component c,
1013 Container p, Rectangle r)
1015 paintComponent(g, c, p, r.x, r.y, r.width, r.height);
1019 * This method returns the common Frame owner used in JDialogs or
1020 * JWindow when no owner is provided.
1022 * @return The common Frame
1024 static Frame getOwnerFrame()
1026 if (ownerFrame == null)
1027 ownerFrame = new OwnerFrame();
1028 return ownerFrame;
1032 * Checks if left mouse button was clicked.
1034 * @param event the event to check
1036 * @return true if left mouse was clicked, false otherwise.
1038 public static boolean isLeftMouseButton(MouseEvent event)
1040 return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK)
1041 == InputEvent.BUTTON1_DOWN_MASK);
1045 * Checks if middle mouse button was clicked.
1047 * @param event the event to check
1049 * @return true if middle mouse was clicked, false otherwise.
1051 public static boolean isMiddleMouseButton(MouseEvent event)
1053 return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
1054 == InputEvent.BUTTON2_DOWN_MASK);
1058 * Checks if right mouse button was clicked.
1060 * @param event the event to check
1062 * @return true if right mouse was clicked, false otherwise.
1064 public static boolean isRightMouseButton(MouseEvent event)
1066 return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
1067 == InputEvent.BUTTON3_DOWN_MASK);
1071 * This frame should be used when constructing a Window/JDialog without
1072 * a parent. In this case, we are forced to use this frame as a window's
1073 * parent, because we simply cannot pass null instead of parent to Window
1074 * constructor, since doing it will result in NullPointerException.
1076 private static class OwnerFrame extends Frame
1078 public void setVisible(boolean b)
1080 // Do nothing here.
1083 public boolean isShowing()
1085 return true;
1089 public static boolean notifyAction(Action action,
1090 KeyStroke ks,
1091 KeyEvent event,
1092 Object sender,
1093 int modifiers)
1095 if (action != null && action.isEnabled())
1097 String name = (String) action.getValue(Action.ACTION_COMMAND_KEY);
1098 if (name == null
1099 && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
1100 name = new String(new char[] {event.getKeyChar()});
1101 action.actionPerformed(new ActionEvent(sender,
1102 ActionEvent.ACTION_PERFORMED,
1103 name, modifiers));
1104 return true;
1106 return false;
1110 * <p>Change the shared, UI-managed {@link ActionMap} for a given
1111 * component. ActionMaps are arranged in a hierarchy, in order to
1112 * encourage sharing of common actions between components. The hierarchy
1113 * unfortunately places UI-managed ActionMaps at the <em>end</em> of the
1114 * parent-pointer chain, as illustrated:</p>
1116 * <pre>
1117 * [{@link javax.swing.JComponent#getActionMap()}]
1118 * --&gt; [{@link javax.swing.ActionMap}]
1119 * parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapActionMap}]
1120 * parent --&gt; [{@link javax.swing.plaf.ActionMapUIResource}]
1121 * </pre>
1123 * <p>Our goal with this method is to replace the first ActionMap along
1124 * this chain which is an instance of {@link ActionMapUIResource}, since
1125 * these are the ActionMaps which are supposed to be shared between
1126 * components.</p>
1128 * <p>If the provided ActionMap is <code>null</code>, we interpret the
1129 * call as a request to remove the UI-managed ActionMap from the
1130 * component's ActionMap parent chain.</p>
1132 public static void replaceUIActionMap(JComponent component,
1133 ActionMap uiActionMap)
1135 ActionMap child = component.getActionMap();
1136 if (child == null)
1137 component.setActionMap(uiActionMap);
1138 else
1140 ActionMap parent = child.getParent();
1141 while (parent != null && !(parent instanceof ActionMapUIResource))
1143 child = parent;
1144 parent = child.getParent();
1146 child.setParent(uiActionMap);
1151 * <p>Change the shared, UI-managed {@link InputMap} for a given
1152 * component. InputMaps are arranged in a hierarchy, in order to
1153 * encourage sharing of common input mappings between components. The
1154 * hierarchy unfortunately places UI-managed InputMaps at the
1155 * <em>end</em> of the parent-pointer chain, as illustrated:</p>
1157 * <pre>
1158 * [{@link javax.swing.JComponent#getInputMap()}]
1159 * --&gt; [{@link javax.swing.InputMap}]
1160 * parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapWrapper}]
1161 * parent --&gt; [{@link javax.swing.plaf.InputMapUIResource}]
1162 * </pre>
1164 * <p>Our goal with this method is to replace the first InputMap along
1165 * this chain which is an instance of {@link InputMapUIResource}, since
1166 * these are the InputMaps which are supposed to be shared between
1167 * components.</p>
1169 * <p>If the provided InputMap is <code>null</code>, we interpret the
1170 * call as a request to remove the UI-managed InputMap from the
1171 * component's InputMap parent chain.</p>
1173 public static void replaceUIInputMap(JComponent component,
1174 int condition,
1175 InputMap uiInputMap)
1177 InputMap child = component.getInputMap(condition);
1178 if (child == null)
1179 component.setInputMap(condition, uiInputMap);
1180 else
1182 InputMap parent = child.getParent();
1183 while (parent != null && !(parent instanceof InputMapUIResource))
1185 child = parent;
1186 parent = parent.getParent();
1188 child.setParent(uiInputMap);
1193 * Subtracts a rectangle from another and return the area as an array
1194 * of rectangles.
1195 * Returns the areas of rectA which are not covered by rectB.
1196 * If the rectangles do not overlap, or if either parameter is
1197 * <code>null</code>, a zero-size array is returned.
1198 * @param rectA The first rectangle
1199 * @param rectB The rectangle to subtract from the first
1200 * @return An array of rectangles representing the area in rectA
1201 * not overlapped by rectB
1203 public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
1205 if (rectA == null || rectB == null)
1206 return new Rectangle[0];
1208 Rectangle[] r = new Rectangle[4];
1209 int x1 = rectA.x;
1210 int y1 = rectA.y;
1211 int w1 = rectA.width;
1212 int h1 = rectA.height;
1213 int x2 = rectB.x;
1214 int y2 = rectB.y;
1215 int w2 = rectB.width;
1216 int h2 = rectB.height;
1218 // (outer box = rectA)
1219 // -------------
1220 // |_____0_____|
1221 // | |rectB| |
1222 // |_1|_____|_2|
1223 // | 3 |
1224 // -------------
1225 int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
1226 int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
1227 int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
1228 int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
1229 int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
1231 if (H0 > 0)
1232 r[0] = new Rectangle(x1, y1, w1, H0);
1233 else
1234 r[0] = null;
1236 if (W1 > 0 && H12 > 0)
1237 r[1] = new Rectangle(x1, y1 + H0, W1, H12);
1238 else
1239 r[1] = null;
1241 if (W2 > 0 && H12 > 0)
1242 r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
1243 else
1244 r[2] = null;
1246 if (H3 > 0)
1247 r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
1248 else
1249 r[3] = null;
1251 // sort out null objects
1252 int n = 0;
1253 for (int i = 0; i < 4; i++)
1254 if (r[i] != null)
1255 n++;
1256 Rectangle[] out = new Rectangle[n];
1257 for (int i = 3; i >= 0; i--)
1258 if (r[i] != null)
1259 out[--n] = r[i];
1261 return out;
1265 * Calculates the intersection of two rectangles.
1267 * @param x upper-left x coodinate of first rectangle
1268 * @param y upper-left y coodinate of first rectangle
1269 * @param w width of first rectangle
1270 * @param h height of first rectangle
1271 * @param rect a Rectangle object of the second rectangle
1272 * @throws NullPointerException if rect is null.
1274 * @return a rectangle corresponding to the intersection of the
1275 * two rectangles. A zero rectangle is returned if the rectangles
1276 * do not overlap.
1278 public static Rectangle computeIntersection(int x, int y, int w, int h,
1279 Rectangle rect)
1281 int x2 = (int) rect.getX();
1282 int y2 = (int) rect.getY();
1283 int w2 = (int) rect.getWidth();
1284 int h2 = (int) rect.getHeight();
1286 int dx = (x > x2) ? x : x2;
1287 int dy = (y > y2) ? y : y2;
1288 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1289 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1291 if (dw >= 0 && dh >= 0)
1292 return new Rectangle(dx, dy, dw, dh);
1294 return new Rectangle(0, 0, 0, 0);
1298 * Calculates the width of a given string.
1300 * @param fm the <code>FontMetrics</code> object to use
1301 * @param str the string
1303 * @return the width of the the string.
1305 public static int computeStringWidth(FontMetrics fm, String str)
1307 return fm.stringWidth(str);
1311 * Calculates the union of two rectangles.
1313 * @param x upper-left x coodinate of first rectangle
1314 * @param y upper-left y coodinate of first rectangle
1315 * @param w width of first rectangle
1316 * @param h height of first rectangle
1317 * @param rect a Rectangle object of the second rectangle
1318 * @throws NullPointerException if rect is null.
1320 * @return a rectangle corresponding to the union of the
1321 * two rectangles. A rectangle encompassing both is returned if the
1322 * rectangles do not overlap.
1324 public static Rectangle computeUnion(int x, int y, int w, int h,
1325 Rectangle rect)
1327 int x2 = (int) rect.getX();
1328 int y2 = (int) rect.getY();
1329 int w2 = (int) rect.getWidth();
1330 int h2 = (int) rect.getHeight();
1332 int dx = (x < x2) ? x : x2;
1333 int dy = (y < y2) ? y : y2;
1334 int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1335 int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1337 if (dw >= 0 && dh >= 0)
1338 return new Rectangle(dx, dy, dw, dh);
1340 return new Rectangle(0, 0, 0, 0);
1344 * Tests if a rectangle contains another.
1345 * @param a first rectangle
1346 * @param b second rectangle
1347 * @return true if a contains b, false otherwise
1348 * @throws NullPointerException
1350 public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
1352 // Note: zero-size rects inclusive, differs from Rectangle.contains()
1353 return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
1354 && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
1355 && b.y + b.height <= a.y + a.height;
1359 * Returns the InputMap that is provided by the ComponentUI of
1360 * <code>component</code> for the specified condition.
1362 * @param component the component for which the InputMap is returned
1363 * @param cond the condition that specifies which of the three input
1364 * maps should be returned, may be
1365 * {@link JComponent#WHEN_IN_FOCUSED_WINDOW},
1366 * {@link JComponent#WHEN_FOCUSED} or
1367 * {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}
1369 * @return The input map.
1371 public static InputMap getUIInputMap(JComponent component, int cond)
1373 if (UIManager.getUI(component) != null)
1374 // we assume here that the UI class sets the parent of the component's
1375 // InputMap, which is the correct behaviour. If it's not, then
1376 // this can be considered a bug
1377 return component.getInputMap(cond).getParent();
1378 else
1379 return null;
1383 * Returns the ActionMap that is provided by the ComponentUI of
1384 * <code>component</code>.
1386 * @param component the component for which the ActionMap is returned
1388 public static ActionMap getUIActionMap(JComponent component)
1390 if (UIManager.getUI(component) != null)
1391 // we assume here that the UI class sets the parent of the component's
1392 // ActionMap, which is the correct behaviour. If it's not, then
1393 // this can be considered a bug
1394 return component.getActionMap().getParent();
1395 else
1396 return null;
1400 * Processes key bindings for the component that is associated with the
1401 * key event. Note that this method does not make sense for
1402 * JComponent-derived components, except when
1403 * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is
1404 * not called.
1406 * This method searches through the component hierarchy of the component's
1407 * top-level container to find a <code>JComponent</code> that has a binding
1408 * for the key event in the WHEN_IN_FOCUSED_WINDOW scope.
1410 * @param ev the key event
1412 * @return <code>true</code> if a binding has been found and processed,
1413 * <code>false</code> otherwise
1415 * @since 1.4
1417 public static boolean processKeyBindings(KeyEvent ev)
1419 Component c = ev.getComponent();
1420 KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev);
1421 KeyboardManager km = KeyboardManager.getManager();
1422 return km.processKeyStroke(c, s, ev);