001    /* SwingUtilities.java --
002       Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing;
040    
041    import java.applet.Applet;
042    import java.awt.Component;
043    import java.awt.Container;
044    import java.awt.FontMetrics;
045    import java.awt.Frame;
046    import java.awt.Graphics;
047    import java.awt.Insets;
048    import java.awt.KeyboardFocusManager;
049    import java.awt.Point;
050    import java.awt.Rectangle;
051    import java.awt.Shape;
052    import java.awt.Window;
053    import java.awt.event.ActionEvent;
054    import java.awt.event.InputEvent;
055    import java.awt.event.KeyEvent;
056    import java.awt.event.MouseEvent;
057    import java.lang.reflect.InvocationTargetException;
058    
059    import javax.accessibility.Accessible;
060    import javax.accessibility.AccessibleStateSet;
061    import javax.swing.plaf.ActionMapUIResource;
062    import javax.swing.plaf.InputMapUIResource;
063    import javax.swing.plaf.basic.BasicHTML;
064    import javax.swing.text.View;
065    
066    /**
067     * A number of static utility functions which are
068     * useful when drawing swing components, dispatching events, or calculating
069     * regions which need painting.
070     *
071     * @author Graydon Hoare (graydon@redhat.com)
072     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
073     */
074    public class SwingUtilities
075      implements SwingConstants
076    {
077      /** 
078       * This frame should be used as parent for JWindow or JDialog 
079       * that doesn't an owner
080       */
081      private static OwnerFrame ownerFrame;
082    
083      private SwingUtilities()
084      {
085        // Do nothing.
086      }
087    
088      /**
089       * Calculates the portion of the component's bounds which is inside the
090       * component's border insets. This area is usually the area a component
091       * should confine its painting to. The coordinates are returned in terms
092       * of the <em>component's</em> coordinate system, where (0,0) is the
093       * upper left corner of the component's bounds.
094       *
095       * @param c  the component to measure the bounds of (if <code>null</code>, 
096       *     this method returns <code>null</code>).
097       * @param r  a carrier to store the return value in (if <code>null</code>, a
098       *     new <code>Rectangle</code> instance is created).
099       *
100       * @return The calculated area inside the component and its border insets.
101       */
102      public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
103      {
104        if (c == null)
105          return null;
106        r = c.getBounds(r);
107        Insets i = c.getInsets();
108        r.x = i.left;
109        r.width = r.width - i.left - i.right;
110        r.y = i.top;
111        r.height = r.height - i.top - i.bottom;
112        return r;
113      }
114    
115      /**
116       * Returns the focus owner or <code>null</code> if <code>comp</code> is not
117       * the focus owner or a parent of it.
118       * 
119       * @param comp the focus owner or a parent of it
120       * 
121       * @return the focus owner, or <code>null</code>
122       * 
123       * @deprecated 1.4 Replaced by
124       * <code>KeyboardFocusManager.getFocusOwner()</code>.
125       */
126      public static Component findFocusOwner(Component comp)
127      {
128        // Get real focus owner.
129        Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
130                                                   .getFocusOwner();
131    
132        // Check if comp is the focus owner or a parent of it.
133        Component tmp = focusOwner;
134        
135        while (tmp != null)
136          {
137            if (tmp == comp)
138              return focusOwner;
139    
140            tmp = tmp.getParent();
141          }
142        
143        return null;
144      }
145      
146      /**
147       * Returns the <code>Accessible</code> child of the specified component
148       * which appears at the supplied <code>Point</code>.  If there is no
149       * child located at that particular pair of co-ordinates, null is returned
150       * instead.
151       *
152       * @param c the component whose children may be found at the specified
153       *          point.
154       * @param p the point at which to look for the existence of children
155       *          of the specified component.
156       * @return the <code>Accessible</code> child at the point, <code>p</code>,
157       *         or null if there is no child at this point.
158       * @see javax.accessibility.AccessibleComponent#getAccessibleAt
159       */
160      public static Accessible getAccessibleAt(Component c, Point p)
161      {
162        return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
163      }
164    
165      /**
166       * <p>
167       * Returns the <code>Accessible</code> child of the specified component
168       * that has the supplied index within the parent component.  The indexing
169       * of the children is zero-based, making the first child have an index of
170       * 0.
171       * </p>
172       * <p>
173       * Caution is advised when using this method, as its operation relies
174       * on the behaviour of varying implementations of an abstract method.
175       * For greater surety, direct use of the AWT component implementation
176       * of this method is advised.
177       * </p>
178       *
179       * @param c the component whose child should be returned.
180       * @param i the index of the child within the parent component.
181       * @return the <code>Accessible</code> child at index <code>i</code>
182       *         in the component, <code>c</code>.
183       * @see javax.accessibility.AccessibleContext#getAccessibleChild
184       * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild
185       */
186      public static Accessible getAccessibleChild(Component c, int i)
187      {
188        return c.getAccessibleContext().getAccessibleChild(i);
189      }
190    
191      /**
192       * <p>
193       * Returns the number of <code>Accessible</code> children within
194       * the supplied component.
195       * </p>
196       * <p>
197       * Caution is advised when using this method, as its operation relies
198       * on the behaviour of varying implementations of an abstract method.
199       * For greater surety, direct use of the AWT component implementation
200       * of this method is advised.
201       * </p>
202       *
203       * @param c the component whose children should be counted.
204       * @return the number of children belonging to the component,
205       *         <code>c</code>.
206       * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount
207       * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount
208       */
209      public static int getAccessibleChildrenCount(Component c)
210      {
211        return c.getAccessibleContext().getAccessibleChildrenCount();
212      }
213    
214      /**
215       * <p>
216       * Returns the zero-based index of the specified component
217       * within its parent.  If the component doesn't have a parent,
218       * -1 is returned.
219       * </p>
220       * <p>
221       * Caution is advised when using this method, as its operation relies
222       * on the behaviour of varying implementations of an abstract method.
223       * For greater surety, direct use of the AWT component implementation
224       * of this method is advised.
225       * </p>
226       *
227       * @param c the component whose parental index should be found.
228       * @return the index of the component within its parent, or -1
229       *         if the component doesn't have a parent.
230       * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent
231       * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent
232       */
233      public static int getAccessibleIndexInParent(Component c)
234      {
235        return c.getAccessibleContext().getAccessibleIndexInParent();
236      }
237    
238      /**
239       * <p>
240       * Returns a set of <code>AccessibleState</code>s, which represent
241       * the state of the supplied component.
242       * </p>
243       * <p>
244       * Caution is advised when using this method, as its operation relies
245       * on the behaviour of varying implementations of an abstract method.
246       * For greater surety, direct use of the AWT component implementation
247       * of this method is advised.
248       * </p>
249       *
250       * @param c the component whose accessible state should be retrieved.
251       * @return a set of <code>AccessibleState</code> objects, which represent
252       *         the state of the supplied component.
253       * @see javax.accessibility.AccessibleContext#getAccessibleStateSet
254       * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet
255       */
256      public static AccessibleStateSet getAccessibleStateSet(Component c)
257      {
258        return c.getAccessibleContext().getAccessibleStateSet();
259      }
260    
261      /**
262       * Calculates the bounds of a component in the component's own coordinate
263       * space. The result has the same height and width as the component's
264       * bounds, but its location is set to (0,0).
265       *
266       * @param aComponent The component to measure
267       *
268       * @return The component's bounds in its local coordinate space
269       */
270      public static Rectangle getLocalBounds(Component aComponent)
271      {
272        Rectangle bounds = aComponent.getBounds();
273        return new Rectangle(0, 0, bounds.width, bounds.height);
274      }
275    
276      /**
277       * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
278       * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
279       *
280       * @param comp The component to get the JRootPane of
281       *
282       * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
283       * 
284       * @see javax.swing.RootPaneContainer#getRootPane
285       * @see #getAncestorOfClass
286       */
287      public static JRootPane getRootPane(Component comp)
288      {
289        if (comp instanceof RootPaneContainer)
290          return ((RootPaneContainer)comp).getRootPane();
291        else
292          return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
293      }
294    
295      /**
296       * Returns the least ancestor of <code>comp</code> which has the
297       * specified name.
298       *
299       * @param name The name to search for
300       * @param comp The component to search the ancestors of
301       *
302       * @return The nearest ancestor of <code>comp</code> with the given
303       * name, or <code>null</code> if no such ancestor exists
304       *
305       * @see java.awt.Component#getName
306       * @see #getAncestorOfClass
307       */
308      public static Container getAncestorNamed(String name, Component comp)
309      {
310        while (comp != null && (comp.getName() != name))
311          comp = comp.getParent();
312        return (Container) comp;
313      }
314    
315      /**
316       * Returns the least ancestor of <code>comp</code> which is an instance
317       * of the specified class.
318       *
319       * @param c The class to search for
320       * @param comp The component to search the ancestors of
321       *
322       * @return The nearest ancestor of <code>comp</code> which is an instance
323       * of the given class, or <code>null</code> if no such ancestor exists
324       *
325       * @see #getAncestorOfClass
326       * @see #windowForComponent
327       */
328      public static Container getAncestorOfClass(Class<?> c, Component comp)
329      {
330        while (comp != null && (! c.isInstance(comp)))
331          comp = comp.getParent();
332        return (Container) comp;
333      }
334    
335      /**
336       * Returns the first ancestor of <code>comp</code> that is a {@link Window}
337       * or <code>null</code> if <code>comp</code> is not contained in a
338       * {@link Window}.
339       *
340       * This is equivalent to calling
341       * <code>getAncestorOfClass(Window, comp)</code> or
342       * <code>windowForComponent(comp)</code>.
343       *
344       * @param comp the component for which we are searching the ancestor Window
345       *
346       * @return the first ancestor Window of <code>comp</code> or
347       *     <code>null</code> if <code>comp</code> is not contained in a Window
348       */
349      public static Window getWindowAncestor(Component comp)
350      {
351        return (Window) getAncestorOfClass(Window.class, comp);
352      }
353    
354      /**
355       * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
356       *
357       * @param comp The component to search for an ancestor window 
358       *
359       * @return An ancestral window, or <code>null</code> if none exists
360       */
361      public static Window windowForComponent(Component comp)
362      {
363        return (Window) getAncestorOfClass(Window.class, comp);
364      }
365    
366      /**
367       * Returns the "root" of the component tree containint <code>comp</code>
368       * The root is defined as either the <em>least</em> ancestor of
369       * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
370       * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
371       * Window} ancestors are found.
372       *
373       * @param comp The component to search for a root
374       *
375       * @return The root of the component's tree, or <code>null</code>
376       */
377      public static Component getRoot(Component comp)
378      {
379        Applet app = null;
380        Window win = null;
381    
382        while (comp != null)
383          {
384            if (win == null && comp instanceof Window)
385              win = (Window) comp;
386            else if (comp instanceof Applet)
387              app = (Applet) comp;
388            comp = comp.getParent();
389          }
390        
391        if (win != null)
392          return win;
393        return app;
394      }
395    
396      /**
397       * Return true if a descends from b, in other words if b is an ancestor of a.
398       * 
399       * @param a The child to search the ancestry of
400       * @param b The potential ancestor to search for
401       * @return true if a is a descendent of b, false otherwise
402       */
403      public static boolean isDescendingFrom(Component a, Component b)
404      {
405        while (true)
406          {
407            if (a == null || b == null)
408              return false;
409            if (a == b)
410              return true;
411            a = a.getParent();
412          }
413      }
414    
415      /**
416       * Returns the deepest descendent of parent which is both visible and
417       * contains the point <code>(x,y)</code>. Returns parent when either
418       * parent is not a container, or has no children which contain
419       * <code>(x,y)</code>. Returns <code>null</code> when either
420       * <code>(x,y)</code> is outside the bounds of parent, or parent is
421       * <code>null</code>.
422       * 
423       * @param parent The component to search the descendents of
424       * @param x Horizontal coordinate to search for
425       * @param y Vertical coordinate to search for
426       *
427       * @return A component containing <code>(x,y)</code>, or
428       * <code>null</code>
429       *
430       * @see java.awt.Container#findComponentAt(int, int)
431       */
432      public static Component getDeepestComponentAt(Component parent, int x, int y)
433      {
434        if (parent == null || (! parent.contains(x, y)))
435          return null;
436    
437        if (! (parent instanceof Container))
438          return parent;
439    
440        Container c = (Container) parent;
441        return c.findComponentAt(x, y);
442      }
443    
444      /**
445       * Converts a point from a component's local coordinate space to "screen"
446       * coordinates (such as the coordinate space mouse events are delivered
447       * in). This operation is equivalent to translating the point by the
448       * location of the component (which is the origin of its coordinate
449       * space).
450       *
451       * @param p The point to convert
452       * @param c The component which the point is expressed in terms of
453       *
454       * @see #convertPointFromScreen
455       */
456      public static void convertPointToScreen(Point p, Component c)
457      {
458        Point c0 = c.getLocationOnScreen();
459        p.translate(c0.x, c0.y);
460      }
461    
462      /**
463       * Converts a point from "screen" coordinates (such as the coordinate
464       * space mouse events are delivered in) to a component's local coordinate
465       * space. This operation is equivalent to translating the point by the
466       * negation of the component's location (which is the origin of its
467       * coordinate space).
468       *
469       * @param p The point to convert
470       * @param c The component which the point should be expressed in terms of
471       */
472      public static void convertPointFromScreen(Point p, Component c)
473      {
474        Point c0 = c.getLocationOnScreen();
475        p.translate(-c0.x, -c0.y);
476      }
477    
478      /**
479       * Converts a point <code>(x,y)</code> from the coordinate space of one
480       * component to another. This is equivalent to converting the point from
481       * <code>source</code> space to screen space, then back from screen space
482       * to <code>destination</code> space. If exactly one of the two
483       * Components is <code>null</code>, it is taken to refer to the root
484       * ancestor of the other component. If both are <code>null</code>, no
485       * transformation is done.
486       *
487       * @param source The component which the point is expressed in terms of
488       * @param x Horizontal coordinate of point to transform
489       * @param y Vertical coordinate of point to transform
490       * @param destination The component which the return value will be
491       * expressed in terms of
492       *
493       * @return The point <code>(x,y)</code> converted from the coordinate space of the
494       * source component to the coordinate space of the destination component
495       *
496       * @see #convertPointToScreen
497       * @see #convertPointFromScreen
498       * @see #convertRectangle
499       * @see #getRoot
500       */
501      public static Point convertPoint(Component source, int x, int y,
502                                       Component destination)
503      {
504        Point pt = new Point(x, y);
505    
506        if (source == null && destination == null)
507          return pt;
508    
509        if (source == null)
510          source = getRoot(destination);
511    
512        if (destination == null)
513          destination = getRoot(source);
514    
515        if (source.isShowing() && destination.isShowing())
516          {
517            convertPointToScreen(pt, source);
518            convertPointFromScreen(pt, destination);
519          }
520    
521        return pt;
522      }
523      
524      public static Point convertPoint(Component source, Point aPoint, Component destination)
525      {
526        return convertPoint(source, aPoint.x, aPoint.y, destination);
527      }
528    
529      /**
530       * Converts a rectangle from the coordinate space of one component to
531       * another. This is equivalent to converting the rectangle from
532       * <code>source</code> space to screen space, then back from screen space
533       * to <code>destination</code> space. If exactly one of the two
534       * Components is <code>null</code>, it is taken to refer to the root
535       * ancestor of the other component. If both are <code>null</code>, no
536       * transformation is done.
537       *
538       * @param source The component which the rectangle is expressed in terms of
539       * @param rect The rectangle to convert
540       * @param destination The component which the return value will be
541       * expressed in terms of
542       *
543       * @return A new rectangle, equal in size to the input rectangle, but
544       * with its position converted from the coordinate space of the source
545       * component to the coordinate space of the destination component
546       *
547       * @see #convertPointToScreen
548       * @see #convertPointFromScreen
549       * @see #convertPoint(Component, int, int, Component)
550       * @see #getRoot
551       */
552      public static Rectangle convertRectangle(Component source,
553                                               Rectangle rect,
554                                               Component destination)
555      {
556        Point pt = convertPoint(source, rect.x, rect.y, destination);
557        return new Rectangle(pt.x, pt.y, rect.width, rect.height);
558      }
559    
560      /**
561       * Convert a mouse event which refrers to one component to another.  This
562       * includes changing the mouse event's coordinate space, as well as the
563       * source property of the event. If <code>source</code> is
564       * <code>null</code>, it is taken to refer to <code>destination</code>'s
565       * root component. If <code>destination</code> is <code>null</code>, the
566       * new event will remain expressed in <code>source</code>'s coordinate
567       * system.
568       *
569       * @param source The component the mouse event currently refers to
570       * @param sourceEvent The mouse event to convert
571       * @param destination The component the new mouse event should refer to
572       *
573       * @return A new mouse event expressed in terms of the destination
574       * component's coordinate space, and with the destination component as
575       * its source
576       *
577       * @see #convertPoint(Component, int, int, Component)
578       */
579      public static MouseEvent convertMouseEvent(Component source,
580                                                 MouseEvent sourceEvent,
581                                                 Component destination)
582      {
583        Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
584                                   destination);
585    
586        return new MouseEvent(destination, sourceEvent.getID(),
587                              sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
588                              newpt.x, newpt.y, sourceEvent.getClickCount(),
589                              sourceEvent.isPopupTrigger(), sourceEvent.getButton());
590      }
591    
592      /**
593       * Recursively walk the component tree under <code>comp</code> calling
594       * <code>updateUI</code> on each {@link JComponent} found. This causes
595       * the entire tree to re-initialize its UI delegates.
596       *
597       * @param comp The component to walk the children of, calling <code>updateUI</code>
598       */
599      public static void updateComponentTreeUI(Component comp)
600      {
601        updateComponentTreeUIImpl(comp);
602        if (comp instanceof JComponent)
603          {
604            JComponent jc = (JComponent) comp;
605            jc.revalidate();
606          }
607        else
608          {
609            comp.invalidate();
610            comp.validate();
611          }
612        comp.repaint();
613      }
614    
615      /**
616       * Performs the actual work for {@link #updateComponentTreeUI(Component)}.
617       * This calls updateUI() on c if it is a JComponent, and then walks down
618       * the component tree and calls this method on each child component.
619       *
620       * @param c the component to update the UI
621       */
622      private static void updateComponentTreeUIImpl(Component c)
623      {
624        if (c instanceof JComponent)
625          {
626            JComponent jc = (JComponent) c;
627            jc.updateUI();
628          }
629    
630        Component[] components = null;
631        if (c instanceof JMenu)
632          components = ((JMenu) c).getMenuComponents();
633        else if (c instanceof Container)
634          components = ((Container) c).getComponents();
635        if (components != null)
636          {
637            for (int i = 0; i < components.length; ++i)
638              updateComponentTreeUIImpl(components[i]);
639          }
640      }
641    
642      /**
643       * <p>Layout a "compound label" consisting of a text string and an icon
644       * which is to be placed near the rendered text. Once the text and icon
645       * are laid out, the text rectangle and icon rectangle parameters are
646       * altered to store the calculated positions.</p>
647       *
648       * <p>The size of the text is calculated from the provided font metrics
649       * object.  This object should be the metrics of the font you intend to
650       * paint the label with.</p>
651       *
652       * <p>The position values control where the text is placed relative to
653       * the icon. The horizontal position value should be one of the constants
654       * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>,
655       * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value
656       * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code>
657       * or <code>CENTER</code>.</p>
658       *
659       * <p>The text-icon gap value controls the number of pixels between the
660       * icon and the text.</p>
661       *
662       * <p>The alignment values control where the text and icon are placed, as
663       * a combined unit, within the view rectangle. The horizontal alignment
664       * value should be one of the constants <code>LEADING</code>,
665       * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
666       * <code>CENTER</code>. The vertical alignment valus should be one of the
667       * constants <code>TOP</code>, <code>BOTTOM</code> or
668       * <code>CENTER</code>.</p>
669       *
670       * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
671       * given for horizontal alignment or horizontal text position, they are
672       * interpreted relative to the provided component's orientation property,
673       * a constant in the {@link java.awt.ComponentOrientation} class. For
674       * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>,
675       * then the <code>LEADING</code> value is a synonym for <code>LEFT</code>
676       * and the <code>TRAILING</code> value is a synonym for
677       * <code>RIGHT</code></p>
678       *
679       * <p>If the text and icon are equal to or larger than the view
680       * rectangle, the horizontal and vertical alignment values have no
681       * affect.</p>
682       *
683       * @param c A component used for its orientation value
684       * @param fm The font metrics used to measure the text
685       * @param text The text to place in the compound label
686       * @param icon The icon to place next to the text
687       * @param verticalAlignment The vertical alignment of the label relative
688       * to its component
689       * @param horizontalAlignment The horizontal alignment of the label
690       * relative to its component
691       * @param verticalTextPosition The vertical position of the label's text
692       * relative to its icon
693       * @param horizontalTextPosition The horizontal position of the label's
694       * text relative to its icon
695       * @param viewR The view rectangle, specifying the area which layout is
696       * constrained to
697       * @param iconR A rectangle which is modified to hold the laid-out
698       * position of the icon
699       * @param textR A rectangle which is modified to hold the laid-out
700       * position of the text
701       * @param textIconGap The distance between text and icon
702       *
703       * @return The string of characters, possibly truncated with an elipsis,
704       * which is laid out in this label
705       */
706    
707      public static String layoutCompoundLabel(JComponent c, 
708                                               FontMetrics fm,
709                                               String text, 
710                                               Icon icon, 
711                                               int verticalAlignment,
712                                               int horizontalAlignment, 
713                                               int verticalTextPosition,
714                                               int horizontalTextPosition, 
715                                               Rectangle viewR,
716                                               Rectangle iconR, 
717                                               Rectangle textR, 
718                                               int textIconGap)
719      {
720    
721        // Fix up the orientation-based horizontal positions.
722    
723        boolean ltr = true;
724        if (c != null && ! c.getComponentOrientation().isLeftToRight())
725          ltr = false;
726    
727        switch (horizontalTextPosition)
728          {
729          case LEADING:
730            horizontalTextPosition = ltr ? LEFT : RIGHT;
731            break;
732          case TRAILING:
733            horizontalTextPosition = ltr ? RIGHT : LEFT;
734            break;
735          default:
736            // Nothing to do in the other cases.
737          }
738    
739        // Fix up the orientation-based alignments.
740        switch (horizontalAlignment)
741          {
742            case LEADING:
743              horizontalAlignment = ltr ? LEFT : RIGHT;
744              break;
745            case TRAILING:
746              horizontalAlignment = ltr ? RIGHT : LEFT;
747              break;
748            default:
749              // Nothing to do in the other cases.
750          }
751    
752        return layoutCompoundLabelImpl(c, fm, text, icon,
753                                       verticalAlignment,
754                                       horizontalAlignment,
755                                       verticalTextPosition,
756                                       horizontalTextPosition,
757                                       viewR, iconR, textR, textIconGap);
758      }
759    
760      /**
761       * <p>Layout a "compound label" consisting of a text string and an icon
762       * which is to be placed near the rendered text. Once the text and icon
763       * are laid out, the text rectangle and icon rectangle parameters are
764       * altered to store the calculated positions.</p>
765       *
766       * <p>The size of the text is calculated from the provided font metrics
767       * object.  This object should be the metrics of the font you intend to
768       * paint the label with.</p>
769       *
770       * <p>The position values control where the text is placed relative to
771       * the icon. The horizontal position value should be one of the constants
772       * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
773       * vertical position value should be one fo the constants
774       * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
775       *
776       * <p>The text-icon gap value controls the number of pixels between the
777       * icon and the text.</p>
778       *
779       * <p>The alignment values control where the text and icon are placed, as
780       * a combined unit, within the view rectangle. The horizontal alignment
781       * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
782       * <code>CENTER</code>. The vertical alignment valus should be one of the
783       * constants <code>TOP</code>, <code>BOTTOM</code> or
784       * <code>CENTER</code>.</p>
785       *
786       * <p>If the text and icon are equal to or larger than the view
787       * rectangle, the horizontal and vertical alignment values have no
788       * affect.</p>
789       *
790       * <p>Note that this method does <em>not</em> know how to deal with
791       * horizontal alignments or positions given as <code>LEADING</code> or
792       * <code>TRAILING</code> values. Use the other overloaded variant of this
793       * method if you wish to use such values.
794       *
795       * @param fm The font metrics used to measure the text
796       * @param text The text to place in the compound label
797       * @param icon The icon to place next to the text
798       * @param verticalAlignment The vertical alignment of the label relative
799       * to its component
800       * @param horizontalAlignment The horizontal alignment of the label
801       * relative to its component
802       * @param verticalTextPosition The vertical position of the label's text
803       * relative to its icon
804       * @param horizontalTextPosition The horizontal position of the label's
805       * text relative to its icon
806       * @param viewR The view rectangle, specifying the area which layout is
807       * constrained to
808       * @param iconR A rectangle which is modified to hold the laid-out
809       * position of the icon
810       * @param textR A rectangle which is modified to hold the laid-out
811       * position of the text
812       * @param textIconGap The distance between text and icon
813       *
814       * @return The string of characters, possibly truncated with an elipsis,
815       * which is laid out in this label
816       */
817    
818      public static String layoutCompoundLabel(FontMetrics fm,
819                                               String text,
820                                               Icon icon,
821                                               int verticalAlignment,
822                                               int horizontalAlignment,
823                                               int verticalTextPosition,
824                                               int horizontalTextPosition,
825                                               Rectangle viewR,
826                                               Rectangle iconR,
827                                               Rectangle textR,
828                                               int textIconGap)
829      {
830        return layoutCompoundLabelImpl(null, fm, text, icon, verticalAlignment,
831                                       horizontalAlignment, verticalTextPosition,
832                                       horizontalTextPosition, viewR, iconR, textR,
833                                       textIconGap);
834      }
835    
836      /**
837       * <p>Layout a "compound label" consisting of a text string and an icon
838       * which is to be placed near the rendered text. Once the text and icon
839       * are laid out, the text rectangle and icon rectangle parameters are
840       * altered to store the calculated positions.</p>
841       *
842       * <p>The size of the text is calculated from the provided font metrics
843       * object.  This object should be the metrics of the font you intend to
844       * paint the label with.</p>
845       *
846       * <p>The position values control where the text is placed relative to
847       * the icon. The horizontal position value should be one of the constants
848       * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
849       * vertical position value should be one fo the constants
850       * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
851       *
852       * <p>The text-icon gap value controls the number of pixels between the
853       * icon and the text.</p>
854       *
855       * <p>The alignment values control where the text and icon are placed, as
856       * a combined unit, within the view rectangle. The horizontal alignment
857       * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
858       * <code>CENTER</code>. The vertical alignment valus should be one of the
859       * constants <code>TOP</code>, <code>BOTTOM</code> or
860       * <code>CENTER</code>.</p>
861       *
862       * <p>If the text and icon are equal to or larger than the view
863       * rectangle, the horizontal and vertical alignment values have no
864       * affect.</p>
865       *
866       * <p>Note that this method does <em>not</em> know how to deal with
867       * horizontal alignments or positions given as <code>LEADING</code> or
868       * <code>TRAILING</code> values. Use the other overloaded variant of this
869       * method if you wish to use such values.
870       *
871       * @param fm The font metrics used to measure the text
872       * @param text The text to place in the compound label
873       * @param icon The icon to place next to the text
874       * @param verticalAlignment The vertical alignment of the label relative
875       * to its component
876       * @param horizontalAlignment The horizontal alignment of the label
877       * relative to its component
878       * @param verticalTextPosition The vertical position of the label's text
879       * relative to its icon
880       * @param horizontalTextPosition The horizontal position of the label's
881       * text relative to its icon
882       * @param viewR The view rectangle, specifying the area which layout is
883       * constrained to
884       * @param iconR A rectangle which is modified to hold the laid-out
885       * position of the icon
886       * @param textR A rectangle which is modified to hold the laid-out
887       * position of the text
888       * @param textIconGap The distance between text and icon
889       *
890       * @return The string of characters, possibly truncated with an elipsis,
891       * which is laid out in this label
892       */
893      private static String layoutCompoundLabelImpl(JComponent c,
894                                                    FontMetrics fm,
895                                                    String text,
896                                                    Icon icon,
897                                                    int verticalAlignment,
898                                                    int horizontalAlignment,
899                                                    int verticalTextPosition,
900                                                    int horizontalTextPosition,
901                                                    Rectangle viewR,
902                                                    Rectangle iconR,
903                                                    Rectangle textR,
904                                                    int textIconGap)
905      {
906    
907        // Work out basic height and width.
908    
909        if (icon == null)
910          {
911            textIconGap = 0;
912            iconR.width = 0;
913            iconR.height = 0;
914          }
915        else
916          {
917            iconR.width = icon.getIconWidth();
918            iconR.height = icon.getIconHeight();
919          }
920    
921        if (text == null || text.equals(""))
922          {
923            textIconGap = 0;
924            textR.width = 0;
925            textR.height = 0;
926            text = "";
927          }
928        else
929          {
930            int availableWidth = viewR.width;
931            if (horizontalTextPosition != CENTER)
932              availableWidth -= iconR.width + textIconGap;
933            View html = c == null ? null
934                               : (View) c.getClientProperty(BasicHTML.propertyKey);
935            if (html != null)
936              {
937                textR.width = (int) html.getPreferredSpan(View.X_AXIS);
938                textR.width = Math.min(availableWidth, textR.width);
939                textR.height = (int) html.getPreferredSpan(View.Y_AXIS);
940              }
941            else
942              {
943                int fromIndex = 0;
944                textR.width = fm.stringWidth(text);
945                textR.height = fm.getHeight();
946                if (textR.width > availableWidth)
947                  {
948                    text = clipString(c, fm, text, availableWidth);
949                    textR.width = fm.stringWidth(text);
950                  }
951              }
952          }
953    
954        // Work out the position of text, assuming the top-left coord
955        // starts at (0,0). We will fix that up momentarily, after these
956        // "position" decisions are made and we look at alignment.
957    
958        switch (verticalTextPosition)
959          {
960          case TOP:
961            textR.y = horizontalTextPosition == CENTER ?
962                      - textR.height - textIconGap : 0;
963            break;
964          case BOTTOM:
965            textR.y = horizontalTextPosition == CENTER ?
966                      iconR.height + textIconGap : iconR.height - textR.height;
967            break;
968          case CENTER:
969            textR.y = iconR.height / 2 - textR.height / 2;
970            break;
971          }
972    
973        switch (horizontalTextPosition)
974          {
975          case LEFT:
976            textR.x = -(textR.width + textIconGap);
977            break;
978          case RIGHT:
979            textR.x = iconR.width + textIconGap;
980            break;
981          case CENTER:
982            textR.x = iconR.width / 2 - textR.width / 2;
983            break;
984          }
985    
986        // The two rectangles are laid out correctly now, but only assuming
987        // that their upper left corner is at (0,0). If we have any alignment other
988        // than TOP and LEFT, we need to adjust them.
989    
990        // These coordinates specify the rectangle that contains both the
991        // icon and text. Move it so that it fullfills the alignment properties.
992        int lx = Math.min(iconR.x, textR.x);
993        int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx;
994        int ly = Math.min(iconR.y, textR.y);
995        int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly;
996        int horizontalAdjustment = 0;
997        int verticalAdjustment = 0;
998        switch (verticalAlignment)
999          {
1000          case TOP:
1001            verticalAdjustment = viewR.y - ly;
1002            break;
1003          case BOTTOM:
1004            verticalAdjustment = viewR.y + viewR.height - ly - lh;
1005            break;
1006          case CENTER:
1007            verticalAdjustment = viewR.y + viewR.height / 2 - ly  - lh / 2;
1008            break;
1009          }
1010        switch (horizontalAlignment)
1011          {
1012          case LEFT:
1013            horizontalAdjustment = viewR.x - lx;
1014            break;
1015          case RIGHT:
1016            horizontalAdjustment = viewR.x + viewR.width - lx - lw;
1017            break;
1018          case CENTER:
1019            horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2));
1020            break;
1021          }
1022        iconR.x += horizontalAdjustment;
1023        iconR.y += verticalAdjustment;
1024    
1025        textR.x += horizontalAdjustment;
1026        textR.y += verticalAdjustment;
1027    
1028        return text;
1029      }
1030    
1031      /**
1032       * The method clips the specified string so that it fits into the
1033       * available width. It is only called when the text really doesn't fit,
1034       * so we don't need to check that again.
1035       *
1036       * @param c the component
1037       * @param fm the font metrics
1038       * @param text the text
1039       * @param availableWidth the available width
1040       *
1041       * @return the clipped string
1042       */
1043      private static String clipString(JComponent c, FontMetrics fm, String text,
1044                                       int availableWidth)
1045      {
1046        String dots = "...";
1047        int dotsWidth = fm.stringWidth(dots);
1048        char[] string = text.toCharArray();
1049        int endIndex = string.length;
1050        while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth
1051               && endIndex > 0)
1052          endIndex--;
1053        String clipped;
1054        if (string.length >= endIndex + 3)
1055          {
1056            string[endIndex] = '.';
1057            string[endIndex + 1] = '.';
1058            string[endIndex + 2] = '.';
1059            clipped = new String(string, 0, endIndex + 3);
1060          }
1061        else
1062          {
1063            char[] clippedChars = new char[string.length + 3];
1064            System.arraycopy(string, 0, clippedChars, 0, string.length);
1065            clippedChars[endIndex] = '.';
1066            clippedChars[endIndex + 1] = '.';
1067            clippedChars[endIndex + 2] = '.';
1068            clipped = new String(clippedChars, 0, endIndex + 3);
1069          }
1070        return clipped;
1071      }
1072    
1073      /** 
1074       * Calls {@link java.awt.EventQueue#invokeLater} with the
1075       * specified {@link Runnable}. 
1076       */
1077      public static void invokeLater(Runnable doRun)
1078      {
1079        java.awt.EventQueue.invokeLater(doRun);
1080      }
1081    
1082      /** 
1083       * Calls {@link java.awt.EventQueue#invokeAndWait} with the
1084       * specified {@link Runnable}. 
1085       */
1086      public static void invokeAndWait(Runnable doRun)
1087        throws InterruptedException,
1088        InvocationTargetException
1089      {
1090        java.awt.EventQueue.invokeAndWait(doRun);
1091      }
1092    
1093      /** 
1094       * Calls {@link java.awt.EventQueue#isDispatchThread()}.
1095       * 
1096       * @return <code>true</code> if the current thread is the current AWT event 
1097       * dispatch thread.
1098       */
1099      public static boolean isEventDispatchThread()
1100      {
1101        return java.awt.EventQueue.isDispatchThread();
1102      }
1103      
1104      /**
1105       * This method paints the given component at the given position and size.
1106       * The component will be reparented to the container given.
1107       * 
1108       * @param g The Graphics object to draw with.
1109       * @param c The Component to draw
1110       * @param p The Container to reparent to.
1111       * @param x The x coordinate to draw at.
1112       * @param y The y coordinate to draw at.
1113       * @param w The width of the drawing area.
1114       * @param h The height of the drawing area.
1115       */
1116      public static void paintComponent(Graphics g, Component c, Container p, 
1117                                        int x, int y, int w, int h)
1118      {       
1119        Container parent = c.getParent();
1120        if (parent != null)
1121          parent.remove(c);
1122        if (p != null)
1123          p.add(c);
1124        
1125        Shape savedClip = g.getClip();
1126        
1127        g.setClip(x, y, w, h);
1128        g.translate(x, y);
1129    
1130        c.paint(g);
1131        
1132        g.translate(-x, -y);
1133        g.setClip(savedClip);
1134      }
1135    
1136      /**
1137       * This method paints the given component in the given rectangle.
1138       * The component will be reparented to the container given.
1139       * 
1140       * @param g The Graphics object to draw with.
1141       * @param c The Component to draw
1142       * @param p The Container to reparent to.
1143       * @param r The rectangle that describes the drawing area.
1144       */  
1145      public static void paintComponent(Graphics g, Component c, 
1146                                        Container p, Rectangle r)
1147      {
1148        paintComponent(g, c, p, r.x, r.y, r.width, r.height);
1149      }
1150      
1151      /**
1152       * This method returns the common Frame owner used in JDialogs or
1153       * JWindow when no owner is provided.
1154       *
1155       * @return The common Frame 
1156       */
1157      static Window getOwnerFrame(Window owner)
1158      {
1159        Window result = owner;
1160        if (result == null)
1161          {
1162            if (ownerFrame == null)
1163              ownerFrame = new OwnerFrame();
1164            result = ownerFrame;
1165          }
1166        return result;
1167      }
1168    
1169      /**
1170       * Checks if left mouse button was clicked.
1171       *
1172       * @param event the event to check
1173       *
1174       * @return true if left mouse was clicked, false otherwise.
1175       */
1176      public static boolean isLeftMouseButton(MouseEvent event)
1177      {
1178        return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0);
1179      }
1180    
1181      /**
1182       * Checks if middle mouse button was clicked.
1183       *
1184       * @param event the event to check
1185       *
1186       * @return true if middle mouse was clicked, false otherwise.
1187       */
1188      public static boolean isMiddleMouseButton(MouseEvent event)
1189      {
1190        return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
1191                 == InputEvent.BUTTON2_DOWN_MASK);
1192      }
1193    
1194      /**
1195       * Checks if right mouse button was clicked.
1196       *
1197       * @param event the event to check
1198       *
1199       * @return true if right mouse was clicked, false otherwise.
1200       */
1201      public static boolean isRightMouseButton(MouseEvent event)
1202      {
1203        return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
1204                 == InputEvent.BUTTON3_DOWN_MASK);
1205      }
1206      
1207      /**
1208       * This frame should be used when constructing a Window/JDialog without
1209       * a parent. In this case, we are forced to use this frame as a window's
1210       * parent, because we simply cannot pass null instead of parent to Window
1211       * constructor, since doing it will result in NullPointerException.
1212       */
1213      private static class OwnerFrame extends Frame
1214      {
1215        public void setVisible(boolean b)
1216        {
1217          // Do nothing here. 
1218        }
1219        
1220        public boolean isShowing()
1221        {
1222          return true;
1223        }
1224      }
1225    
1226      public static boolean notifyAction(Action action,
1227                                         KeyStroke ks,
1228                                         KeyEvent event,
1229                                         Object sender,
1230                                         int modifiers)
1231      {
1232        if (action != null && action.isEnabled())
1233          {
1234            String name = (String) action.getValue(Action.ACTION_COMMAND_KEY);
1235            if (name == null
1236                && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
1237              name = new String(new char[] {event.getKeyChar()});
1238            action.actionPerformed(new ActionEvent(sender,
1239                                                   ActionEvent.ACTION_PERFORMED,
1240                                                   name, modifiers));
1241            return true;
1242          }
1243        return false;
1244      }
1245    
1246      /**
1247       * <p>Change the shared, UI-managed {@link ActionMap} for a given
1248       * component. ActionMaps are arranged in a hierarchy, in order to
1249       * encourage sharing of common actions between components. The hierarchy
1250       * unfortunately places UI-managed ActionMaps at the <em>end</em> of the
1251       * parent-pointer chain, as illustrated:</p>
1252       *
1253       * <pre>
1254       *  [{@link javax.swing.JComponent#getActionMap()}] 
1255       *          --&gt; [{@link javax.swing.ActionMap}] 
1256       *     parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapActionMap}] 
1257       *       parent --&gt; [{@link javax.swing.plaf.ActionMapUIResource}]
1258       * </pre>
1259       *
1260       * <p>Our goal with this method is to replace the first ActionMap along
1261       * this chain which is an instance of {@link ActionMapUIResource}, since
1262       * these are the ActionMaps which are supposed to be shared between
1263       * components.</p>
1264       *
1265       * <p>If the provided ActionMap is <code>null</code>, we interpret the
1266       * call as a request to remove the UI-managed ActionMap from the
1267       * component's ActionMap parent chain.</p>
1268       */
1269      public static void replaceUIActionMap(JComponent component, 
1270                                            ActionMap uiActionMap)
1271      {
1272        ActionMap child = component.getActionMap();
1273        if (child == null)
1274          component.setActionMap(uiActionMap);
1275        else
1276          {
1277            ActionMap parent = child.getParent();
1278            while (parent != null && !(parent instanceof ActionMapUIResource))
1279              {
1280                child = parent;
1281                parent = child.getParent();
1282              }
1283            // Sanity check to avoid loops.
1284            if (child != uiActionMap)
1285              child.setParent(uiActionMap);
1286          }
1287      }
1288    
1289      /**
1290       * <p>Change the shared, UI-managed {@link InputMap} for a given
1291       * component. InputMaps are arranged in a hierarchy, in order to
1292       * encourage sharing of common input mappings between components. The
1293       * hierarchy unfortunately places UI-managed InputMaps at the
1294       * <em>end</em> of the parent-pointer chain, as illustrated:</p>
1295       *
1296       * <pre>
1297       *  [{@link javax.swing.JComponent#getInputMap()}] 
1298       *          --&gt; [{@link javax.swing.InputMap}] 
1299       *     parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapWrapper}] 
1300       *       parent --&gt; [{@link javax.swing.plaf.InputMapUIResource}]
1301       * </pre>
1302       *
1303       * <p>Our goal with this method is to replace the first InputMap along
1304       * this chain which is an instance of {@link InputMapUIResource}, since
1305       * these are the InputMaps which are supposed to be shared between
1306       * components.</p>
1307       *
1308       * <p>If the provided InputMap is <code>null</code>, we interpret the
1309       * call as a request to remove the UI-managed InputMap from the
1310       * component's InputMap parent chain.</p>
1311       */
1312      public static void replaceUIInputMap(JComponent component, 
1313                                           int condition, 
1314                                           InputMap uiInputMap)
1315      {
1316        InputMap child = component.getInputMap(condition);
1317        if (child == null)
1318          component.setInputMap(condition, uiInputMap);
1319        else
1320          {
1321            InputMap parent = child.getParent();
1322            while (parent != null && !(parent instanceof InputMapUIResource))
1323              {
1324                child = parent;
1325                parent = parent.getParent();
1326              }
1327            // Sanity check to avoid loops.
1328            if (child != uiInputMap)
1329              child.setParent(uiInputMap);
1330          }
1331      }
1332    
1333      /**
1334       * Subtracts a rectangle from another and return the area as an array
1335       * of rectangles.
1336       * Returns the areas of rectA which are not covered by rectB.
1337       * If the rectangles do not overlap, or if either parameter is
1338       * <code>null</code>, a zero-size array is returned.
1339       * @param rectA The first rectangle
1340       * @param rectB The rectangle to subtract from the first
1341       * @return An array of rectangles representing the area in rectA
1342       * not overlapped by rectB
1343       */
1344      public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
1345      {
1346        if (rectA == null || rectB == null)
1347          return new Rectangle[0];
1348    
1349        Rectangle[] r = new Rectangle[4];
1350        int x1 = rectA.x;
1351        int y1 = rectA.y;
1352        int w1 = rectA.width;
1353        int h1 = rectA.height;
1354        int x2 = rectB.x;
1355        int y2 = rectB.y;
1356        int w2 = rectB.width;
1357        int h2 = rectB.height;
1358    
1359        // (outer box = rectA)
1360        // ------------- 
1361        // |_____0_____|
1362        // |  |rectB|  |
1363        // |_1|_____|_2|
1364        // |     3     |
1365        // -------------
1366        int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
1367        int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
1368        int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
1369        int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
1370        int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
1371    
1372        if (H0 > 0)
1373          r[0] = new Rectangle(x1, y1, w1, H0);
1374        else
1375          r[0] = null;
1376    
1377        if (W1 > 0 && H12 > 0)
1378          r[1] = new Rectangle(x1, y1 + H0, W1, H12);
1379        else
1380          r[1] = null;
1381    
1382        if (W2 > 0 && H12 > 0)
1383          r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
1384        else
1385          r[2] = null;
1386    
1387        if (H3 > 0)
1388          r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
1389        else
1390          r[3] = null;
1391    
1392        // sort out null objects
1393        int n = 0;
1394        for (int i = 0; i < 4; i++)
1395          if (r[i] != null)
1396            n++;
1397        Rectangle[] out = new Rectangle[n];
1398        for (int i = 3; i >= 0; i--)
1399          if (r[i] != null)
1400            out[--n] = r[i];
1401    
1402        return out;
1403      }
1404    
1405      /**
1406       * Calculates the intersection of two rectangles. The result is stored
1407       * in <code>rect</code>. This is basically the same
1408       * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1409       * create new Rectangle instances. The tradeoff is that you loose any data in
1410       * <code>rect</code>.
1411       *
1412       * @param x upper-left x coodinate of first rectangle
1413       * @param y upper-left y coodinate of first rectangle
1414       * @param w width of first rectangle
1415       * @param h height of first rectangle
1416       * @param rect a Rectangle object of the second rectangle
1417       *
1418       * @throws NullPointerException if rect is null
1419       *
1420       * @return a rectangle corresponding to the intersection of the
1421       *         two rectangles. An empty rectangle is returned if the rectangles
1422       *         do not overlap
1423       */
1424      public static Rectangle computeIntersection(int x, int y, int w, int h,
1425                                                  Rectangle rect)
1426      {
1427        int x2 = (int) rect.x;
1428        int y2 = (int) rect.y;
1429        int w2 = (int) rect.width;
1430        int h2 = (int) rect.height;
1431    
1432        int dx = (x > x2) ? x : x2;
1433        int dy = (y > y2) ? y : y2;
1434        int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1435        int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1436    
1437        if (dw >= 0 && dh >= 0)
1438          rect.setBounds(dx, dy, dw, dh);
1439        else
1440          rect.setBounds(0, 0, 0, 0);
1441    
1442        return rect;
1443      }
1444      
1445      /**
1446       * Calculates the width of a given string.
1447       *
1448       * @param fm the <code>FontMetrics</code> object to use
1449       * @param str the string
1450       * 
1451       * @return the width of the the string.
1452       */
1453      public static int computeStringWidth(FontMetrics fm, String str)
1454      {
1455        return fm.stringWidth(str);
1456      }
1457    
1458      /**
1459       * Calculates the union of two rectangles. The result is stored in
1460       * <code>rect</code>. This is basically the same as
1461       * {@link Rectangle#union(Rectangle)} except that it avoids creation of new
1462       * Rectangle objects. The tradeoff is that you loose any data in
1463       * <code>rect</code>.
1464       *
1465       * @param x upper-left x coodinate of first rectangle
1466       * @param y upper-left y coodinate of first rectangle
1467       * @param w width of first rectangle
1468       * @param h height of first rectangle
1469       * @param rect a Rectangle object of the second rectangle
1470       *
1471       * @throws NullPointerException if rect is null
1472       *
1473       * @return a rectangle corresponding to the union of the
1474       *         two rectangles; a rectangle encompassing both is returned if the
1475       *         rectangles do not overlap
1476       */
1477      public static Rectangle computeUnion(int x, int y, int w, int h,
1478                                           Rectangle rect)
1479      {
1480        int x2 = (int) rect.x;
1481        int y2 = (int) rect.y;
1482        int w2 = (int) rect.width;
1483        int h2 = (int) rect.height;
1484    
1485        int dx = (x < x2) ? x : x2;
1486        int dy = (y < y2) ? y : y2;
1487        int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1488        int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1489    
1490        if (dw >= 0 && dh >= 0)
1491          rect.setBounds(dx, dy, dw, dh);
1492        else
1493          rect.setBounds(0, 0, 0, 0);
1494        return rect;
1495      }
1496    
1497      /**
1498       * Tests if a rectangle contains another.
1499       * @param a first rectangle
1500       * @param b second rectangle
1501       * @return true if a contains b, false otherwise
1502       * @throws NullPointerException
1503       */
1504      public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
1505      {
1506        // Note: zero-size rects inclusive, differs from Rectangle.contains()
1507        return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
1508               && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
1509               && b.y + b.height <= a.y + a.height;
1510      }
1511    
1512      /**
1513       * Returns the InputMap that is provided by the ComponentUI of
1514       * <code>component</code> for the specified condition.
1515       *
1516       * @param component the component for which the InputMap is returned
1517       * @param cond the condition that specifies which of the three input
1518       *     maps should be returned, may be
1519       *     {@link JComponent#WHEN_IN_FOCUSED_WINDOW},
1520       *     {@link JComponent#WHEN_FOCUSED} or
1521       *     {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}
1522       *
1523       * @return The input map.
1524       */
1525      public static InputMap getUIInputMap(JComponent component, int cond)
1526      {
1527        if (UIManager.getUI(component) != null)
1528          // we assume here that the UI class sets the parent of the component's
1529          // InputMap, which is the correct behaviour. If it's not, then
1530          // this can be considered a bug
1531          return component.getInputMap(cond).getParent();
1532        else
1533          return null;
1534      }
1535    
1536      /**
1537       * Returns the ActionMap that is provided by the ComponentUI of
1538       * <code>component</code>.
1539       *
1540       * @param component the component for which the ActionMap is returned
1541       */
1542      public static ActionMap getUIActionMap(JComponent component)
1543      {
1544        if (UIManager.getUI(component) != null)
1545          // we assume here that the UI class sets the parent of the component's
1546          // ActionMap, which is the correct behaviour. If it's not, then
1547          // this can be considered a bug
1548          return component.getActionMap().getParent();
1549        else
1550          return null;
1551      }
1552    
1553      /**
1554       * Processes key bindings for the component that is associated with the 
1555       * key event. Note that this method does not make sense for
1556       * JComponent-derived components, except when
1557       * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is
1558       * not called.
1559       *
1560       * This method searches through the component hierarchy of the component's
1561       * top-level container to find a <code>JComponent</code> that has a binding
1562       * for the key event in the WHEN_IN_FOCUSED_WINDOW scope.
1563       *
1564       * @param ev the key event
1565       *
1566       * @return <code>true</code> if a binding has been found and processed,
1567       *         <code>false</code> otherwise
1568       *
1569       * @since 1.4
1570       */
1571      public static boolean processKeyBindings(KeyEvent ev)
1572      {
1573        Component c = ev.getComponent();
1574        KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev);
1575        KeyboardManager km = KeyboardManager.getManager();
1576        return km.processKeyStroke(c, s, ev);
1577      }
1578      
1579      /**
1580       * Returns a string representing one of the horizontal alignment codes
1581       * defined in the {@link SwingConstants} interface.  The following table
1582       * lists the constants and return values:
1583       * <p>
1584       * <table border="0">
1585       * <tr>
1586       *   <th>Code:</th><th>Returned String:</th>
1587       * </tr>
1588       * <tr>
1589       *   <td>{@link SwingConstants#CENTER}</td>
1590       *   <td><code>"CENTER"</code></td>
1591       * </tr>
1592       * <tr>
1593       *   <td>{@link SwingConstants#LEFT}</td>
1594       *   <td><code>"LEFT"</code></td>
1595       * </tr>
1596       * <tr>
1597       *   <td>{@link SwingConstants#RIGHT}</td>
1598       *   <td><code>"RIGHT"</code></td>
1599       * </tr>
1600       * <tr>
1601       *   <td>{@link SwingConstants#LEADING}</td>
1602       *   <td><code>"LEADING"</code></td>
1603       * </tr>
1604       * <tr>
1605       *   <td>{@link SwingConstants#TRAILING}</td>
1606       *   <td><code>"TRAILING"</code></td>
1607       * </tr>
1608       * </table>
1609       * </p>
1610       * If the supplied code is not one of those listed, this methods will throw
1611       * an {@link IllegalArgumentException}.
1612       * 
1613       * @param code  the code.
1614       * 
1615       * @return A string representing the given code.
1616       */
1617      static String convertHorizontalAlignmentCodeToString(int code)
1618      {
1619        switch (code) 
1620        {
1621          case SwingConstants.CENTER: 
1622            return "CENTER";
1623          case SwingConstants.LEFT:
1624            return "LEFT";
1625          case SwingConstants.RIGHT:
1626            return "RIGHT";
1627          case SwingConstants.LEADING:
1628            return "LEADING";
1629          case SwingConstants.TRAILING:
1630            return "TRAILING";
1631          default:
1632            throw new IllegalArgumentException("Unrecognised code: " + code);
1633        }
1634      }
1635      
1636      /**
1637       * Returns a string representing one of the vertical alignment codes
1638       * defined in the {@link SwingConstants} interface.  The following table
1639       * lists the constants and return values:
1640       * <p>
1641       * <table border="0">
1642       * <tr>
1643       *   <th>Code:</th><th>Returned String:</th>
1644       * </tr>
1645       * <tr>
1646       *   <td>{@link SwingConstants#CENTER}</td>
1647       *   <td><code>"CENTER"</code></td>
1648       * </tr>
1649       * <tr>
1650       *   <td>{@link SwingConstants#TOP}</td>
1651       *   <td><code>"TOP"</code></td>
1652       * </tr>
1653       * <tr>
1654       *   <td>{@link SwingConstants#BOTTOM}</td>
1655       *   <td><code>"BOTTOM"</code></td>
1656       * </tr>
1657       * </table>
1658       * </p>
1659       * If the supplied code is not one of those listed, this methods will throw
1660       * an {@link IllegalArgumentException}.
1661       * 
1662       * @param code  the code.
1663       * 
1664       * @return A string representing the given code.
1665       */
1666      static String convertVerticalAlignmentCodeToString(int code)
1667      {
1668        switch (code)
1669        {
1670          case SwingConstants.CENTER:
1671            return "CENTER";
1672          case SwingConstants.TOP:
1673            return "TOP";
1674          case SwingConstants.BOTTOM:
1675            return "BOTTOM";
1676          default:
1677            throw new IllegalArgumentException("Unrecognised code: " + code);
1678        }
1679      }
1680      
1681      /**
1682       * Returns a string representing one of the default operation codes
1683       * defined in the {@link WindowConstants} interface.  The following table
1684       * lists the constants and return values:
1685       * <p>
1686       * <table border="0">
1687       * <tr>
1688       *   <th>Code:</th><th>Returned String:</th>
1689       * </tr>
1690       * <tr>
1691       *   <td>{@link WindowConstants#DO_NOTHING_ON_CLOSE}</td>
1692       *   <td><code>"DO_NOTHING_ON_CLOSE"</code></td>
1693       * </tr>
1694       * <tr>
1695       *   <td>{@link WindowConstants#HIDE_ON_CLOSE}</td>
1696       *   <td><code>"HIDE_ON_CLOSE"</code></td>
1697       * </tr>
1698       * <tr>
1699       *   <td>{@link WindowConstants#DISPOSE_ON_CLOSE}</td>
1700       *   <td><code>"DISPOSE_ON_CLOSE"</code></td>
1701       * </tr>
1702       * <tr>
1703       *   <td>{@link WindowConstants#EXIT_ON_CLOSE}</td>
1704       *   <td><code>"EXIT_ON_CLOSE"</code></td>
1705       * </tr>
1706       * </table>
1707       * </p>
1708       * If the supplied code is not one of those listed, this method will throw
1709       * an {@link IllegalArgumentException}.
1710       * 
1711       * @param code  the code.
1712       * 
1713       * @return A string representing the given code.
1714       */
1715      static String convertWindowConstantToString(int code) 
1716      {
1717        switch (code)
1718        {
1719          case WindowConstants.DO_NOTHING_ON_CLOSE:
1720            return "DO_NOTHING_ON_CLOSE";
1721          case WindowConstants.HIDE_ON_CLOSE:
1722            return "HIDE_ON_CLOSE";
1723          case WindowConstants.DISPOSE_ON_CLOSE:
1724            return "DISPOSE_ON_CLOSE";
1725          case WindowConstants.EXIT_ON_CLOSE:
1726            return "EXIT_ON_CLOSE";
1727          default:
1728            throw new IllegalArgumentException("Unrecognised code: " + code);
1729        }
1730      }
1731    
1732      /**
1733       * Converts a rectangle in the coordinate system of a child component into
1734       * a rectangle of one of it's Ancestors. The result is stored in the input
1735       * rectangle.
1736       *
1737       * @param comp the child component
1738       * @param r the rectangle to convert
1739       * @param ancestor the ancestor component
1740       */
1741      static void convertRectangleToAncestor(Component comp, Rectangle r,
1742                                             Component ancestor)
1743      {
1744        if (comp == ancestor)
1745          return;
1746    
1747        r.x += comp.getX();
1748        r.y += comp.getY();
1749    
1750        Component parent = comp.getParent();
1751        if (parent != null && parent != ancestor)
1752          convertRectangleToAncestor(parent, r, ancestor);
1753      }
1754    }