Merge from the pain train
[official-gcc.git] / libjava / javax / swing / SwingUtilities.java
blob8e987425fc4d260e4039828f5c702d6be5a22fd8
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 * This class contains 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 * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
357 * @param comp The component to search for an ancestor window
359 * @return An ancestral window, or <code>null</code> if none exists
361 public static Window windowForComponent(Component comp)
363 return (Window) getAncestorOfClass(Window.class, comp);
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.
373 * @param comp The component to search for a root
375 * @return The root of the component's tree, or <code>null</code>
377 public static Component getRoot(Component comp)
379 Applet app = null;
380 Window win = null;
382 while (comp != null)
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();
391 if (win != null)
392 return win;
393 else
394 return app;
398 * Return true if a descends from b, in other words if b is an
399 * ancestor of a.
401 * @param a The child to search the ancestry of
402 * @param b The potential ancestor to search for
404 * @return true if a is a descendent of b, false otherwise
406 public static boolean isDescendingFrom(Component a, Component b)
408 while (true)
410 if (a == null || b == null)
411 return false;
412 if (a == b)
413 return true;
414 a = a.getParent();
419 * Returns the deepest descendent of parent which is both visible and
420 * contains the point <code>(x,y)</code>. Returns parent when either
421 * parent is not a container, or has no children which contain
422 * <code>(x,y)</code>. Returns <code>null</code> when either
423 * <code>(x,y)</code> is outside the bounds of parent, or parent is
424 * <code>null</code>.
426 * @param parent The component to search the descendents of
427 * @param x Horizontal coordinate to search for
428 * @param y Vertical coordinate to search for
430 * @return A component containing <code>(x,y)</code>, or
431 * <code>null</code>
433 * @see java.awt.Container#findComponentAt
435 public static Component getDeepestComponentAt(Component parent, int x, int y)
437 if (parent == null || (! parent.contains(x, y)))
438 return null;
440 if (! (parent instanceof Container))
441 return parent;
443 Container c = (Container) parent;
444 return c.findComponentAt(x, y);
448 * Converts a point from a component's local coordinate space to "screen"
449 * coordinates (such as the coordinate space mouse events are delivered
450 * in). This operation is equivalent to translating the point by the
451 * location of the component (which is the origin of its coordinate
452 * space).
454 * @param p The point to convert
455 * @param c The component which the point is expressed in terms of
457 * @see convertPointFromScreen
459 public static void convertPointToScreen(Point p, Component c)
461 Point c0 = c.getLocationOnScreen();
462 p.translate(c0.x, c0.y);
466 * Converts a point from "screen" coordinates (such as the coordinate
467 * space mouse events are delivered in) to a component's local coordinate
468 * space. This operation is equivalent to translating the point by the
469 * negation of the component's location (which is the origin of its
470 * coordinate space).
472 * @param p The point to convert
473 * @param c The component which the point should be expressed in terms of
475 public static void convertPointFromScreen(Point p, Component c)
477 Point c0 = c.getLocationOnScreen();
478 p.translate(-c0.x, -c0.y);
482 * Converts a point <code>(x,y)</code> from the coordinate space of one
483 * component to another. This is equivalent to converting the point from
484 * <code>source</code> space to screen space, then back from screen space
485 * to <code>destination</code> space. If exactly one of the two
486 * Components is <code>null</code>, it is taken to refer to the root
487 * ancestor of the other component. If both are <code>null</code>, no
488 * transformation is done.
490 * @param source The component which the point is expressed in terms of
491 * @param x Horizontal coordinate of point to transform
492 * @param y Vertical coordinate of point to transform
493 * @param destination The component which the return value will be
494 * expressed in terms of
496 * @return The point <code>(x,y)</code> converted from the coordinate space of the
497 * source component to the coordinate space of the destination component
499 * @see #convertPointToScreen
500 * @see #convertPointFromScreen
501 * @see #convertRectangle
502 * @see #getRoot
504 public static Point convertPoint(Component source, int x, int y,
505 Component destination)
507 Point pt = new Point(x, y);
509 if (source == null && destination == null)
510 return pt;
512 if (source == null)
513 source = getRoot(destination);
515 if (destination == null)
516 destination = getRoot(source);
518 convertPointToScreen(pt, source);
519 convertPointFromScreen(pt, destination);
521 return pt;
524 public static Point convertPoint(Component source, Point aPoint, Component destination)
526 return convertPoint(source, aPoint.x, aPoint.y, destination);
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.
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
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
547 * @see #convertPointToScreen
548 * @see #convertPointFromScreen
549 * @see #convertPoint
550 * @see #getRoot
552 public static Rectangle convertRectangle(Component source,
553 Rectangle rect,
554 Component destination)
556 Point pt = convertPoint(source, rect.x, rect.y, destination);
557 return new Rectangle(pt.x, pt.y, rect.width, rect.height);
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.
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
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
577 * @see #convertPoint
579 public static MouseEvent convertMouseEvent(Component source,
580 MouseEvent sourceEvent,
581 Component destination)
583 Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
584 destination);
586 return new MouseEvent(destination, sourceEvent.getID(),
587 sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
588 newpt.x, newpt.y, sourceEvent.getClickCount(),
589 sourceEvent.isPopupTrigger(), sourceEvent.getButton());
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.
597 * @param comp The component to walk the children of, calling <code>updateUI</code>
599 public static void updateComponentTreeUI(Component comp)
601 if (comp == null)
602 return;
604 if (comp instanceof Container)
606 Component[] children = ((Container)comp).getComponents();
607 for (int i = 0; i < children.length; ++i)
608 updateComponentTreeUI(children[i]);
611 if (comp instanceof JComponent)
612 ((JComponent)comp).updateUI();
617 * <p>Layout a "compound label" consisting of a text string and an icon
618 * which is to be placed near the rendered text. Once the text and icon
619 * are laid out, the text rectangle and icon rectangle parameters are
620 * altered to store the calculated positions.</p>
622 * <p>The size of the text is calculated from the provided font metrics
623 * object. This object should be the metrics of the font you intend to
624 * paint the label with.</p>
626 * <p>The position values control where the text is placed relative to
627 * the icon. The horizontal position value should be one of the constants
628 * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>,
629 * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value
630 * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code>
631 * or <code>CENTER</code>.</p>
633 * <p>The text-icon gap value controls the number of pixels between the
634 * icon and the text.</p>
636 * <p>The alignment values control where the text and icon are placed, as
637 * a combined unit, within the view rectangle. The horizontal alignment
638 * value should be one of the constants <code>LEADING</code>,
639 * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
640 * <code>CENTER</code>. The vertical alignment valus should be one of the
641 * constants <code>TOP</code>, <code>BOTTOM</code> or
642 * <code>CENTER</code>.</p>
644 * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
645 * given for horizontal alignment or horizontal text position, they are
646 * interpreted relative to the provided component's orientation property,
647 * a constant in the {@link java.awt.ComponentOrientation} class. For
648 * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>,
649 * then the <code>LEADING</code> value is a synonym for <code>LEFT</code>
650 * and the <code>TRAILING</code> value is a synonym for
651 * <code>RIGHT</code></p>
653 * <p>If the text and icon are equal to or larger than the view
654 * rectangle, the horizontal and vertical alignment values have no
655 * affect.</p>
657 * @param c A component used for its orientation value
658 * @param fm The font metrics used to measure the text
659 * @param text The text to place in the compound label
660 * @param icon The icon to place next to the text
661 * @param verticalAlignment The vertical alignment of the label relative
662 * to its component
663 * @param horizontalAlignment The horizontal alignment of the label
664 * relative to its component
665 * @param verticalTextPosition The vertical position of the label's text
666 * relative to its icon
667 * @param horizontalTextPosition The horizontal position of the label's
668 * text relative to its icon
669 * @param viewR The view rectangle, specifying the area which layout is
670 * constrained to
671 * @param iconR A rectangle which is modified to hold the laid-out
672 * position of the icon
673 * @param textR A rectangle which is modified to hold the laid-out
674 * position of the text
675 * @param textIconGap The distance between text and icon
677 * @return The string of characters, possibly truncated with an elipsis,
678 * which is laid out in this label
681 public static String layoutCompoundLabel(JComponent c,
682 FontMetrics fm,
683 String text,
684 Icon icon,
685 int verticalAlignment,
686 int horizontalAlignment,
687 int verticalTextPosition,
688 int horizontalTextPosition,
689 Rectangle viewR,
690 Rectangle iconR,
691 Rectangle textR,
692 int textIconGap)
695 // Fix up the orientation-based horizontal positions.
697 if (horizontalTextPosition == LEADING)
699 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
700 horizontalTextPosition = RIGHT;
701 else
702 horizontalTextPosition = LEFT;
704 else if (horizontalTextPosition == TRAILING)
706 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
707 horizontalTextPosition = LEFT;
708 else
709 horizontalTextPosition = RIGHT;
712 // Fix up the orientation-based alignments.
714 if (horizontalAlignment == LEADING)
716 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
717 horizontalAlignment = RIGHT;
718 else
719 horizontalAlignment = LEFT;
721 else if (horizontalAlignment == TRAILING)
723 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
724 horizontalAlignment = LEFT;
725 else
726 horizontalAlignment = RIGHT;
729 return layoutCompoundLabel(fm, text, icon,
730 verticalAlignment,
731 horizontalAlignment,
732 verticalTextPosition,
733 horizontalTextPosition,
734 viewR, iconR, textR, textIconGap);
738 * <p>Layout a "compound label" consisting of a text string and an icon
739 * which is to be placed near the rendered text. Once the text and icon
740 * are laid out, the text rectangle and icon rectangle parameters are
741 * altered to store the calculated positions.</p>
743 * <p>The size of the text is calculated from the provided font metrics
744 * object. This object should be the metrics of the font you intend to
745 * paint the label with.</p>
747 * <p>The position values control where the text is placed relative to
748 * the icon. The horizontal position value should be one of the constants
749 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
750 * vertical position value should be one fo the constants
751 * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
753 * <p>The text-icon gap value controls the number of pixels between the
754 * icon and the text.</p>
756 * <p>The alignment values control where the text and icon are placed, as
757 * a combined unit, within the view rectangle. The horizontal alignment
758 * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
759 * <code>CENTER</code>. The vertical alignment valus should be one of the
760 * constants <code>TOP</code>, <code>BOTTOM</code> or
761 * <code>CENTER</code>.</p>
763 * <p>If the text and icon are equal to or larger than the view
764 * rectangle, the horizontal and vertical alignment values have no
765 * affect.</p>
767 * <p>Note that this method does <em>not</em> know how to deal with
768 * horizontal alignments or positions given as <code>LEADING</code> or
769 * <code>TRAILING</code> values. Use the other overloaded variant of this
770 * method if you wish to use such values.
772 * @param fm The font metrics used to measure the text
773 * @param text The text to place in the compound label
774 * @param icon The icon to place next to the text
775 * @param verticalAlignment The vertical alignment of the label relative
776 * to its component
777 * @param horizontalAlignment The horizontal alignment of the label
778 * relative to its component
779 * @param verticalTextPosition The vertical position of the label's text
780 * relative to its icon
781 * @param horizontalTextPosition The horizontal position of the label's
782 * text relative to its icon
783 * @param viewR The view rectangle, specifying the area which layout is
784 * constrained to
785 * @param iconR A rectangle which is modified to hold the laid-out
786 * position of the icon
787 * @param textR A rectangle which is modified to hold the laid-out
788 * position of the text
789 * @param textIconGap The distance between text and icon
791 * @return The string of characters, possibly truncated with an elipsis,
792 * which is laid out in this label
795 public static String layoutCompoundLabel(FontMetrics fm,
796 String text,
797 Icon icon,
798 int verticalAlignment,
799 int horizontalAlignment,
800 int verticalTextPosition,
801 int horizontalTextPosition,
802 Rectangle viewR,
803 Rectangle iconR,
804 Rectangle textR,
805 int textIconGap)
808 // Work out basic height and width.
810 if (icon == null)
812 textIconGap = 0;
813 iconR.width = 0;
814 iconR.height = 0;
816 else
818 iconR.width = icon.getIconWidth();
819 iconR.height = icon.getIconHeight();
821 if (text == null)
823 textIconGap = 0;
824 textR.width = 0;
825 textR.height = 0;
827 else
829 textR.width = fm.stringWidth(text);
830 textR.height = fm.getHeight();
833 // Work out the position of text and icon, assuming the top-left coord
834 // starts at (0,0). We will fix that up momentarily, after these
835 // "position" decisions are made and we look at alignment.
837 switch (horizontalTextPosition)
839 case LEFT:
840 textR.x = 0;
841 iconR.x = textR.width + textIconGap;
842 break;
843 case RIGHT:
844 iconR.x = 0;
845 textR.x = iconR.width + textIconGap;
846 break;
847 case CENTER:
848 int centerLine = Math.max(textR.width, iconR.width) / 2;
849 textR.x = centerLine - textR.width/2;
850 iconR.x = centerLine - iconR.width/2;
851 break;
854 switch (verticalTextPosition)
856 case TOP:
857 textR.y = 0;
858 iconR.y = (horizontalTextPosition == CENTER
859 ? textR.height + textIconGap : 0);
860 break;
861 case BOTTOM:
862 iconR.y = 0;
863 textR.y = (horizontalTextPosition == CENTER
864 ? iconR.height + textIconGap
865 : iconR.height - textR.height);
866 break;
867 case CENTER:
868 int centerLine = Math.max(textR.height, iconR.height) / 2;
869 textR.y = centerLine - textR.height/2;
870 iconR.y = centerLine - iconR.height/2;
871 break;
873 // The two rectangles are laid out correctly now, but only assuming
874 // that their upper left corner is at (0,0). If we have any alignment other
875 // than TOP and LEFT, we need to adjust them.
877 Rectangle u = textR.union(iconR);
878 int horizontalAdjustment = viewR.x;
879 int verticalAdjustment = viewR.y;
880 switch (verticalAlignment)
882 case TOP:
883 break;
884 case BOTTOM:
885 verticalAdjustment += (viewR.height - u.height);
886 break;
887 case CENTER:
888 verticalAdjustment += ((viewR.height/2) - (u.height/2));
889 break;
891 switch (horizontalAlignment)
893 case LEFT:
894 break;
895 case RIGHT:
896 horizontalAdjustment += (viewR.width - u.width);
897 break;
898 case CENTER:
899 horizontalAdjustment += ((viewR.width/2) - (u.width/2));
900 break;
903 iconR.x += horizontalAdjustment;
904 iconR.y += verticalAdjustment;
906 textR.x += horizontalAdjustment;
907 textR.y += verticalAdjustment;
909 return text;
912 /**
913 * Calls {@link java.awt.EventQueue.invokeLater} with the
914 * specified {@link Runnable}.
916 public static void invokeLater(Runnable doRun)
918 java.awt.EventQueue.invokeLater(doRun);
921 /**
922 * Calls {@link java.awt.EventQueue.invokeAndWait} with the
923 * specified {@link Runnable}.
925 public static void invokeAndWait(Runnable doRun)
926 throws InterruptedException,
927 InvocationTargetException
929 java.awt.EventQueue.invokeAndWait(doRun);
932 /**
933 * Calls {@link java.awt.EventQueue.isEventDispatchThread}.
935 public static boolean isEventDispatchThread()
937 return java.awt.EventQueue.isDispatchThread();
941 * This method paints the given component at the given position and size.
942 * The component will be reparented to the container given.
944 * @param g The Graphics object to draw with.
945 * @param c The Component to draw
946 * @param p The Container to reparent to.
947 * @param x The x coordinate to draw at.
948 * @param y The y coordinate to draw at.
949 * @param w The width of the drawing area.
950 * @param h The height of the drawing area.
952 public static void paintComponent(Graphics g, Component c, Container p,
953 int x, int y, int w, int h)
955 Container parent = c.getParent();
956 if (parent != null)
957 parent.remove(c);
958 if (p != null)
959 p.add(c);
961 Shape savedClip = g.getClip();
963 g.setClip(x, y, w, h);
964 g.translate(x, y);
966 c.paint(g);
968 g.translate(-x, -y);
969 g.setClip(savedClip);
973 * This method paints the given component in the given rectangle.
974 * The component will be reparented to the container given.
976 * @param g The Graphics object to draw with.
977 * @param c The Component to draw
978 * @param p The Container to reparent to.
979 * @param r The rectangle that describes the drawing area.
981 public static void paintComponent(Graphics g, Component c,
982 Container p, Rectangle r)
984 paintComponent(g, c, p, r.x, r.y, r.width, r.height);
988 * This method returns the common Frame owner used in JDialogs or
989 * JWindow when no owner is provided.
991 * @return The common Frame
993 static Frame getOwnerFrame()
995 if (ownerFrame == null)
996 ownerFrame = new OwnerFrame();
997 return ownerFrame;
1001 * Checks if left mouse button was clicked.
1003 * @param event the event to check
1005 * @return true if left mouse was clicked, false otherwise.
1007 public static boolean isLeftMouseButton(MouseEvent event)
1009 return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK)
1010 == InputEvent.BUTTON1_DOWN_MASK);
1014 * Checks if middle mouse button was clicked.
1016 * @param event the event to check
1018 * @return true if middle mouse was clicked, false otherwise.
1020 public static boolean isMiddleMouseButton(MouseEvent event)
1022 return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
1023 == InputEvent.BUTTON2_DOWN_MASK);
1027 * Checks if right mouse button was clicked.
1029 * @param event the event to check
1031 * @return true if right mouse was clicked, false otherwise.
1033 public static boolean isRightMouseButton(MouseEvent event)
1035 return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
1036 == InputEvent.BUTTON3_DOWN_MASK);
1040 * This frame should be used when constructing a Window/JDialog without
1041 * a parent. In this case, we are forced to use this frame as a window's
1042 * parent, because we simply cannot pass null instead of parent to Window
1043 * constructor, since doing it will result in NullPointerException.
1045 private static class OwnerFrame extends Frame
1047 public void setVisible(boolean b)
1049 // Do nothing here.
1052 public boolean isShowing()
1054 return true;
1058 public static boolean notifyAction(Action action,
1059 KeyStroke ks,
1060 KeyEvent event,
1061 Object sender,
1062 int modifiers)
1064 if (action != null && action.isEnabled())
1066 String name = (String) action.getValue(Action.ACTION_COMMAND_KEY);
1067 if (name == null
1068 && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
1069 name = new String(new char[] {event.getKeyChar()});
1070 action.actionPerformed(new ActionEvent(sender,
1071 ActionEvent.ACTION_PERFORMED,
1072 name, modifiers));
1073 return true;
1075 return false;
1079 * <p>Change the shared, UI-managed {@link ActionMap} for a given
1080 * component. ActionMaps are arranged in a hierarchy, in order to
1081 * encourage sharing of common actions between components. The hierarchy
1082 * unfortunately places UI-managed ActionMaps at the <em>end</em> of the
1083 * parent-pointer chain, as illustrated:</p>
1085 * <pre>
1086 * [{@link javax.swing.JComponent#getActionMap()}]
1087 * --&gt; [{@link javax.swing.ActionMap}]
1088 * parent --&gt; [{@link javax.swing.text.KeymapActionMap}]
1089 * parent --&gt; [{@link javax.swing.plaf.ActionMapUIResource}]
1090 * </pre>
1092 * <p>Our goal with this method is to replace the first ActionMap along
1093 * this chain which is an instance of {@link ActionMapUIResource}, since
1094 * these are the ActionMaps which are supposed to be shared between
1095 * components.</p>
1097 * <p>If the provided ActionMap is <code>null</code>, we interpret the
1098 * call as a request to remove the UI-managed ActionMap from the
1099 * component's ActionMap parent chain.</p>
1101 public static void replaceUIActionMap(JComponent component,
1102 ActionMap uiActionMap)
1104 ActionMap child = component.getActionMap();
1105 if (child == null)
1106 component.setActionMap(uiActionMap);
1107 else
1109 while(child.getParent() != null
1110 && !(child.getParent() instanceof ActionMapUIResource))
1111 child = child.getParent();
1112 if (child != null)
1113 child.setParent(uiActionMap);
1118 * <p>Change the shared, UI-managed {@link InputMap} for a given
1119 * component. InputMaps are arranged in a hierarchy, in order to
1120 * encourage sharing of common input mappings between components. The
1121 * hierarchy unfortunately places UI-managed InputMaps at the
1122 * <em>end</em> of the parent-pointer chain, as illustrated:</p>
1124 * <pre>
1125 * [{@link javax.swing.JComponent#getInputMap()}]
1126 * --&gt; [{@link javax.swing.InputMap}]
1127 * parent --&gt; [{@link javax.swing.text.KeymapWrapper}]
1128 * parent --&gt; [{@link javax.swing.plaf.InputMapUIResource}]
1129 * </pre>
1131 * <p>Our goal with this method is to replace the first InputMap along
1132 * this chain which is an instance of {@link InputMapUIResource}, since
1133 * these are the InputMaps which are supposed to be shared between
1134 * components.</p>
1136 * <p>If the provided InputMap is <code>null</code>, we interpret the
1137 * call as a request to remove the UI-managed InputMap from the
1138 * component's InputMap parent chain.</p>
1140 public static void replaceUIInputMap(JComponent component,
1141 int condition,
1142 InputMap uiInputMap)
1144 InputMap child = component.getInputMap(condition);
1145 if (child == null)
1146 component.setInputMap(condition, uiInputMap);
1147 else
1149 while(child.getParent() != null
1150 && !(child.getParent() instanceof InputMapUIResource))
1151 child = child.getParent();
1152 if (child != null)
1153 child.setParent(uiInputMap);
1158 * Subtracts a rectangle from another and return the area as an array
1159 * of rectangles.
1160 * Returns the areas of rectA which are not covered by rectB.
1161 * If the rectangles do not overlap, or if either parameter is
1162 * <code>null</code>, a zero-size array is returned.
1163 * @param rectA The first rectangle
1164 * @param rectB The rectangle to subtract from the first
1165 * @return An array of rectangles representing the area in rectA
1166 * not overlapped by rectB
1168 public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
1170 if (rectA == null || rectB == null)
1171 return new Rectangle[0];
1173 Rectangle[] r = new Rectangle[4];
1174 int x1 = rectA.x;
1175 int y1 = rectA.y;
1176 int w1 = rectA.width;
1177 int h1 = rectA.height;
1178 int x2 = rectB.x;
1179 int y2 = rectB.y;
1180 int w2 = rectB.width;
1181 int h2 = rectB.height;
1183 // (outer box = rectA)
1184 // -------------
1185 // |_____0_____|
1186 // | |rectB| |
1187 // |_1|_____|_2|
1188 // | 3 |
1189 // -------------
1190 int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
1191 int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
1192 int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
1193 int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
1194 int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
1196 if (H0 > 0)
1197 r[0] = new Rectangle(x1, y1, w1, H0);
1198 else
1199 r[0] = null;
1201 if (W1 > 0 && H12 > 0)
1202 r[1] = new Rectangle(x1, y1 + H0, W1, H12);
1203 else
1204 r[1] = null;
1206 if (W2 > 0 && H12 > 0)
1207 r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
1208 else
1209 r[2] = null;
1211 if (H3 > 0)
1212 r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
1213 else
1214 r[3] = null;
1216 // sort out null objects
1217 int n = 0;
1218 for (int i = 0; i < 4; i++)
1219 if (r[i] != null)
1220 n++;
1221 Rectangle[] out = new Rectangle[n];
1222 for (int i = 3; i >= 0; i--)
1223 if (r[i] != null)
1224 out[--n] = r[i];
1226 return out;
1230 * Calculates the intersection of two rectangles.
1232 * @param x upper-left x coodinate of first rectangle
1233 * @param x upper-left y coodinate of first rectangle
1234 * @param w width of first rectangle
1235 * @param h height of first rectangle
1236 * @param rect a Rectangle object of the second rectangle
1237 * @throws a NullPointerException if rect is null.
1239 * @return a rectangle corresponding to the intersection of the
1240 * two rectangles. A zero rectangle is returned if the rectangles
1241 * do not overlap.
1243 public static Rectangle computeIntersection(int x, int y, int w, int h,
1244 Rectangle rect)
1246 int x2 = (int) rect.getX();
1247 int y2 = (int) rect.getY();
1248 int w2 = (int) rect.getWidth();
1249 int h2 = (int) rect.getHeight();
1251 int dx = (x > x2) ? x : x2;
1252 int dy = (y > y2) ? y : y2;
1253 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1254 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1256 if (dw >= 0 && dh >= 0)
1257 return new Rectangle(dx, dy, dw, dh);
1259 return new Rectangle(0, 0, 0, 0);
1263 * Calculates the width of a given string.
1265 * @param fm the <code>FontMetrics</code> object to use
1266 * @param str the string
1268 * @return the width of the the string.
1270 public static int computeStringWidth(FontMetrics fm, String str)
1272 return fm.stringWidth(str);
1276 * Calculates the union of two rectangles.
1278 * @param x upper-left x coodinate of first rectangle
1279 * @param x upper-left y coodinate of first rectangle
1280 * @param w width of first rectangle
1281 * @param h height of first rectangle
1282 * @param rect a Rectangle object of the second rectangle
1283 * @throws a NullPointerException if rect is null.
1285 * @return a rectangle corresponding to the union of the
1286 * two rectangles. A rectangle encompassing both is returned if the
1287 * rectangles do not overlap.
1289 public static Rectangle computeUnion(int x, int y, int w, int h,
1290 Rectangle rect)
1292 int x2 = (int) rect.getX();
1293 int y2 = (int) rect.getY();
1294 int w2 = (int) rect.getWidth();
1295 int h2 = (int) rect.getHeight();
1297 int dx = (x < x2) ? x : x2;
1298 int dy = (y < y2) ? y : y2;
1299 int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1300 int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1302 if (dw >= 0 && dh >= 0)
1303 return new Rectangle(dx, dy, dw, dh);
1305 return new Rectangle(0, 0, 0, 0);
1309 * Tests if a rectangle contains another.
1310 * @param a first rectangle
1311 * @param b second rectangle
1312 * @return true if a contains b, false otherwise
1313 * @throws NullPointerException
1315 public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
1317 // Note: zero-size rects inclusive, differs from Rectangle.contains()
1318 return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
1319 && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
1320 && b.y + b.height <= a.y + a.height;