OSDN Git Service

2005-04-19 Roman Kennke <roman@kennke.org>
[pf3gnuchains/gcc-fork.git] / libjava / javax / swing / SwingUtilities.java
1 /* SwingUtilities.java --
2    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing;
40
41 import java.applet.Applet;
42 import java.awt.Component;
43 import java.awt.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;
59
60 import javax.accessibility.Accessible;
61 import javax.accessibility.AccessibleStateSet;
62 import javax.swing.plaf.ActionMapUIResource;
63 import javax.swing.plaf.InputMapUIResource;
64
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.
69  *
70  * @author Graydon Hoare (graydon@redhat.com)
71  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
72  */
73 public class SwingUtilities
74   implements SwingConstants
75 {
76   /** 
77    * This frame should be used as parent for JWindow or JDialog 
78    * that doesn't an owner
79    */
80   private static OwnerFrame ownerFrame;
81
82   private SwingUtilities()
83   {
84     // Do nothing.
85   }
86   
87   /**
88    * Calculates the portion of the base rectangle which is inside the
89    * insets.
90    *
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>
95    *
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>
98    *
99    * @see #calculateInnerArea
100    */
101   public static Rectangle calculateInsetArea(Rectangle base, Insets insets,
102                                              Rectangle ret)
103   {
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;
110   }
111
112   /**
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.
118    *
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>
122    *
123    * @return The calculated area inside the component and its border
124    * insets
125    *
126    * @see #calculateInsetArea
127    */
128   public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
129   {
130     Rectangle b = getLocalBounds(c);
131     return calculateInsetArea(b, c.getInsets(), r);
132   }
133
134   /**
135    * Returns the focus owner or <code>null</code> if <code>comp</code> is not
136    * the focus owner or a parent of it.
137    * 
138    * @param comp the focus owner or a parent of it
139    * 
140    * @return the focus owner, or <code>null</code>
141    * 
142    * @deprecated 1.4 Replaced by
143    * <code>KeyboardFocusManager.getFocusOwner()</code>.
144    */
145   public static Component findFocusOwner(Component comp)
146   {
147     // Get real focus owner.
148     Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
149                                                .getFocusOwner();
150
151     // Check if comp is the focus owner or a parent of it.
152     Component tmp = focusOwner;
153     
154     while (tmp != null)
155       {
156         if (tmp == comp)
157           return focusOwner;
158
159         tmp = tmp.getParent();
160       }
161     
162     return null;
163   }
164   
165   /**
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.
170    *
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
178    */
179   public static Accessible getAccessibleAt(Component c, Point p)
180   {
181     return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
182   }
183
184   /**
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>
197    *
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
204    */
205   public static Accessible getAccessibleChild(Component c, int i)
206   {
207     return c.getAccessibleContext().getAccessibleChild(i);
208   }
209
210   /**
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>
221    *
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
227    */
228   public static int getAccessibleChildrenCount(Component c)
229   {
230     return c.getAccessibleContext().getAccessibleChildrenCount();
231   }
232
233   /**
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>
245    *
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
251    */
252   public static int getAccessibleIndexInParent(Component c)
253   {
254     return c.getAccessibleContext().getAccessibleIndexInParent();
255   }
256
257   /**
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>
268    *
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
274    */
275   public static AccessibleStateSet getAccessibleStateSet(Component c)
276   {
277     return c.getAccessibleContext().getAccessibleStateSet();
278   }
279
280   /**
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).
284    *
285    * @param aComponent The component to measure
286    *
287    * @return The component's bounds in its local coordinate space
288    */
289   public static Rectangle getLocalBounds(Component aComponent)
290   {
291     Rectangle bounds = aComponent.getBounds();
292     return new Rectangle(0, 0, bounds.width, bounds.height);
293   }
294
295   /**
296    * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
297    * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
298    *
299    * @param comp The component to get the JRootPane of
300    *
301    * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
302    * 
303    * @see javax.swing.RootPaneContainer#getRootPane
304    * @see #getAncestorOfClass
305    */
306   public static JRootPane getRootPane(Component comp)
307   {
308     if (comp instanceof RootPaneContainer)
309       return ((RootPaneContainer)comp).getRootPane();
310     else
311       return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
312   }
313
314   /**
315    * Returns the least ancestor of <code>comp</code> which has the
316    * specified name.
317    *
318    * @param name The name to search for
319    * @param comp The component to search the ancestors of
320    *
321    * @return The nearest ancestor of <code>comp</code> with the given
322    * name, or <code>null</code> if no such ancestor exists
323    *
324    * @see java.awt.Component#getName
325    * @see #getAncestorOfClass
326    */
327   public static Container getAncestorNamed(String name, Component comp)
328   {
329     while (comp != null && (comp.getName() != name))
330       comp = comp.getParent();
331     return (Container) comp;
332   }
333
334   /**
335    * Returns the least ancestor of <code>comp</code> which is an instance
336    * of the specified class.
337    *
338    * @param c The class to search for
339    * @param comp The component to search the ancestors of
340    *
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
343    *
344    * @see #getAncestorOfClass
345    * @see #windowForComponent
346    */
347   public static Container getAncestorOfClass(Class c, Component comp)
348   {
349     while (comp != null && (! c.isInstance(comp)))
350       comp = comp.getParent();
351     return (Container) comp;
352   }
353
354   /**
355    * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
356    *
357    * @param comp The component to search for an ancestor window 
358    *
359    * @return An ancestral window, or <code>null</code> if none exists
360    */
361   public static Window windowForComponent(Component comp)
362   {
363     return (Window) getAncestorOfClass(Window.class, comp);
364   }
365
366   /**
367    * Returns the "root" of the component tree containint <code>comp</code>
368    * The root is defined as either the <em>least</em> ancestor of
369    * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
370    * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
371    * Window} ancestors are found.
372    *
373    * @param comp The component to search for a root
374    *
375    * @return The root of the component's tree, or <code>null</code>
376    */
377   public static Component getRoot(Component comp)
378   {
379     Applet app = null;
380     Window win = null;
381
382     while (comp != null)
383       {
384         if (win == null && comp instanceof Window)
385           win = (Window) comp;
386         else if (comp instanceof Applet)
387           app = (Applet) comp;
388         comp = comp.getParent();
389       }
390
391     if (win != null)
392       return win;
393     else
394       return app;
395   }
396
397   /**
398    * Return true if a descends from b, in other words if b is an
399    * ancestor of a.
400    *
401    * @param a The child to search the ancestry of
402    * @param b The potential ancestor to search for
403    *
404    * @return true if a is a descendent of b, false otherwise
405    */
406   public static boolean isDescendingFrom(Component a, Component b)
407   {
408     while (true)
409       {
410         if (a == null || b == null)
411           return false;
412         if (a == b)
413           return true;
414         a = a.getParent();
415       }
416   }
417
418   /**
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>.
425    * 
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
429    *
430    * @return A component containing <code>(x,y)</code>, or
431    * <code>null</code>
432    *
433    * @see java.awt.Container#findComponentAt
434    */
435   public static Component getDeepestComponentAt(Component parent, int x, int y)
436   {
437     if (parent == null || (! parent.contains(x, y)))
438       return null;
439
440     if (! (parent instanceof Container))
441       return parent;
442
443     Container c = (Container) parent;
444     return c.findComponentAt(x, y);
445   }
446
447   /**
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).
453    *
454    * @param p The point to convert
455    * @param c The component which the point is expressed in terms of
456    *
457    * @see convertPointFromScreen
458    */
459   public static void convertPointToScreen(Point p, Component c)
460   {
461     Point c0 = c.getLocationOnScreen();
462     p.translate(c0.x, c0.y);
463   }
464
465   /**
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).
471    *
472    * @param p The point to convert
473    * @param c The component which the point should be expressed in terms of
474    */
475   public static void convertPointFromScreen(Point p, Component c)
476   {
477     Point c0 = c.getLocationOnScreen();
478     p.translate(-c0.x, -c0.y);
479   }
480
481   /**
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.
489    *
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
495    *
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
498    *
499    * @see #convertPointToScreen
500    * @see #convertPointFromScreen
501    * @see #convertRectangle
502    * @see #getRoot
503    */
504   public static Point convertPoint(Component source, int x, int y,
505                                    Component destination)
506   {
507     Point pt = new Point(x, y);
508
509     if (source == null && destination == null)
510       return pt;
511
512     if (source == null)
513       source = getRoot(destination);
514
515     if (destination == null)
516       destination = getRoot(source);
517
518     convertPointToScreen(pt, source);
519     convertPointFromScreen(pt, destination);
520
521     return pt;
522   }
523   
524   public static Point convertPoint(Component source, Point aPoint, Component destination)
525   {
526     return convertPoint(source, aPoint.x, aPoint.y, destination);
527   }
528
529   /**
530    * Converts a rectangle from the coordinate space of one component to
531    * another. This is equivalent to converting the rectangle from
532    * <code>source</code> space to screen space, then back from screen space
533    * to <code>destination</code> space. If exactly one of the two
534    * Components is <code>null</code>, it is taken to refer to the root
535    * ancestor of the other component. If both are <code>null</code>, no
536    * transformation is done.
537    *
538    * @param source The component which the rectangle is expressed in terms of
539    * @param rect The rectangle to convert
540    * @param destination The component which the return value will be
541    * expressed in terms of
542    *
543    * @return A new rectangle, equal in size to the input rectangle, but
544    * with its position converted from the coordinate space of the source
545    * component to the coordinate space of the destination component
546    *
547    * @see #convertPointToScreen
548    * @see #convertPointFromScreen
549    * @see #convertPoint
550    * @see #getRoot
551    */
552   public static Rectangle convertRectangle(Component source,
553                                            Rectangle rect,
554                                            Component destination)
555   {
556     Point pt = convertPoint(source, rect.x, rect.y, destination);
557     return new Rectangle(pt.x, pt.y, rect.width, rect.height);
558   }
559
560   /**
561    * Convert a mouse event which refrers to one component to another.  This
562    * includes changing the mouse event's coordinate space, as well as the
563    * source property of the event. If <code>source</code> is
564    * <code>null</code>, it is taken to refer to <code>destination</code>'s
565    * root component. If <code>destination</code> is <code>null</code>, the
566    * new event will remain expressed in <code>source</code>'s coordinate
567    * system.
568    *
569    * @param source The component the mouse event currently refers to
570    * @param sourceEvent The mouse event to convert
571    * @param destination The component the new mouse event should refer to
572    *
573    * @return A new mouse event expressed in terms of the destination
574    * component's coordinate space, and with the destination component as
575    * its source
576    *
577    * @see #convertPoint
578    */
579   public static MouseEvent convertMouseEvent(Component source,
580                                              MouseEvent sourceEvent,
581                                              Component destination)
582   {
583     Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
584                                destination);
585
586     return new MouseEvent(destination, sourceEvent.getID(),
587                           sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
588                           newpt.x, newpt.y, sourceEvent.getClickCount(),
589                           sourceEvent.isPopupTrigger(), sourceEvent.getButton());
590   }
591
592   /**
593    * Recursively walk the component tree under <code>comp</code> calling
594    * <code>updateUI</code> on each {@link JComponent} found. This causes
595    * the entire tree to re-initialize its UI delegates.
596    *
597    * @param comp The component to walk the children of, calling <code>updateUI</code>
598    */
599   public static void updateComponentTreeUI(Component comp)
600   {
601     if (comp == null)
602       return;
603     
604     if (comp instanceof Container)
605       {
606         Component[] children = ((Container)comp).getComponents();
607         for (int i = 0; i < children.length; ++i)
608           updateComponentTreeUI(children[i]);
609       }
610
611     if (comp instanceof JComponent)
612       ((JComponent)comp).updateUI();
613   }
614
615
616   /**
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>
621    *
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>
625    *
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>
632    *
633    * <p>The text-icon gap value controls the number of pixels between the
634    * icon and the text.</p>
635    *
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>
643    *
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>
652    *
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>
656    *
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
676    *
677    * @return The string of characters, possibly truncated with an elipsis,
678    * which is laid out in this label
679    */
680
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)
693   {
694
695     // Fix up the orientation-based horizontal positions.
696
697     if (horizontalTextPosition == LEADING)
698       {
699         if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
700           horizontalTextPosition = RIGHT;
701         else
702           horizontalTextPosition = LEFT;
703       }
704     else if (horizontalTextPosition == TRAILING)
705       {
706         if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
707           horizontalTextPosition = LEFT;
708         else
709           horizontalTextPosition = RIGHT;
710       }
711
712     // Fix up the orientation-based alignments.
713
714     if (horizontalAlignment == LEADING)
715       {
716         if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
717           horizontalAlignment = RIGHT;
718         else
719           horizontalAlignment = LEFT;
720       }
721     else if (horizontalAlignment == TRAILING)
722       {
723         if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
724           horizontalAlignment = LEFT;
725         else
726           horizontalAlignment = RIGHT;
727       }
728     
729     return layoutCompoundLabel(fm, text, icon,
730                                verticalAlignment,
731                                horizontalAlignment,
732                                verticalTextPosition,
733                                horizontalTextPosition,
734                                viewR, iconR, textR, textIconGap);
735   }
736
737   /**
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>
742    *
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>
746    *
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>
752    *
753    * <p>The text-icon gap value controls the number of pixels between the
754    * icon and the text.</p>
755    *
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>
762    *
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>
766    *
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.
771    *
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
790    *
791    * @return The string of characters, possibly truncated with an elipsis,
792    * which is laid out in this label
793    */
794
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)
806   {
807
808     // Work out basic height and width.
809
810     if (icon == null)
811       {
812         textIconGap = 0;
813         iconR.width = 0;
814         iconR.height = 0;
815       }
816     else
817       {
818         iconR.width = icon.getIconWidth();
819         iconR.height = icon.getIconHeight();
820       }
821     if (text == null)
822       {
823         textIconGap = 0;
824         textR.width = 0;
825         textR.height = 0;
826       }
827     else
828       {
829         textR.width = fm.stringWidth(text);
830         textR.height = fm.getHeight(); 
831       }
832
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.
836
837     switch (horizontalTextPosition)
838       {
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;
852       }
853
854     switch (verticalTextPosition)
855       {
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;
872       }
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.
876
877     Rectangle u = textR.union(iconR);
878     int horizontalAdjustment = viewR.x;
879     int verticalAdjustment = viewR.y;
880     switch (verticalAlignment)
881       {
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;
890       }
891     switch (horizontalAlignment)
892       {
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;
901       }
902
903     iconR.x += horizontalAdjustment;
904     iconR.y += verticalAdjustment;
905
906     textR.x += horizontalAdjustment;
907     textR.y += verticalAdjustment;
908
909     return text;
910   }
911
912   /** 
913    * Calls {@link java.awt.EventQueue.invokeLater} with the
914    * specified {@link Runnable}. 
915    */
916   public static void invokeLater(Runnable doRun)
917   {
918     java.awt.EventQueue.invokeLater(doRun);
919   }
920
921   /** 
922    * Calls {@link java.awt.EventQueue.invokeAndWait} with the
923    * specified {@link Runnable}. 
924    */
925   public static void invokeAndWait(Runnable doRun)
926     throws InterruptedException,
927     InvocationTargetException
928   {
929     java.awt.EventQueue.invokeAndWait(doRun);
930   }
931
932   /** 
933    * Calls {@link java.awt.EventQueue.isEventDispatchThread}.
934    */
935   public static boolean isEventDispatchThread()
936   {
937     return java.awt.EventQueue.isDispatchThread();
938   }
939   
940   /**
941    * This method paints the given component at the given position and size.
942    * The component will be reparented to the container given.
943    * 
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.
951    */
952   public static void paintComponent(Graphics g, Component c, Container p, 
953                                     int x, int y, int w, int h)
954   {       
955     Container parent = c.getParent();
956     if (parent != null)
957       parent.remove(c);
958     if (p != null)
959       p.add(c);
960     
961     Shape savedClip = g.getClip();
962     
963     g.setClip(x, y, w, h);
964     g.translate(x, y);
965
966     c.paint(g);
967     
968     g.translate(-x, -y);
969     g.setClip(savedClip);
970   }
971
972   /**
973    * This method paints the given component in the given rectangle.
974    * The component will be reparented to the container given.
975    * 
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.
980    */  
981   public static void paintComponent(Graphics g, Component c, 
982                                     Container p, Rectangle r)
983   {
984     paintComponent(g, c, p, r.x, r.y, r.width, r.height);
985   }
986   
987   /**
988    * This method returns the common Frame owner used in JDialogs or
989    * JWindow when no owner is provided.
990    *
991    * @return The common Frame 
992    */
993   static Frame getOwnerFrame()
994   {
995     if (ownerFrame == null)
996       ownerFrame = new OwnerFrame();
997     return ownerFrame;
998   }
999
1000   /**
1001    * Checks if left mouse button was clicked.
1002    *
1003    * @param event the event to check
1004    *
1005    * @return true if left mouse was clicked, false otherwise.
1006    */
1007   public static boolean isLeftMouseButton(MouseEvent event)
1008   {
1009     return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK)
1010              == InputEvent.BUTTON1_DOWN_MASK);
1011   }
1012
1013   /**
1014    * Checks if middle mouse button was clicked.
1015    *
1016    * @param event the event to check
1017    *
1018    * @return true if middle mouse was clicked, false otherwise.
1019    */
1020   public static boolean isMiddleMouseButton(MouseEvent event)
1021   {
1022     return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
1023              == InputEvent.BUTTON2_DOWN_MASK);
1024   }
1025
1026   /**
1027    * Checks if right mouse button was clicked.
1028    *
1029    * @param event the event to check
1030    *
1031    * @return true if right mouse was clicked, false otherwise.
1032    */
1033   public static boolean isRightMouseButton(MouseEvent event)
1034   {
1035     return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
1036              == InputEvent.BUTTON3_DOWN_MASK);
1037   }
1038   
1039   /**
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.
1044    */
1045   private static class OwnerFrame extends Frame
1046   {
1047     public void setVisible(boolean b)
1048     {
1049       // Do nothing here. 
1050     }
1051     
1052     public boolean isShowing()
1053     {
1054       return true;
1055     }
1056   }
1057
1058   public static boolean notifyAction(Action action,
1059                                      KeyStroke ks,
1060                                      KeyEvent event,
1061                                      Object sender,
1062                                      int modifiers)
1063   {
1064     if (action != null && action.isEnabled())
1065       {
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;
1074       }
1075     return false;
1076   }
1077
1078   /**
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>
1084    *
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>
1091    *
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>
1096    *
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>
1100    */
1101   public static void replaceUIActionMap(JComponent component, 
1102                                         ActionMap uiActionMap)
1103   {
1104     ActionMap child = component.getActionMap();
1105     if (child == null)
1106       component.setActionMap(uiActionMap);
1107     else
1108       {
1109         while(child.getParent() != null
1110               && !(child.getParent() instanceof ActionMapUIResource))
1111           child = child.getParent();
1112         if (child != null)
1113           child.setParent(uiActionMap);
1114       }
1115   }
1116
1117   /**
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>
1123    *
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>
1130    *
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>
1135    *
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>
1139    */
1140   public static void replaceUIInputMap(JComponent component, 
1141                                        int condition, 
1142                                        InputMap uiInputMap)
1143   {
1144     InputMap child = component.getInputMap(condition);
1145     if (child == null)
1146       component.setInputMap(condition, uiInputMap);
1147     else
1148       {
1149         while(child.getParent() != null
1150               && !(child.getParent() instanceof InputMapUIResource))
1151           child = child.getParent();
1152         if (child != null)
1153           child.setParent(uiInputMap);
1154       }
1155   }
1156
1157   /**
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
1167    */
1168   public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
1169   {
1170     if (rectA == null || rectB == null)
1171       return new Rectangle[0];
1172
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;
1182
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
1195
1196     if (H0 > 0)
1197       r[0] = new Rectangle(x1, y1, w1, H0);
1198     else
1199       r[0] = null;
1200
1201     if (W1 > 0 && H12 > 0)
1202       r[1] = new Rectangle(x1, y1 + H0, W1, H12);
1203     else
1204       r[1] = null;
1205
1206     if (W2 > 0 && H12 > 0)
1207       r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
1208     else
1209       r[2] = null;
1210
1211     if (H3 > 0)
1212       r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
1213     else
1214       r[3] = null;
1215
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];
1225
1226     return out;
1227   }
1228
1229   /**
1230    * Calculates the intersection of two rectangles.
1231    *
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.
1238    *
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.
1242    */
1243   public static Rectangle computeIntersection(int x, int y, int w, int h,
1244                                               Rectangle rect)
1245   {
1246     int x2 = (int) rect.getX();
1247     int y2 = (int) rect.getY();
1248     int w2 = (int) rect.getWidth();
1249     int h2 = (int) rect.getHeight();
1250
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);
1255
1256     if (dw >= 0 && dh >= 0)
1257       return new Rectangle(dx, dy, dw, dh);
1258
1259     return new Rectangle(0, 0, 0, 0);
1260   }
1261   
1262   /**
1263    * Calculates the width of a given string.
1264    *
1265    * @param fm the <code>FontMetrics</code> object to use
1266    * @param str the string
1267    * 
1268    * @return the width of the the string.
1269    */
1270   public static int computeStringWidth(FontMetrics fm, String str)
1271   {
1272     return fm.stringWidth(str);
1273   }
1274
1275   /**
1276    * Calculates the union of two rectangles.
1277    *
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.
1284    *
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.
1288    */
1289   public static Rectangle computeUnion(int x, int y, int w, int h,
1290                                        Rectangle rect)
1291   {
1292     int x2 = (int) rect.getX();
1293     int y2 = (int) rect.getY();
1294     int w2 = (int) rect.getWidth();
1295     int h2 = (int) rect.getHeight();
1296
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);
1301
1302     if (dw >= 0 && dh >= 0)
1303       return new Rectangle(dx, dy, dw, dh);
1304
1305     return new Rectangle(0, 0, 0, 0);
1306   }
1307
1308   /**
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
1314    */
1315   public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
1316   {
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;
1321   }
1322 }