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)
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
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
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. */
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;
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
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()
88 * Calculates the portion of the component's bounds which is inside the
89 * component's border insets. This area is usually the area a component
90 * should confine its painting to. The coordinates are returned in terms
91 * of the <em>component's</em> coordinate system, where (0,0) is the
92 * upper left corner of the component's bounds.
94 * @param c The component to measure the bounds of
95 * @param r A Rectangle to store the return value in, or
98 * @return The calculated area inside the component and its border
101 public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
103 Rectangle b = getLocalBounds(c);
106 Insets i = c.getInsets();
108 r.width = b.width - i.left - i.right;
110 r.height = b.height - i.top - i.bottom;
115 * Returns the focus owner or <code>null</code> if <code>comp</code> is not
116 * the focus owner or a parent of it.
118 * @param comp the focus owner or a parent of it
120 * @return the focus owner, or <code>null</code>
122 * @deprecated 1.4 Replaced by
123 * <code>KeyboardFocusManager.getFocusOwner()</code>.
125 public static Component findFocusOwner(Component comp)
127 // Get real focus owner.
128 Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
131 // Check if comp is the focus owner or a parent of it.
132 Component tmp = focusOwner;
139 tmp = tmp.getParent();
146 * Returns the <code>Accessible</code> child of the specified component
147 * which appears at the supplied <code>Point</code>. If there is no
148 * child located at that particular pair of co-ordinates, null is returned
151 * @param c the component whose children may be found at the specified
153 * @param p the point at which to look for the existence of children
154 * of the specified component.
155 * @return the <code>Accessible</code> child at the point, <code>p</code>,
156 * or null if there is no child at this point.
157 * @see javax.accessibility.AccessibleComponent#getAccessibleAt
159 public static Accessible getAccessibleAt(Component c, Point p)
161 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
166 * Returns the <code>Accessible</code> child of the specified component
167 * that has the supplied index within the parent component. The indexing
168 * of the children is zero-based, making the first child have an index of
172 * Caution is advised when using this method, as its operation relies
173 * on the behaviour of varying implementations of an abstract method.
174 * For greater surety, direct use of the AWT component implementation
175 * of this method is advised.
178 * @param c the component whose child should be returned.
179 * @param i the index of the child within the parent component.
180 * @return the <code>Accessible</code> child at index <code>i</code>
181 * in the component, <code>c</code>.
182 * @see javax.accessibility.AccessibleContext#getAccessibleChild
183 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild
185 public static Accessible getAccessibleChild(Component c, int i)
187 return c.getAccessibleContext().getAccessibleChild(i);
192 * Returns the number of <code>Accessible</code> children within
193 * the supplied component.
196 * Caution is advised when using this method, as its operation relies
197 * on the behaviour of varying implementations of an abstract method.
198 * For greater surety, direct use of the AWT component implementation
199 * of this method is advised.
202 * @param c the component whose children should be counted.
203 * @return the number of children belonging to the component,
205 * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount
206 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount
208 public static int getAccessibleChildrenCount(Component c)
210 return c.getAccessibleContext().getAccessibleChildrenCount();
215 * Returns the zero-based index of the specified component
216 * within its parent. If the component doesn't have a parent,
220 * Caution is advised when using this method, as its operation relies
221 * on the behaviour of varying implementations of an abstract method.
222 * For greater surety, direct use of the AWT component implementation
223 * of this method is advised.
226 * @param c the component whose parental index should be found.
227 * @return the index of the component within its parent, or -1
228 * if the component doesn't have a parent.
229 * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent
230 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent
232 public static int getAccessibleIndexInParent(Component c)
234 return c.getAccessibleContext().getAccessibleIndexInParent();
239 * Returns a set of <code>AccessibleState</code>s, which represent
240 * the state of the supplied component.
243 * Caution is advised when using this method, as its operation relies
244 * on the behaviour of varying implementations of an abstract method.
245 * For greater surety, direct use of the AWT component implementation
246 * of this method is advised.
249 * @param c the component whose accessible state should be retrieved.
250 * @return a set of <code>AccessibleState</code> objects, which represent
251 * the state of the supplied component.
252 * @see javax.accessibility.AccessibleContext#getAccessibleStateSet
253 * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet
255 public static AccessibleStateSet getAccessibleStateSet(Component c)
257 return c.getAccessibleContext().getAccessibleStateSet();
261 * Calculates the bounds of a component in the component's own coordinate
262 * space. The result has the same height and width as the component's
263 * bounds, but its location is set to (0,0).
265 * @param aComponent The component to measure
267 * @return The component's bounds in its local coordinate space
269 public static Rectangle getLocalBounds(Component aComponent)
271 Rectangle bounds = aComponent.getBounds();
272 return new Rectangle(0, 0, bounds.width, bounds.height);
276 * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
277 * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
279 * @param comp The component to get the JRootPane of
281 * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
283 * @see javax.swing.RootPaneContainer#getRootPane
284 * @see #getAncestorOfClass
286 public static JRootPane getRootPane(Component comp)
288 if (comp instanceof RootPaneContainer)
289 return ((RootPaneContainer)comp).getRootPane();
291 return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
295 * Returns the least ancestor of <code>comp</code> which has the
298 * @param name The name to search for
299 * @param comp The component to search the ancestors of
301 * @return The nearest ancestor of <code>comp</code> with the given
302 * name, or <code>null</code> if no such ancestor exists
304 * @see java.awt.Component#getName
305 * @see #getAncestorOfClass
307 public static Container getAncestorNamed(String name, Component comp)
309 while (comp != null && (comp.getName() != name))
310 comp = comp.getParent();
311 return (Container) comp;
315 * Returns the least ancestor of <code>comp</code> which is an instance
316 * of the specified class.
318 * @param c The class to search for
319 * @param comp The component to search the ancestors of
321 * @return The nearest ancestor of <code>comp</code> which is an instance
322 * of the given class, or <code>null</code> if no such ancestor exists
324 * @see #getAncestorOfClass
325 * @see #windowForComponent
327 public static Container getAncestorOfClass(Class c, Component comp)
329 while (comp != null && (! c.isInstance(comp)))
330 comp = comp.getParent();
331 return (Container) comp;
335 * Returns the first ancestor of <code>comp</code> that is a {@link Window}
336 * or <code>null</code> if <code>comp</code> is not contained in a
339 * This is equivalent to calling
340 * <code>getAncestorOfClass(Window, comp)</code> or
341 * <code>windowForComponent(comp)</code>.
343 * @param comp the component for which we are searching the ancestor Window
345 * @return the first ancestor Window of <code>comp</code> or
346 * <code>null</code> if <code>comp</code> is not contained in a Window
348 public static Window getWindowAncestor(Component comp)
350 return (Window) getAncestorOfClass(Window.class, comp);
354 * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
356 * @param comp The component to search for an ancestor window
358 * @return An ancestral window, or <code>null</code> if none exists
360 public static Window windowForComponent(Component comp)
362 return (Window) getAncestorOfClass(Window.class, comp);
366 * Returns the "root" of the component tree containint <code>comp</code>
367 * The root is defined as either the <em>least</em> ancestor of
368 * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
369 * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
370 * Window} ancestors are found.
372 * @param comp The component to search for a root
374 * @return The root of the component's tree, or <code>null</code>
376 public static Component getRoot(Component comp)
383 if (win == null && comp instanceof Window)
385 else if (comp instanceof Applet)
387 comp = comp.getParent();
397 * Return true if a descends from b, in other words if b is an
400 * @param a The child to search the ancestry of
401 * @param b The potential ancestor to search for
403 * @return true if a is a descendent of b, false otherwise
405 public static boolean isDescendingFrom(Component a, Component b)
409 if (a == null || b == null)
418 * Returns the deepest descendent of parent which is both visible and
419 * contains the point <code>(x,y)</code>. Returns parent when either
420 * parent is not a container, or has no children which contain
421 * <code>(x,y)</code>. Returns <code>null</code> when either
422 * <code>(x,y)</code> is outside the bounds of parent, or parent is
425 * @param parent The component to search the descendents of
426 * @param x Horizontal coordinate to search for
427 * @param y Vertical coordinate to search for
429 * @return A component containing <code>(x,y)</code>, or
432 * @see java.awt.Container#findComponentAt(int, int)
434 public static Component getDeepestComponentAt(Component parent, int x, int y)
436 if (parent == null || (! parent.contains(x, y)))
439 if (! (parent instanceof Container))
442 Container c = (Container) parent;
443 return c.findComponentAt(x, y);
447 * Converts a point from a component's local coordinate space to "screen"
448 * coordinates (such as the coordinate space mouse events are delivered
449 * in). This operation is equivalent to translating the point by the
450 * location of the component (which is the origin of its coordinate
453 * @param p The point to convert
454 * @param c The component which the point is expressed in terms of
456 * @see #convertPointFromScreen
458 public static void convertPointToScreen(Point p, Component c)
460 Point c0 = c.getLocationOnScreen();
461 p.translate(c0.x, c0.y);
465 * Converts a point from "screen" coordinates (such as the coordinate
466 * space mouse events are delivered in) to a component's local coordinate
467 * space. This operation is equivalent to translating the point by the
468 * negation of the component's location (which is the origin of its
471 * @param p The point to convert
472 * @param c The component which the point should be expressed in terms of
474 public static void convertPointFromScreen(Point p, Component c)
476 Point c0 = c.getLocationOnScreen();
477 p.translate(-c0.x, -c0.y);
481 * Converts a point <code>(x,y)</code> from the coordinate space of one
482 * component to another. This is equivalent to converting the point from
483 * <code>source</code> space to screen space, then back from screen space
484 * to <code>destination</code> space. If exactly one of the two
485 * Components is <code>null</code>, it is taken to refer to the root
486 * ancestor of the other component. If both are <code>null</code>, no
487 * transformation is done.
489 * @param source The component which the point is expressed in terms of
490 * @param x Horizontal coordinate of point to transform
491 * @param y Vertical coordinate of point to transform
492 * @param destination The component which the return value will be
493 * expressed in terms of
495 * @return The point <code>(x,y)</code> converted from the coordinate space of the
496 * source component to the coordinate space of the destination component
498 * @see #convertPointToScreen
499 * @see #convertPointFromScreen
500 * @see #convertRectangle
503 public static Point convertPoint(Component source, int x, int y,
504 Component destination)
506 Point pt = new Point(x, y);
508 if (source == null && destination == null)
512 source = getRoot(destination);
514 if (destination == null)
515 destination = getRoot(source);
517 if (source.isShowing() && destination.isShowing())
519 convertPointToScreen(pt, source);
520 convertPointFromScreen(pt, destination);
526 public static Point convertPoint(Component source, Point aPoint, Component destination)
528 return convertPoint(source, aPoint.x, aPoint.y, destination);
532 * Converts a rectangle from the coordinate space of one component to
533 * another. This is equivalent to converting the rectangle from
534 * <code>source</code> space to screen space, then back from screen space
535 * to <code>destination</code> space. If exactly one of the two
536 * Components is <code>null</code>, it is taken to refer to the root
537 * ancestor of the other component. If both are <code>null</code>, no
538 * transformation is done.
540 * @param source The component which the rectangle is expressed in terms of
541 * @param rect The rectangle to convert
542 * @param destination The component which the return value will be
543 * expressed in terms of
545 * @return A new rectangle, equal in size to the input rectangle, but
546 * with its position converted from the coordinate space of the source
547 * component to the coordinate space of the destination component
549 * @see #convertPointToScreen
550 * @see #convertPointFromScreen
551 * @see #convertPoint(Component, int, int, Component)
554 public static Rectangle convertRectangle(Component source,
556 Component destination)
558 Point pt = convertPoint(source, rect.x, rect.y, destination);
559 return new Rectangle(pt.x, pt.y, rect.width, rect.height);
563 * Convert a mouse event which refrers to one component to another. This
564 * includes changing the mouse event's coordinate space, as well as the
565 * source property of the event. If <code>source</code> is
566 * <code>null</code>, it is taken to refer to <code>destination</code>'s
567 * root component. If <code>destination</code> is <code>null</code>, the
568 * new event will remain expressed in <code>source</code>'s coordinate
571 * @param source The component the mouse event currently refers to
572 * @param sourceEvent The mouse event to convert
573 * @param destination The component the new mouse event should refer to
575 * @return A new mouse event expressed in terms of the destination
576 * component's coordinate space, and with the destination component as
579 * @see #convertPoint(Component, int, int, Component)
581 public static MouseEvent convertMouseEvent(Component source,
582 MouseEvent sourceEvent,
583 Component destination)
585 Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
588 return new MouseEvent(destination, sourceEvent.getID(),
589 sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
590 newpt.x, newpt.y, sourceEvent.getClickCount(),
591 sourceEvent.isPopupTrigger(), sourceEvent.getButton());
595 * Recursively walk the component tree under <code>comp</code> calling
596 * <code>updateUI</code> on each {@link JComponent} found. This causes
597 * the entire tree to re-initialize its UI delegates.
599 * @param comp The component to walk the children of, calling <code>updateUI</code>
601 public static void updateComponentTreeUI(Component comp)
606 if (comp instanceof Container)
608 Component[] children = ((Container)comp).getComponents();
609 for (int i = 0; i < children.length; ++i)
610 updateComponentTreeUI(children[i]);
613 if (comp instanceof JComponent)
614 ((JComponent)comp).updateUI();
619 * <p>Layout a "compound label" consisting of a text string and an icon
620 * which is to be placed near the rendered text. Once the text and icon
621 * are laid out, the text rectangle and icon rectangle parameters are
622 * altered to store the calculated positions.</p>
624 * <p>The size of the text is calculated from the provided font metrics
625 * object. This object should be the metrics of the font you intend to
626 * paint the label with.</p>
628 * <p>The position values control where the text is placed relative to
629 * the icon. The horizontal position value should be one of the constants
630 * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>,
631 * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value
632 * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code>
633 * or <code>CENTER</code>.</p>
635 * <p>The text-icon gap value controls the number of pixels between the
636 * icon and the text.</p>
638 * <p>The alignment values control where the text and icon are placed, as
639 * a combined unit, within the view rectangle. The horizontal alignment
640 * value should be one of the constants <code>LEADING</code>,
641 * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
642 * <code>CENTER</code>. The vertical alignment valus should be one of the
643 * constants <code>TOP</code>, <code>BOTTOM</code> or
644 * <code>CENTER</code>.</p>
646 * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
647 * given for horizontal alignment or horizontal text position, they are
648 * interpreted relative to the provided component's orientation property,
649 * a constant in the {@link java.awt.ComponentOrientation} class. For
650 * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>,
651 * then the <code>LEADING</code> value is a synonym for <code>LEFT</code>
652 * and the <code>TRAILING</code> value is a synonym for
653 * <code>RIGHT</code></p>
655 * <p>If the text and icon are equal to or larger than the view
656 * rectangle, the horizontal and vertical alignment values have no
659 * @param c A component used for its orientation value
660 * @param fm The font metrics used to measure the text
661 * @param text The text to place in the compound label
662 * @param icon The icon to place next to the text
663 * @param verticalAlignment The vertical alignment of the label relative
665 * @param horizontalAlignment The horizontal alignment of the label
666 * relative to its component
667 * @param verticalTextPosition The vertical position of the label's text
668 * relative to its icon
669 * @param horizontalTextPosition The horizontal position of the label's
670 * text relative to its icon
671 * @param viewR The view rectangle, specifying the area which layout is
673 * @param iconR A rectangle which is modified to hold the laid-out
674 * position of the icon
675 * @param textR A rectangle which is modified to hold the laid-out
676 * position of the text
677 * @param textIconGap The distance between text and icon
679 * @return The string of characters, possibly truncated with an elipsis,
680 * which is laid out in this label
683 public static String layoutCompoundLabel(JComponent c,
687 int verticalAlignment,
688 int horizontalAlignment,
689 int verticalTextPosition,
690 int horizontalTextPosition,
697 // Fix up the orientation-based horizontal positions.
699 if (horizontalTextPosition == LEADING)
701 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
702 horizontalTextPosition = RIGHT;
704 horizontalTextPosition = LEFT;
706 else if (horizontalTextPosition == TRAILING)
708 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
709 horizontalTextPosition = LEFT;
711 horizontalTextPosition = RIGHT;
714 // Fix up the orientation-based alignments.
716 if (horizontalAlignment == LEADING)
718 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
719 horizontalAlignment = RIGHT;
721 horizontalAlignment = LEFT;
723 else if (horizontalAlignment == TRAILING)
725 if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
726 horizontalAlignment = LEFT;
728 horizontalAlignment = RIGHT;
731 return layoutCompoundLabel(fm, text, icon,
734 verticalTextPosition,
735 horizontalTextPosition,
736 viewR, iconR, textR, textIconGap);
740 * <p>Layout a "compound label" consisting of a text string and an icon
741 * which is to be placed near the rendered text. Once the text and icon
742 * are laid out, the text rectangle and icon rectangle parameters are
743 * altered to store the calculated positions.</p>
745 * <p>The size of the text is calculated from the provided font metrics
746 * object. This object should be the metrics of the font you intend to
747 * paint the label with.</p>
749 * <p>The position values control where the text is placed relative to
750 * the icon. The horizontal position value should be one of the constants
751 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
752 * vertical position value should be one fo the constants
753 * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
755 * <p>The text-icon gap value controls the number of pixels between the
756 * icon and the text.</p>
758 * <p>The alignment values control where the text and icon are placed, as
759 * a combined unit, within the view rectangle. The horizontal alignment
760 * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
761 * <code>CENTER</code>. The vertical alignment valus should be one of the
762 * constants <code>TOP</code>, <code>BOTTOM</code> or
763 * <code>CENTER</code>.</p>
765 * <p>If the text and icon are equal to or larger than the view
766 * rectangle, the horizontal and vertical alignment values have no
769 * <p>Note that this method does <em>not</em> know how to deal with
770 * horizontal alignments or positions given as <code>LEADING</code> or
771 * <code>TRAILING</code> values. Use the other overloaded variant of this
772 * method if you wish to use such values.
774 * @param fm The font metrics used to measure the text
775 * @param text The text to place in the compound label
776 * @param icon The icon to place next to the text
777 * @param verticalAlignment The vertical alignment of the label relative
779 * @param horizontalAlignment The horizontal alignment of the label
780 * relative to its component
781 * @param verticalTextPosition The vertical position of the label's text
782 * relative to its icon
783 * @param horizontalTextPosition The horizontal position of the label's
784 * text relative to its icon
785 * @param viewR The view rectangle, specifying the area which layout is
787 * @param iconR A rectangle which is modified to hold the laid-out
788 * position of the icon
789 * @param textR A rectangle which is modified to hold the laid-out
790 * position of the text
791 * @param textIconGap The distance between text and icon
793 * @return The string of characters, possibly truncated with an elipsis,
794 * which is laid out in this label
797 public static String layoutCompoundLabel(FontMetrics fm,
800 int verticalAlignment,
801 int horizontalAlignment,
802 int verticalTextPosition,
803 int horizontalTextPosition,
810 // Work out basic height and width.
820 iconR.width = icon.getIconWidth();
821 iconR.height = icon.getIconHeight();
823 if (text == null || text.equals(""))
832 textR.width = fm.stringWidth(text);
833 textR.height = fm.getHeight();
834 while (text.indexOf('\n', fromIndex) != -1)
836 textR.height += fm.getHeight();
837 fromIndex = text.indexOf('\n', fromIndex) + 1;
841 // Work out the position of text and icon, assuming the top-left coord
842 // starts at (0,0). We will fix that up momentarily, after these
843 // "position" decisions are made and we look at alignment.
845 switch (horizontalTextPosition)
849 iconR.x = textR.width + textIconGap;
853 textR.x = iconR.width + textIconGap;
856 int centerLine = Math.max(textR.width, iconR.width) / 2;
857 textR.x = centerLine - textR.width/2;
858 iconR.x = centerLine - iconR.width/2;
862 switch (verticalTextPosition)
866 iconR.y = (horizontalTextPosition == CENTER
867 ? textR.height + textIconGap : 0);
871 textR.y = (horizontalTextPosition == CENTER
872 ? iconR.height + textIconGap
873 : Math.max(iconR.height - textR.height, 0));
876 int centerLine = Math.max(textR.height, iconR.height) / 2;
877 textR.y = centerLine - textR.height/2;
878 iconR.y = centerLine - iconR.height/2;
881 // The two rectangles are laid out correctly now, but only assuming
882 // that their upper left corner is at (0,0). If we have any alignment other
883 // than TOP and LEFT, we need to adjust them.
885 Rectangle u = textR.union(iconR);
886 int horizontalAdjustment = viewR.x;
887 int verticalAdjustment = viewR.y;
888 switch (verticalAlignment)
893 verticalAdjustment += (viewR.height - u.height);
896 verticalAdjustment += ((viewR.height/2) - (u.height/2));
899 switch (horizontalAlignment)
904 horizontalAdjustment += (viewR.width - u.width);
907 horizontalAdjustment += ((viewR.width/2) - (u.width/2));
911 iconR.x += horizontalAdjustment;
912 iconR.y += verticalAdjustment;
914 textR.x += horizontalAdjustment;
915 textR.y += verticalAdjustment;
921 * Calls {@link java.awt.EventQueue#invokeLater} with the
922 * specified {@link Runnable}.
924 public static void invokeLater(Runnable doRun)
926 java.awt.EventQueue.invokeLater(doRun);
930 * Calls {@link java.awt.EventQueue#invokeAndWait} with the
931 * specified {@link Runnable}.
933 public static void invokeAndWait(Runnable doRun)
934 throws InterruptedException,
935 InvocationTargetException
937 java.awt.EventQueue.invokeAndWait(doRun);
941 * Calls {@link java.awt.EventQueue#isDispatchThread()}.
943 * @return <code>true</code> if the current thread is the current AWT event
946 public static boolean isEventDispatchThread()
948 return java.awt.EventQueue.isDispatchThread();
952 * This method paints the given component at the given position and size.
953 * The component will be reparented to the container given.
955 * @param g The Graphics object to draw with.
956 * @param c The Component to draw
957 * @param p The Container to reparent to.
958 * @param x The x coordinate to draw at.
959 * @param y The y coordinate to draw at.
960 * @param w The width of the drawing area.
961 * @param h The height of the drawing area.
963 public static void paintComponent(Graphics g, Component c, Container p,
964 int x, int y, int w, int h)
966 Container parent = c.getParent();
972 Shape savedClip = g.getClip();
974 g.setClip(x, y, w, h);
980 g.setClip(savedClip);
984 * This method paints the given component in the given rectangle.
985 * The component will be reparented to the container given.
987 * @param g The Graphics object to draw with.
988 * @param c The Component to draw
989 * @param p The Container to reparent to.
990 * @param r The rectangle that describes the drawing area.
992 public static void paintComponent(Graphics g, Component c,
993 Container p, Rectangle r)
995 paintComponent(g, c, p, r.x, r.y, r.width, r.height);
999 * This method returns the common Frame owner used in JDialogs or
1000 * JWindow when no owner is provided.
1002 * @return The common Frame
1004 static Window getOwnerFrame(Window owner)
1006 Window result = owner;
1009 if (ownerFrame == null)
1010 ownerFrame = new OwnerFrame();
1011 result = ownerFrame;
1017 * Checks if left mouse button was clicked.
1019 * @param event the event to check
1021 * @return true if left mouse was clicked, false otherwise.
1023 public static boolean isLeftMouseButton(MouseEvent event)
1025 return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK)
1026 == InputEvent.BUTTON1_DOWN_MASK);
1030 * Checks if middle mouse button was clicked.
1032 * @param event the event to check
1034 * @return true if middle mouse was clicked, false otherwise.
1036 public static boolean isMiddleMouseButton(MouseEvent event)
1038 return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
1039 == InputEvent.BUTTON2_DOWN_MASK);
1043 * Checks if right mouse button was clicked.
1045 * @param event the event to check
1047 * @return true if right mouse was clicked, false otherwise.
1049 public static boolean isRightMouseButton(MouseEvent event)
1051 return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
1052 == InputEvent.BUTTON3_DOWN_MASK);
1056 * This frame should be used when constructing a Window/JDialog without
1057 * a parent. In this case, we are forced to use this frame as a window's
1058 * parent, because we simply cannot pass null instead of parent to Window
1059 * constructor, since doing it will result in NullPointerException.
1061 private static class OwnerFrame extends Frame
1063 public void setVisible(boolean b)
1068 public boolean isShowing()
1074 public static boolean notifyAction(Action action,
1080 if (action != null && action.isEnabled())
1082 String name = (String) action.getValue(Action.ACTION_COMMAND_KEY);
1084 && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
1085 name = new String(new char[] {event.getKeyChar()});
1086 action.actionPerformed(new ActionEvent(sender,
1087 ActionEvent.ACTION_PERFORMED,
1095 * <p>Change the shared, UI-managed {@link ActionMap} for a given
1096 * component. ActionMaps are arranged in a hierarchy, in order to
1097 * encourage sharing of common actions between components. The hierarchy
1098 * unfortunately places UI-managed ActionMaps at the <em>end</em> of the
1099 * parent-pointer chain, as illustrated:</p>
1102 * [{@link javax.swing.JComponent#getActionMap()}]
1103 * --> [{@link javax.swing.ActionMap}]
1104 * parent --> [{@link javax.swing.text.JTextComponent.KeymapActionMap}]
1105 * parent --> [{@link javax.swing.plaf.ActionMapUIResource}]
1108 * <p>Our goal with this method is to replace the first ActionMap along
1109 * this chain which is an instance of {@link ActionMapUIResource}, since
1110 * these are the ActionMaps which are supposed to be shared between
1113 * <p>If the provided ActionMap is <code>null</code>, we interpret the
1114 * call as a request to remove the UI-managed ActionMap from the
1115 * component's ActionMap parent chain.</p>
1117 public static void replaceUIActionMap(JComponent component,
1118 ActionMap uiActionMap)
1120 ActionMap child = component.getActionMap();
1122 component.setActionMap(uiActionMap);
1125 ActionMap parent = child.getParent();
1126 while (parent != null && !(parent instanceof ActionMapUIResource))
1129 parent = child.getParent();
1131 child.setParent(uiActionMap);
1136 * <p>Change the shared, UI-managed {@link InputMap} for a given
1137 * component. InputMaps are arranged in a hierarchy, in order to
1138 * encourage sharing of common input mappings between components. The
1139 * hierarchy unfortunately places UI-managed InputMaps at the
1140 * <em>end</em> of the parent-pointer chain, as illustrated:</p>
1143 * [{@link javax.swing.JComponent#getInputMap()}]
1144 * --> [{@link javax.swing.InputMap}]
1145 * parent --> [{@link javax.swing.text.JTextComponent.KeymapWrapper}]
1146 * parent --> [{@link javax.swing.plaf.InputMapUIResource}]
1149 * <p>Our goal with this method is to replace the first InputMap along
1150 * this chain which is an instance of {@link InputMapUIResource}, since
1151 * these are the InputMaps which are supposed to be shared between
1154 * <p>If the provided InputMap is <code>null</code>, we interpret the
1155 * call as a request to remove the UI-managed InputMap from the
1156 * component's InputMap parent chain.</p>
1158 public static void replaceUIInputMap(JComponent component,
1160 InputMap uiInputMap)
1162 InputMap child = component.getInputMap(condition);
1164 component.setInputMap(condition, uiInputMap);
1167 InputMap parent = child.getParent();
1168 while (parent != null && !(parent instanceof InputMapUIResource))
1171 parent = parent.getParent();
1173 child.setParent(uiInputMap);
1178 * Subtracts a rectangle from another and return the area as an array
1180 * Returns the areas of rectA which are not covered by rectB.
1181 * If the rectangles do not overlap, or if either parameter is
1182 * <code>null</code>, a zero-size array is returned.
1183 * @param rectA The first rectangle
1184 * @param rectB The rectangle to subtract from the first
1185 * @return An array of rectangles representing the area in rectA
1186 * not overlapped by rectB
1188 public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
1190 if (rectA == null || rectB == null)
1191 return new Rectangle[0];
1193 Rectangle[] r = new Rectangle[4];
1196 int w1 = rectA.width;
1197 int h1 = rectA.height;
1200 int w2 = rectB.width;
1201 int h2 = rectB.height;
1203 // (outer box = rectA)
1210 int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
1211 int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
1212 int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
1213 int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
1214 int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
1217 r[0] = new Rectangle(x1, y1, w1, H0);
1221 if (W1 > 0 && H12 > 0)
1222 r[1] = new Rectangle(x1, y1 + H0, W1, H12);
1226 if (W2 > 0 && H12 > 0)
1227 r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
1232 r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
1236 // sort out null objects
1238 for (int i = 0; i < 4; i++)
1241 Rectangle[] out = new Rectangle[n];
1242 for (int i = 3; i >= 0; i--)
1250 * Calculates the intersection of two rectangles. The result is stored
1251 * in <code>rect</code>. This is basically the same
1252 * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1253 * create new Rectangle instances. The tradeoff is that you loose any data in
1254 * <code>rect</code>.
1256 * @param x upper-left x coodinate of first rectangle
1257 * @param y upper-left y coodinate of first rectangle
1258 * @param w width of first rectangle
1259 * @param h height of first rectangle
1260 * @param rect a Rectangle object of the second rectangle
1262 * @throws NullPointerException if rect is null
1264 * @return a rectangle corresponding to the intersection of the
1265 * two rectangles. An empty rectangle is returned if the rectangles
1268 public static Rectangle computeIntersection(int x, int y, int w, int h,
1271 int x2 = (int) rect.x;
1272 int y2 = (int) rect.y;
1273 int w2 = (int) rect.width;
1274 int h2 = (int) rect.height;
1276 int dx = (x > x2) ? x : x2;
1277 int dy = (y > y2) ? y : y2;
1278 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1279 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1281 if (dw >= 0 && dh >= 0)
1282 rect.setBounds(dx, dy, dw, dh);
1284 rect.setBounds(0, 0, 0, 0);
1290 * Calculates the width of a given string.
1292 * @param fm the <code>FontMetrics</code> object to use
1293 * @param str the string
1295 * @return the width of the the string.
1297 public static int computeStringWidth(FontMetrics fm, String str)
1299 return fm.stringWidth(str);
1303 * Calculates the union of two rectangles. The result is stored in
1304 * <code>rect</code>. This is basically the same as
1305 * {@link Rectangle#union(Rectangle)} except that it avoids creation of new
1306 * Rectangle objects. The tradeoff is that you loose any data in
1307 * <code>rect</code>.
1309 * @param x upper-left x coodinate of first rectangle
1310 * @param y upper-left y coodinate of first rectangle
1311 * @param w width of first rectangle
1312 * @param h height of first rectangle
1313 * @param rect a Rectangle object of the second rectangle
1315 * @throws NullPointerException if rect is null
1317 * @return a rectangle corresponding to the union of the
1318 * two rectangles; a rectangle encompassing both is returned if the
1319 * rectangles do not overlap
1321 public static Rectangle computeUnion(int x, int y, int w, int h,
1324 int x2 = (int) rect.x;
1325 int y2 = (int) rect.y;
1326 int w2 = (int) rect.width;
1327 int h2 = (int) rect.height;
1329 int dx = (x < x2) ? x : x2;
1330 int dy = (y < y2) ? y : y2;
1331 int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1332 int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1334 if (dw >= 0 && dh >= 0)
1335 rect.setBounds(dx, dy, dw, dh);
1337 rect.setBounds(0, 0, 0, 0);
1342 * Tests if a rectangle contains another.
1343 * @param a first rectangle
1344 * @param b second rectangle
1345 * @return true if a contains b, false otherwise
1346 * @throws NullPointerException
1348 public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
1350 // Note: zero-size rects inclusive, differs from Rectangle.contains()
1351 return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
1352 && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
1353 && b.y + b.height <= a.y + a.height;
1357 * Returns the InputMap that is provided by the ComponentUI of
1358 * <code>component</code> for the specified condition.
1360 * @param component the component for which the InputMap is returned
1361 * @param cond the condition that specifies which of the three input
1362 * maps should be returned, may be
1363 * {@link JComponent#WHEN_IN_FOCUSED_WINDOW},
1364 * {@link JComponent#WHEN_FOCUSED} or
1365 * {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}
1367 * @return The input map.
1369 public static InputMap getUIInputMap(JComponent component, int cond)
1371 if (UIManager.getUI(component) != null)
1372 // we assume here that the UI class sets the parent of the component's
1373 // InputMap, which is the correct behaviour. If it's not, then
1374 // this can be considered a bug
1375 return component.getInputMap(cond).getParent();
1381 * Returns the ActionMap that is provided by the ComponentUI of
1382 * <code>component</code>.
1384 * @param component the component for which the ActionMap is returned
1386 public static ActionMap getUIActionMap(JComponent component)
1388 if (UIManager.getUI(component) != null)
1389 // we assume here that the UI class sets the parent of the component's
1390 // ActionMap, which is the correct behaviour. If it's not, then
1391 // this can be considered a bug
1392 return component.getActionMap().getParent();
1398 * Processes key bindings for the component that is associated with the
1399 * key event. Note that this method does not make sense for
1400 * JComponent-derived components, except when
1401 * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is
1404 * This method searches through the component hierarchy of the component's
1405 * top-level container to find a <code>JComponent</code> that has a binding
1406 * for the key event in the WHEN_IN_FOCUSED_WINDOW scope.
1408 * @param ev the key event
1410 * @return <code>true</code> if a binding has been found and processed,
1411 * <code>false</code> otherwise
1415 public static boolean processKeyBindings(KeyEvent ev)
1417 Component c = ev.getComponent();
1418 KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev);
1419 KeyboardManager km = KeyboardManager.getManager();
1420 return km.processKeyStroke(c, s, ev);