classpath
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] fixes to LightweightDispatcher and SwingUtilities


From: graydon hoare
Subject: [PATCH] fixes to LightweightDispatcher and SwingUtilities
Date: Mon, 02 Feb 2004 16:04:00 -0500
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6b) Gecko/20031205 Thunderbird/0.4

hi,

this patch implements and documents a number of functions in the javax.swing.SwingUtilities class, and updates java.awt.Container.LightweightDispatcher to use them. this corrects several behavioral bugs in the LightweightDispatcher, and moves most of the component-position arithmetic into the utility class, where it can be reused in other swing component logic.

it also includes a small touchup to the javax.swing.plaf.basic.BasicGraphicsUtils, which uses a method in SwingUtilities to perform layout, and has to call it slightly more carefully now.

ok to commit?

-graydon

2004-02-02  Graydon Hoare  <address@hidden>

        * javax/swing/SwingUtilities.java: Many new functions.
        * java/awt/Container.java (LightweightDispatcher): Reimplement.
        * javax/swing/basic/BasicGraphicsUtils.java
        (getPreferredButtonSize): Start layout from top-left corner.

--- java/awt/Container.java     26 Jan 2004 21:55:41 -0000      1.32
+++ java/awt/Container.java     2 Feb 2004 20:53:03 -0000
@@ -52,6 +52,7 @@
 import java.util.EventListener;
 import java.util.Set;
 import javax.accessibility.Accessible;
+import javax.swing.SwingUtilities;
 
 /**
  * A generic window toolkit object that acts as a container for other objects.
@@ -1541,54 +1542,59 @@
     nativeContainer = c;
   }
 
-  void dispose()
-  {
-  }
-
   void enableEvents(long l)
   {
     eventMask |= l;
   }
 
-  void mouseExit (MouseEvent me, int x, int y)
-  {
-  }
-
   void acquireComponentForMouseEvent (MouseEvent me)
   {
     int x = me.getX ();
     int y = me.getY ();
-
     Component candidate = mouseEventTarget;
 
-    boolean candidate_is_container_with_children = 
-      ((candidate != null)
-       && (candidate instanceof Container)
-       && (((Container)candidate).getComponentCount () > 0));
-
-    boolean candidate_does_not_contain_point =
-      ((candidate != null)
-       && (! candidate.contains (x - candidate.getX (),
-                                 y - candidate.getY ())));
-
-    if (candidate == null
-        || candidate_is_container_with_children
-        || candidate_does_not_contain_point)
+    while(candidate != null)
+      {
+        if (candidate.isShowing())
+          {
+            // Convert our point to the candidate's parent's space.
+            Point cp = SwingUtilities.convertPoint(nativeContainer, x, y, 
candidate);
+            
+            // If the event lands inside candidate, we have a hit.
+            if (candidate.contains(cp.x, cp.y))
       {
-        // Try to reacquire.
-        candidate = nativeContainer.findComponentAt (x, y);
+                // If candidate has children, we refine the hit.
+                if (candidate instanceof Container &&
+                    ((Container)candidate).getComponentCount() > 0)            
  
+                  candidate = SwingUtilities.getDeepestComponentAt(candidate, 
cp.x, cp.y);
+                break;
+              }
+          }        
+        // If candidate isn't showing or doesn't contain point, we back out a 
level.
+        candidate = candidate.getParent();
+      }
+    
+    if (candidate == null)
+      {
+        // We either lost, or never had, a candidate; acquire from our native.
+        candidate = 
+          SwingUtilities.getDeepestComponentAt(nativeContainer, x, y);
       }
 
+
+    // If our candidate is new, inform the old target we're leaving.
     if (mouseEventTarget != null
+        && mouseEventTarget.isShowing()
         && mouseEventTarget != candidate)
       {
-        int nx = x - mouseEventTarget.getX ();
-        int ny = y - mouseEventTarget.getY ();
+        Point tp = 
+          SwingUtilities.convertPoint(nativeContainer, 
+                                      x, y, mouseEventTarget);
         MouseEvent exited = new MouseEvent (mouseEventTarget, 
                                             MouseEvent.MOUSE_EXITED,
                                             me.getWhen (), 
                                             me.getModifiers (), 
-                                            nx, ny,
+                                            tp.x, tp.y,
                                             me.getClickCount (),
                                             me.isPopupTrigger (),
                                             me.getButton ());
@@ -1596,25 +1602,22 @@
         mouseEventTarget = null;
       }
 
+    // If we have a candidate, maybe enter it.
     if (candidate != null)
       {
-        // Possibly set new state.
         if (candidate.isLightweight() 
+            && candidate.isShowing()
             && candidate != nativeContainer
             && candidate != mouseEventTarget)
          {
-                       
             mouseEventTarget = candidate;
-                       
-            int nx = x - mouseEventTarget.getX ();
-            int ny = y - mouseEventTarget.getY ();
-                       
-            // If acquired, enter it.
+            Point cp = SwingUtilities.convertPoint(nativeContainer, 
+                                                   x, y, candidate);
             MouseEvent entered = new MouseEvent (mouseEventTarget, 
                                                  MouseEvent.MOUSE_ENTERED,
                                                  me.getWhen (), 
                                                  me.getModifiers (), 
-                                                 nx, ny,
+                                                 cp.x, cp.y,
                                                  me.getClickCount (),
                                                  me.isPopupTrigger (),
                                                  me.getButton ());
@@ -1633,22 +1636,15 @@
         MouseEvent me = (MouseEvent) e;
         acquireComponentForMouseEvent (me);
 
-        // Avoid dispatching an ENTERED event twice
+        // Avoid dispatching an ENTERED event twice.
         if (mouseEventTarget != null
+            && mouseEventTarget.isShowing()
             && e.getID() != MouseEvent.MOUSE_ENTERED)
           {
-            // Calculate point translation for the event target.
-            // We use absolute location on screen rather than relative
-            // location because the event target might be a nested child.
-            Point parentLocation = nativeContainer.getLocationOnScreen();
-            Point childLocation = mouseEventTarget.getLocationOnScreen();
-            me.translatePoint(parentLocation.x - childLocation.x,
-                              parentLocation.y - childLocation.y);
-
-            Component oldSource = (Component) me.getSource ();
-            me.setSource (mouseEventTarget);
-            mouseEventTarget.dispatchEvent (me);
-            me.setSource (oldSource);
+            MouseEvent newEvt = 
+              SwingUtilities.convertMouseEvent(nativeContainer, me, 
+                                               mouseEventTarget);
+            mouseEventTarget.dispatchEvent(newEvt);
           }
       }
     else if (e instanceof KeyEvent && focus != null)
--- javax/swing/plaf/basic/BasicGraphicsUtils.java      27 Jan 2004 18:55:11 
-0000      1.6
+++ javax/swing/plaf/basic/BasicGraphicsUtils.java      2 Feb 2004 20:53:03 
-0000
@@ -614,8 +614,8 @@
       b.getToolkit().getFontMetrics(b.getFont()), // see comment above
       b.getText(),
       b.getIcon(),
-      b.getVerticalAlignment(),
-      b.getHorizontalAlignment(),
+      SwingUtilities.TOP,    // important:
+      SwingUtilities.LEFT,   // large vrect, stick to the top left
       b.getVerticalTextPosition(),
       b.getHorizontalTextPosition(),
       viewRect, iconRect, textRect,
--- javax/swing/SwingUtilities.java     22 Jan 2004 09:54:15 -0000      1.4
+++ javax/swing/SwingUtilities.java     2 Feb 2004 20:53:03 -0000
@@ -35,10 +35,11 @@
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
-
 package javax.swing;
 
+import java.applet.Applet;
 import java.awt.Component;
+import java.awt.ComponentOrientation;
 import java.awt.Container;
 import java.awt.EventQueue;
 import java.awt.Font;
@@ -55,81 +56,623 @@
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleStateSet;
 
+
+/**
+ * This class contains a number of static utility functions which are
+ * useful when drawing swing components, dispatching events, or calculating
+ * regions which need painting.
+ *
+ * @author Graydon Hoare (graydon&064;redhat.com)
+ */
 public class SwingUtilities implements SwingConstants
 {
+
+  /**
+   * Calculates the portion of the base rectangle which is inside the
+   * insets.
+   *
+   * @param base The rectangle to apply the insets to
+   * @param insets The insets to apply to the base rectangle
+   * @param ret A rectangle to use for storing the return value, or
+   * <code>null</code>
+   *
+   * @return The calculated area inside the base rectangle and its insets,
+   * either stored in ret or a new Rectangle if ret is <code>null</code>
+   *
+   * @see #calculateInnerArea
+   */
+  public static Rectangle calculateInsetArea(Rectangle base, Insets insets,
+                                             Rectangle ret)
+  {
+    if (ret == null)
+      ret = new Rectangle();
+    ret.setBounds(base.x + insets.left, base.y + insets.top,
+                  base.width - (insets.left + insets.right),
+                  base.height - (insets.top + insets.bottom));
+    return ret;
+  }
+
+  /**
+   * Calculates the portion of the component's bounds which is inside the
+   * component's border insets. This area is usually the area a component
+   * should confine its painting to.
+   *
+   * @param c The component to measure the bounds of
+   * @param r A Rectangle to store the return value in, or
+   * <code>null</code>
+   *
+   * @return The calculated area inside the component and its border
+   * insets
+   *
+   * @see #calculateInsetArea
+   */
+  public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
+  {
+    return calculateInsetArea(c.getBounds(), c.getInsets(), r);
+  }
+
+  /**
+   * Calculates the bounds of a component in the component's own coordinate
+   * space. The result has the same height and width as the component's
+   * bounds, but its location is set to (0,0).
+   *
+   * @param aComponent The component to measure
+   *
+   * @return The component's bounds in its local coordinate space
+   */
+  public static Rectangle getLocalBounds(Component aComponent)
+  {
+    Rectangle bounds = aComponent.getBounds();
+    return new Rectangle(0, 0, bounds.x, bounds.y);
+  }
+
+  /**
+   * Returns the font metrics object for a given font. The metrics can be
+   * used to calculate crude bounding boxes and positioning information,
+   * for laying out components with textual elements.
+   *
+   * @param font The font to get metrics for
+   *
+   * @return The font's metrics
+   *
+   * @see java.awt.font.GlyphMetrics
+   */
   public static FontMetrics getFontMetrics (Font font)
   {
     return Toolkit.getDefaultToolkit ().getFontMetrics (font);
   }
 
-  public static JRootPane getRootPane (Component a)
+  /**
+   * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
+   * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
+   *
+   * @param comp The component to get the JRootPane of
+   *
+   * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
+   * 
+   * @see javax.swing.RootPaneContainer#getRootPane
+   * @see #getAncestorOfClass
+   */
+  public static JRootPane getRootPane(Component comp)
   {
-    if (a instanceof JRootPane)
-      return (JRootPane) a;
+    if (comp instanceof RootPaneContainer)
+      return ((RootPaneContainer)comp).getRootPane();
+    else
+      return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
+  }
        
-    a = a.getParent();
+  /**
+   * Returns the least ancestor of <code>comp</code> which has the
+   * specified name.
+   *
+   * @param name The name to search for
+   * @param comp The component to search the ancestors of
+   *
+   * @return The nearest ancestor of <code>comp</code> with the given
+   * name, or <code>null</code> if no such ancestor exists
+   *
+   * @see java.awt.Component#getName
+   * @see #getAncestorOfClass
+   */
+  public static Container getAncestorNamed(String name, Component comp)
+  {
+    while (comp != null && (comp.getName() != name))
+      comp = comp.getParent();
+    return (Container) comp;
+  }
 
-    if (a != null)
+  /**
+   * Returns the least ancestor of <code>comp</code> which is an instance
+   * of the specified class.
+   *
+   * @param c The class to search for
+   * @param comp The component to search the ancestors of
+   *
+   * @return The nearest ancestor of <code>comp</code> which is an instance
+   * of the given class, or <code>null</code> if no such ancestor exists
+   *
+   * @see #getAncestorOfClass
+   * @see #windowForComponent
+   * @see 
+   * 
+   */
+  public static Container getAncestorOfClass(Class c, Component comp)
       {
-        return getRootPane(a);
+    while (comp != null && (! c.isInstance(comp)))
+      comp = comp.getParent();
+    return (Container) comp;
       }
     
+  /**
+   * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
+   *
+   * @param comp The component to search for an ancestor window 
+   *
+   * @return An ancestral window, or <code>null</code> if none exists
+   */
+  public static Window windowForComponent(Component comp)
+  {
+    return (Window) getAncestorOfClass(Window.class, comp);
+  }
+
+  /**
+   * Returns the "root" of the component tree containint <code>comp</code>
+   * The root is defined as either the <em>least</em> ancestor of
+   * <code>comp</code> which is a address@hidden Window}, or the 
<em>greatest</em>
+   * ancestor of <code>comp</code> which is a address@hidden Applet} if no 
address@hidden
+   * Window} ancestors are found.
+   *
+   * @param comp The component to search for a root
+   *
+   * @return The root of the component's tree, or <code>null</code>
+   */
+  public static Component getRoot(Component comp)
+  {
+    Applet app = null;
+    Window win = null;
+
+    while (comp != null)
+      {
+        if (win == null && comp instanceof Window)
+          win = (Window) comp;
+        else if (comp instanceof Applet)
+          app = (Applet) comp;
+        comp = comp.getParent();
+      }
+
+    if (win != null)
+      return win;
+    else
+      return app;
+  }
+
+  /**
+   * Return true if a descends from b, in other words if b is an
+   * ancestor of a.
+   *
+   * @param a The child to search the ancestry of
+   * @param b The potential ancestor to search for
+   *
+   * @return true if a is a descendent of b, false otherwise
+   */
+  public static boolean isDescendingFrom(Component a, Component b)
+  {
+    while (true)
+      {
+        if (a == null || b == null)
+          return false;
+        if (a == b)
+          return true;
+        a = a.getParent();
+      }
+  }
+
+  /**
+   * Returns the deepest descendent of parent which is both visible and
+   * contains the point <code>(x,y)</code>. Returns parent when either
+   * parent is not a container, or has no children which contain
+   * <code>(x,y)</code>. Returns <code>null</code> when either
+   * <code>(x,y)</code> is outside the bounds of parent, or parent is
+   * <code>null</code>.
+   * 
+   * @param parent The component to search the descendents of
+   * @param x Horizontal coordinate to search for
+   * @param y Vertical coordinate to search for
+   *
+   * @return A component containing <code>(x,y)</code>, or
+   * <code>null</code>
+   *
+   * @see java.awt.Container#findComponentAt
+   */
+  public static Component getDeepestComponentAt(Component parent, int x, int y)
+  {
+    if (parent == null || (! parent.contains(x, y)))
     return null;
+
+    if (! (parent instanceof Container))
+      return parent;
+
+    Container c = (Container) parent;
+    return c.findComponentAt(x, y);
   }
 
-  public static void updateComponentTreeUI(JFrame comp)
+  /**
+   * Converts a point from a component's local coordinate space to "screen"
+   * coordinates (such as the coordinate space mouse events are delivered
+   * in). This operation is equivalent to translating the point by the
+   * location of the component (which is the origin of its coordinate
+   * space).
+   *
+   * @param p The point to convert
+   * @param c The component which the point is expressed in terms of
+   *
+   * @see convertPointFromScreen
+   */
+  public static void convertPointToScreen(Point p, Component c)
   {
+    Point c0 = c.getLocationOnScreen();
+    p.translate(c0.x, c0.y);
   }
 
-  public static String layoutCompoundLabel(JComponent c, 
-                                           FontMetrics fm,
-                                           String text,
-                                           Icon i,
-                                           int vert_a, 
-                                           int hor_i, 
-                                           int vert_text_pos,
-                                           int hor_text_pos, 
-                                           Rectangle vr,
-                                           Rectangle ir, 
-                                           Rectangle tr,
-                                           int gap)
+  /**
+   * Converts a point from "screen" coordinates (such as the coordinate
+   * space mouse events are delivered in) to a component's local coordinate
+   * space. This operation is equivalent to translating the point by the
+   * negation of the component's location (which is the origin of its
+   * coordinate space).
+   *
+   * @param p The point to convert
+   * @param c The component which the point should be expressed in terms of
+   */
+  public static void convertPointFromScreen(Point p, Component c)
+  {
+    Point c0 = c.getLocationOnScreen();
+    p.translate(-c0.x, -c0.y);
+  }
+
+  /**
+   * Converts a point <code>(x,y)</code> from the coordinate space of one
+   * component to another. This is equivalent to converting the point from
+   * <code>source</code> space to screen space, then back from screen space
+   * to <code>destination</code> space. If exactly one of the two
+   * Components is <code>null</code>, it is taken to refer to the root
+   * ancestor of the other component. If both are <code>null</code>, no
+   * transformation is done.
+   *
+   * @param source The component which the point is expressed in terms of
+   * @param x Horizontal coordinate of point to transform
+   * @param y Vertical coordinate of point to transform
+   * @param destination The component which the return value will be
+   * expressed in terms of
+   *
+   * @return The point <code>(x,y)</code> converted from the coordinate space 
of the
+   * source component to the coordinate space of the destination component
+   *
+   * @see #convertPointToScreen
+   * @see #convertPointFromScreen
+   * @see #convertRectangle
+   * @see #getRoot
+   */
+  public static Point convertPoint(Component source, int x, int y,
+                                   Component destination)
+  {
+    Point pt = new Point(x, y);
+
+    if (source == null && destination == null)
+      return pt;
+
+    if (source == null)
+      source = getRoot(destination);
+
+    if (destination == null)
+      destination = getRoot(source);
+
+    convertPointToScreen(pt, source);
+    convertPointFromScreen(pt, destination);
+
+    return pt;
+  }
+
+  
+  /**
+   * Converts a rectangle from the coordinate space of one component to
+   * another. This is equivalent to converting the rectangle from
+   * <code>source</code> space to screen space, then back from screen space
+   * to <code>destination</code> space. If exactly one of the two
+   * Components is <code>null</code>, it is taken to refer to the root
+   * ancestor of the other component. If both are <code>null</code>, no
+   * transformation is done.
+   *
+   * @param source The component which the rectangle is expressed in terms of
+   * @param rect The rectangle to convert
+   * @param destination The component which the return value will be
+   * expressed in terms of
+   *
+   * @return A new rectangle, equal in size to the input rectangle, but
+   * with its position converted from the coordinate space of the source
+   * component to the coordinate space of the destination component
+   *
+   * @see #convertPointToScreen
+   * @see #convertPointFromScreen
+   * @see #convertPoint
+   * @see #getRoot
+   */
+  public static Rectangle convertRectangle(Component source,
+                                           Rectangle rect,
+                                           Component destination)
   {
-    // view rect 'vr' already ok, 
-    // we need to compute ir (icon rect) and tr (text-rect)
+    Point pt = convertPoint(source, rect.x, rect.y, destination);
+    return new Rectangle(pt.x, pt.y, rect.width, rect.height);
+  }
        
-    int next_x = vr.x;
-    int next_y = vr.y;
+  /**
+   * Convert a mouse event which refrers to one component to another.  This
+   * includes changing the mouse event's coordinate space, as well as the
+   * source property of the event. If <code>source</code> is
+   * <code>null</code>, it is taken to refer to <code>destination</code>'s
+   * root component. If <code>destination</code> is <code>null</code>, the
+   * new event will remain expressed in <code>source</code>'s coordinate
+   * system.
+   *
+   * @param source The component the mouse event currently refers to
+   * @param sourceEvent The mouse event to convert
+   * @param destination The component the new mouse event should refer to
+   *
+   * @return A new mouse event expressed in terms of the destination
+   * component's coordinate space, and with the destination component as
+   * its source
+   *
+   * @see #convertPoint
+   */
+  public static MouseEvent convertMouseEvent(Component source,
+                                             MouseEvent sourceEvent,
+                                             Component destination)
+  {
+    Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
+                               destination);
        
-    ir.height = ir.width = ir.y = ir.x = 0;
+    return new MouseEvent(destination, sourceEvent.getID(),
+                          sourceEvent.getWhen(), sourceEvent.getModifiers(),
+                          newpt.x, newpt.y, sourceEvent.getClickCount(),
+                          sourceEvent.isPopupTrigger(), 
sourceEvent.getButton());
+  }
 
-    if (i != null)
+  /**
+   * Recursively walk the component tree under <code>comp</code> calling
+   * <code>updateUI</code> on each address@hidden JComponent} found. This 
causes
+   * the entire tree to re-initialize its UI delegates.
+   *
+   * @param comp The component to walk the children of, calling 
<code>updateUI</code>
+   */
+  public static void updateComponentTreeUI(Component comp)
       {
-        ir.x = vr.x;
-        ir.y = vr.y;
-        ir.width = i.getIconWidth();
-        ir.height = i.getIconWidth();
+    if (comp == null)
+      return;
 
+    if (comp instanceof Container)
+      {
+        Component[] children = ((Container)comp).getComponents();
+        for (int i = 0; i < children.length; ++i)
+          updateComponentTreeUI(children[i]);
+      }
 
-        next_x += gap + i.getIconWidth();
-        next_y += gap + i.getIconHeight();
+    if (comp instanceof JComponent)
+      ((JComponent)comp).updateUI();
       }
        
-    tr.x = next_x;
-    tr.y = vr.y; // + (vr.height/2);
+  /**
+   * <p>Layout a "compound label" consisting of a text string and an icon
+   * which is to be placed near the rendered text. Once the text and icon
+   * are laid out, the text rectangle and icon rectangle parameters are
+   * altered to store the calculated positions.</p>
+   *
+   * <p>The size of the text is calculated from the provided font metrics
+   * object.  This object should be the metrics of the font you intend to
+   * paint the label with.</p>
+   *
+   * <p>The position values control where the text is placed relative to
+   * the icon. The horizontal position value should be one of the constants
+   * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
+   * vertical position value should be one fo the constants
+   * <code>TOP</code>, <code>BOTTOM</code>, <code>CENTER</code>.</p>
+   *
+   * <p>The text-icon gap value controls the number of pixels between the
+   * icon and the text.</p>
+   *
+   * <p>The alignment values control where the text and icon are placed, as
+   * a combined unit, within the view rectangle. The horizontal alignment
+   * value should be one of the constants <code>LEADING</code>,
+   * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
+   * <code>CENTER</code>. The vertical alignment valus should be one of the
+   * constants <code>TOP</code>, <code>BOTTOM</code> or
+   * <code>CENTER</code>.</p>
+   *
+   * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
+   * given for horizontal alignment, they are interpreted relative to the
+   * provided component's orientation property, a constant in the 
address@hidden
+   * java.awt.ComponentOrientation} class. For example, if the component's
+   * orientation is <code>LEFT_TO_RIGHT</code>, then the
+   * <code>LEADING</code> alignment is a synonym for <code>LEFT</code> and
+   * the <code>TRAILING</code> alignment is a synonym for
+   * <code>RIGHT</code></p>
+   *
+   * <p>If the text and icon are equal to or larger than the view
+   * rectangle, the horizontal and vertical alignment values have no
+   * affect.</p>
+   *
+   * @param c A component used for its orientation value
+   * @param fm The font metrics used to measure the text
+   * @param text The text to place in the compound label
+   * @param icon The icon to place next to the text
+   * @param verticalAlignment The vertical alignment of the label relative
+   * to its component
+   * @param horizontalAlignment The horizontal alignment of the label
+   * relative to its component
+   * @param verticalTextPosition The vertical position of the label's text
+   * relative to its icon
+   * @param horizontalTextPosition The horizontal position of the label's
+   * text relative to its icon
+   * @param viewR The view rectangle, specifying the area which layout is
+   * constrained to
+   * @param iconR A rectangle which is modified to hold the laid-out
+   * position of the icon
+   * @param textR A rectangle which is modified to hold the laid-out
+   * position of the text
+   * @param textIconGap The distance between text and icon
+   *
+   * @return The string of characters, possibly truncated with an elipsis,
+   * which is laid out in this label
+   */
+  public static String layoutCompoundLabel(JComponent c, 
+                                           FontMetrics fm,
+                                           String text, 
+                                           Icon icon, 
+                                           int verticalAlignment,
+                                           int horizontalAlignment, 
+                                           int verticalTextPosition,
+                                           int horizontalTextPosition, 
+                                           Rectangle viewR,
+                                           Rectangle iconR, 
+                                           Rectangle textR, 
+                                           int textIconGap)
+  {
 
-    tr.width  = fm.stringWidth(text);
-    tr.height = fm.getHeight(); // +  fm.getAscent()/2;
+    // Work out basic height and width.
 
-    return text;
+    if (icon == null)
+      {
+        textIconGap = 0;
+        iconR.width = 0;
+        iconR.height = 0;
+      }
+      else
+      {
+        iconR.width = icon.getIconWidth();
+        iconR.height = icon.getIconWidth();
   }
+    textR.width = fm.stringWidth(text);
+    textR.height = fm.getHeight(); 
 
+    // Work out the position of text and icon, assuming the top-left coord
+    // starts at (0,0). We will fix that up momentarily, after these
+    // "position" decisions are made and we look at alignment.
+
+    switch (horizontalTextPosition)
+      {
+      case LEFT:
+        textR.x = 0;
+        iconR.x = textR.width + textIconGap;
+        break;
+      case RIGHT:
+        iconR.x = 0;
+        textR.x = iconR.width + textIconGap;
+        break;
+      case CENTER:
+        int centerLine = Math.max(textR.width, iconR.width) / 2;
+        textR.x = centerLine - textR.width/2;
+        iconR.x = centerLine - iconR.width/2;
+        break;
 }
 
+    switch (verticalTextPosition)
+      {
+      case TOP:
+        textR.y = 0;
+        iconR.y = textR.height + textIconGap;
+        break;
+      case BOTTOM:
+        iconR.y = 0;
+        textR.y = iconR.height + textIconGap;
+        break;
+      case CENTER:
+        int centerLine = Math.max(textR.height, iconR.height) / 2;
+        textR.y = centerLine - textR.height/2;
+        iconR.y = centerLine - iconR.height/2;
+        break;
+      }
 
+    // Fix up the orientation-based alignments.
 
+    if (horizontalAlignment == LEADING)
+      {
+        if (c.getComponentOrientation() == ComponentOrientation.LEFT_TO_RIGHT)
+          horizontalAlignment = LEFT;
+        else if (c.getComponentOrientation() == 
ComponentOrientation.RIGHT_TO_LEFT)
+          horizontalAlignment = RIGHT;
+      }
+    else if (horizontalAlignment == TRAILING)
+      {
+        if (c.getComponentOrientation() == ComponentOrientation.LEFT_TO_RIGHT)
+          horizontalAlignment = RIGHT;
+        else if (c.getComponentOrientation() == 
ComponentOrientation.RIGHT_TO_LEFT)
+          horizontalAlignment = LEFT;
+      }
 
+    // The two rectangles are laid out correctly now, but only assuming
+    // that their upper left corner is at (0,0). If we have any alignment other
+    // than TOP and LEFT, we need to adjust them.
+
+    Rectangle u = textR.union(iconR);
+    int horizontalAdjustment = viewR.x;
+    int verticalAdjustment = viewR.y;
+    switch (verticalAlignment)
+      {
+      case TOP:
+        break;
+      case BOTTOM:
+        verticalAdjustment += (viewR.height - u.height);
+        break;
+      case CENTER:
+        verticalAdjustment += ((viewR.height/2) - (u.height/2));
+        break;
+      }
+    switch (horizontalAlignment)
+      {
+      case LEFT:
+        break;
+      case RIGHT:
+        horizontalAdjustment += (viewR.width - u.width);
+        break;
+      case CENTER:
+        horizontalAdjustment += ((viewR.width/2) - (u.width/2));
+        break;
+      }
 
+    iconR.x += horizontalAdjustment;
+    iconR.y += verticalAdjustment;
 
+    textR.x += horizontalAdjustment;
+    textR.y += verticalAdjustment;
 
+    return text;
+  }
+
+  /** 
+   * Calls address@hidden java.awt.EventQueue.invokeLater} with the
+   * specified address@hidden Runnable}. 
+   */
+  public static void invokeLater(Runnable doRun)
+  {
+    java.awt.EventQueue.invokeLater(doRun);
+  }
+
+  /** 
+   * Calls address@hidden java.awt.EventQueue.invokeAndWait} with the
+   * specified address@hidden Runnable}. 
+   */
+  public static void invokeAndWait(Runnable doRun)
+    throws InterruptedException,
+    InvocationTargetException
+  {
+    java.awt.EventQueue.invokeAndWait(doRun);
+  }
 
+  /** 
+   * Calls address@hidden java.awt.EventQueue.isEventDispatchThread}.
+   */
+  public static boolean isEventDispatchThread()
+  {
+    return java.awt.EventQueue.isDispatchThread();
+  }
 
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]