001/* SwingUtilities.java --
002   Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import java.applet.Applet;
042import java.awt.Component;
043import java.awt.Container;
044import java.awt.FontMetrics;
045import java.awt.Frame;
046import java.awt.Graphics;
047import java.awt.Insets;
048import java.awt.KeyboardFocusManager;
049import java.awt.Point;
050import java.awt.Rectangle;
051import java.awt.Shape;
052import java.awt.Window;
053import java.awt.event.ActionEvent;
054import java.awt.event.InputEvent;
055import java.awt.event.KeyEvent;
056import java.awt.event.MouseEvent;
057import java.lang.reflect.InvocationTargetException;
058
059import javax.accessibility.Accessible;
060import javax.accessibility.AccessibleStateSet;
061import javax.swing.plaf.ActionMapUIResource;
062import javax.swing.plaf.InputMapUIResource;
063import javax.swing.plaf.basic.BasicHTML;
064import 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 */
074public 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}