Adds clipping behavior to Image (which necessitated an update to the setElement() method in UIObject and Widget to support arbitrary changes to a widget's root element). Also lays the foundation for image bundles with AbstractImagePrototype. Note the addition clear.cache.gif, which is required for clipped images but will now be a standard well-known image. It's a 1x1 pixel transparent image that can be cached infinitely. It might be useful for other purposes -- even in application code -- in addition to clipped Image.java.

Patch by: rdayal
Review by: bruce

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@726 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/javadoc/com/google/gwt/examples/ImageExample.java b/user/javadoc/com/google/gwt/examples/ImageExample.java
index 41d640c..7777d98 100644
--- a/user/javadoc/com/google/gwt/examples/ImageExample.java
+++ b/user/javadoc/com/google/gwt/examples/ImageExample.java
@@ -22,35 +22,59 @@
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ClickListener;
 
 public class ImageExample implements EntryPoint {
 
   private Label lbl = new Label();
+  private Button btn = new Button("Clip this image");
+  private Button btn2 = new Button("Restore image");
 
   public void onModuleLoad() {
-    // Create an image, not yet referencing a URL.
-    Image image = new Image();
+    // Create an image, not yet referencing a URL. We make it final so that we
+    // can manipulate the image object within the ClickHandlers for the buttons
+    final Image image = new Image();
 
     // Hook up a load listener, so that we can find out when it loads (or
-    // fails to, as the case may be).
+    // fails to, as the case may be). Once the image loads, we can enable the
+    // buttons that are used to manipulate the images
     image.addLoadListener(new LoadListener() {
-      public void onLoad(Widget sender) {
-        lbl.setText("Done loading.");
-      }
-    
       public void onError(Widget sender) {
         lbl.setText("An error occurred while loading.");
       }
+
+      // This event may not fire if the image is already cached
+      public void onLoad(Widget sender) {
+        lbl.setText("Done loading.");
+      }
     });
 
     // Point the image at a real URL.
     lbl.setText("Loading...");
     image.setUrl("http://www.google.com/images/logo.gif");
 
-    // Add the image & label to the root panel.
+    // When the user clicks this button, we want to clip the image
+    btn.addClickListener(new ClickListener() {
+      public void onClick(Widget sender) {
+        image.setVisibleRect(70, 0, 47, 100);
+      }
+    });
+
+    // When the user clicks this button, we want to restore the image to its
+    // unclipped state
+    btn2.addClickListener(new ClickListener() {
+      public void onClick(Widget sender) {
+        image.setUrl("http://www.google.com/images/logo.gif");
+      }
+    });
+
+    // Add the image, label, and clip/restore buttons to the root panel.
     VerticalPanel panel = new VerticalPanel();
     panel.add(lbl);
     panel.add(image);
+    panel.add(btn);
+    panel.add(btn2);
     RootPanel.get().add(panel);
   }
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/gwt/user/ClippedImage.gwt.xml b/user/src/com/google/gwt/user/ClippedImage.gwt.xml
new file mode 100644
index 0000000..c4ba233
--- /dev/null
+++ b/user/src/com/google/gwt/user/ClippedImage.gwt.xml
@@ -0,0 +1,25 @@
+<!--                                                                        -->

+<!-- Copyright 2007 Google Inc. All Rights Reserved.                        -->

+<!--                                                                        -->

+<!-- This module is required in order to use an Image in its clipped state, -->

+<!-- or an ImageBundle.                                                     -->

+<!--                                                                        -->

+<!-- The implementation differs between Internet Explorer and other         -->

+<!-- browsers, so we use deferred binding to create the correct instance of -->

+<!-- the class based on the user's browser.                                 -->

+<!--                                                                        -->

+<!-- This module is interited by the User module, so it will be available   -->

+<!-- to all GWT applications.                                               -->

+

+<module>

+	<inherits name="com.google.gwt.core.Core" />

+	<inherits name="com.google.gwt.user.UserAgent" />

+

+	<!-- IE needs a different implementation -->

+	<replace-with

+		class="com.google.gwt.user.client.ui.impl.ClippedImageImplIE6">

+		<when-type-is

+			class="com.google.gwt.user.client.ui.impl.ClippedImageImpl" />

+		<when-property-is name="user.agent" value="ie6" />

+	</replace-with>

+</module>

diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml
index adbf794..5ce6852 100644
--- a/user/src/com/google/gwt/user/User.gwt.xml
+++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -14,4 +14,5 @@
    <inherits name="com.google.gwt.user.Form"/>
    <inherits name="com.google.gwt.user.TextBox"/>
    <inherits name="com.google.gwt.user.Focus"/>
+   <inherits name="com.google.gwt.user.ClippedImage"/>
 </module>
diff --git a/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
new file mode 100644
index 0000000..8656716
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
@@ -0,0 +1,70 @@
+/*

+ * Copyright 2007 Google Inc.

+ * 

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ * 

+ * http://www.apache.org/licenses/LICENSE-2.0

+ * 

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.user.client.ui;

+

+/**

+ * An opaque representation of a particular image such that the image can be

+ * accessed either as an HTML fragment or as an {@link Image} object. An image

+ * protype can be thought of as an abstract image factory with additional

+ * capabilities.

+ * 

+ * <p>

+ * The {@link #applyTo(Image)} method provides an efficient way to replace the

+ * contents of an existing <code>Image</code>. This is useful in cases where

+ * an image changes its appearance based on a user's action. Instead of creating

+ * two <code>Image</code> objects then alternately hiding/showing them, one

+ * can use the {@link #applyTo(Image)} method of two

+ * <code>AbstractImagePrototype</code> objects to transform a single

+ * <code>Image</code> object between two (or more) visual representations. The

+ * use of <code>AbstractImagePrototypes</code> results in an cleaner and more

+ * efficient implementation.

+ * </p>

+ * 

+ * <p>

+ * This class is also a useful way to encapsulate complex HTML that represents

+ * an image without actually instantiating <code>Image</code> objects. When

+ * constructing large HTML fragments, especially those that contain many images,

+ * {@link #getHTML()} can be much more efficient.

+ * </p>

+ */

+public abstract class AbstractImagePrototype {

+

+  /**

+   * Transforms an existing {@link Image} into the image represented by this

+   * prototype.

+   * 

+   * @param image the instance to be transformed to match this prototype

+   */

+  public abstract void applyTo(Image image);

+

+  /**

+   * Creates a new {@link Image} instance based on the image represented by this

+   * prototype.

+   * 

+   * @return a new <code>Image</code> based on this prototype

+   */

+  public abstract Image createImage();

+

+  /**

+   * Gets an HTML fragment that displays the image represented by this

+   * prototype. The HTML returned is not necessarily a simple

+   * <code>&lt;img&gt;</code> element. It may be a more complex structure that

+   * should be treated opaquely.

+   * 

+   * @return the HTML representation of this prototype

+   */

+  public abstract String getHTML();

+}

diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java
index 331d54d..37330b1 100644
--- a/user/src/com/google/gwt/user/client/ui/Image.java
+++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,19 +16,46 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.impl.ClippedImageImpl;
+import com.google.gwt.core.client.GWT;
 
 import java.util.HashMap;
 
 /**
- * A widget that displays the image at a given URL.
+ * A widget that displays the image at a given URL. The image can be in
+ * 'unclipped' mode (the default) or 'clipped' mode. In clipped mode, a viewport
+ * is overlaid on top of the image so that a subset of the image will be
+ * displayed. In unclipped mode, there is no viewport - the entire image will be
+ * visible. Whether an image is in clipped or unclipped mode depends on how the
+ * image is constructed, and how it is transformed after construction. Methods
+ * will operate differently depending on the mode that the image is in. These
+ * differences are detailed in the documentation for each method.
+ * 
+ * <p>
+ * If an image transitions between clipped mode and unclipped mode, any
+ * {@link Element}-specific attributes added by the user (including style
+ * attributes, style names, and style modifiers), except for event listeners,
+ * will be lost.
+ * </p>
  * 
  * <h3>CSS Style Rules</h3>
  * <ul class="css">
  * <li>.gwt-Image { }</li>
  * </ul>
  * 
+ * Tranformations between clipped and unclipped state will result in a loss of
+ * any style names that were set/added; the only style names that are preserved
+ * are those that are mentioned in the static CSS style rules. Due to
+ * browser-specific HTML constructions needed to achieve the clipping effect,
+ * certain CSS attributes, such as padding and background, may not work as
+ * expected when an image is in clipped mode. These limitations can usually be
+ * easily worked around by encapsulating the image in a container widget that
+ * can itself be styled.
+ * 
  * <p>
  * <h3>Example</h3>
  * {@example com.google.gwt.examples.ImageExample}
@@ -38,6 +65,206 @@
     SourcesMouseEvents, SourcesLoadEvents {
 
   /**
+   * Abstract class which is used to hold the state associated with an image
+   * object.
+   */
+  private abstract static class State {
+
+    public abstract int getHeight(Image image);
+
+    public abstract int getOriginLeft();
+
+    public abstract int getOriginTop();
+
+    public abstract String getUrl(Image image);
+
+    public abstract int getWidth(Image image);
+
+    public abstract void setUrl(Image image, String url);
+
+    public abstract void setUrlAndVisibleRect(Image image, String url,
+        int left, int top, int width, int height);
+
+    public abstract void setVisibleRect(Image image, int left, int top,
+        int width, int height);
+
+    // This method is used only by unit tests.
+    protected abstract String getStateName();
+  }
+
+  /**
+   * Implementation of behaviors associated with the unclipped state of an
+   * image.
+   */
+  private static class UnclippedState extends State {
+
+    UnclippedState(Image image) {
+      image.setElement(DOM.createImg());
+      image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD
+          | Event.ONERROR);
+    }
+
+    UnclippedState(Image image, String url) {
+      this(image);
+      setUrl(image, url);
+    }
+
+    public int getHeight(Image image) {
+      return DOM.getIntAttribute(image.getElement(), "height");
+    }
+
+    public int getOriginLeft() {
+      return 0;
+    }
+
+    public int getOriginTop() {
+      return 0;
+    }
+
+    public String getUrl(Image image) {
+      return DOM.getAttribute(image.getElement(), "src");
+    }
+
+    public int getWidth(Image image) {
+      return DOM.getIntAttribute(image.getElement(), "width");
+    }
+
+    public void setUrl(Image image, String url) {
+      DOM.setAttribute(image.getElement(), "src", url);
+    }
+
+    public void setUrlAndVisibleRect(Image image, String url, int left,
+        int top, int width, int height) {
+      image.changeState(new ClippedState(image, url, left, top, width, height));
+    }
+
+    public void setVisibleRect(Image image, int left, int top, int width,
+        int height) {
+      image.changeState(new ClippedState(image, getUrl(image), left, top,
+          width, height));
+    }
+
+    // This method is used only by unit tests.
+    protected String getStateName() {
+      return "unclipped";
+    }
+  }
+
+  /**
+   * Implementation of behaviors associated with the clipped state of an image.
+   */
+  private static class ClippedState extends State {
+
+    private static final ClippedImageImpl impl = (ClippedImageImpl) GWT.create(ClippedImageImpl.class);
+
+    private int left = 0;
+    private int top = 0;
+    private int width = 0;
+    private int height = 0;
+    private String url = null;
+
+    ClippedState(Image image, String url, int left, int top, int width,
+        int height) {
+      this.left = left;
+      this.top = top;
+      this.width = width;
+      this.height = height;
+      this.url = url;
+      image.setElement(impl.createStructure(url, left, top, width, height));
+      image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS);
+      fireSyntheticLoadEvent(image);
+    }
+
+    private void fireSyntheticLoadEvent(final Image image) {
+      /*
+       * We need to synthesize a load event, because the native events that are
+       * fired would correspond to the loading of clear.cache.gif, which is
+       * incorrect. A native event would not even fire in Internet Explorer,
+       * because the root element is a wrapper element around the <img> element.
+       * Since we are synthesizing a load event, we do not need to sink the
+       * onload event.
+       * 
+       * We use a deferred command here to simulate the native version of the
+       * load event as closely as possible. In the native event case, it is
+       * unlikely that a second load event would occur while you are in the load
+       * event handler.
+       */
+      DeferredCommand.add(new Command() {
+        public void execute() {
+          if (image.loadListeners != null) {
+            image.loadListeners.fireLoad(image);
+          }
+        }
+      });
+    }
+
+    public int getHeight(Image image) {
+      return height;
+    }
+
+    public int getOriginLeft() {
+      return left;
+    }
+
+    public int getOriginTop() {
+      return top;
+    }
+
+    public String getUrl(Image image) {
+      return url;
+    }
+
+    public int getWidth(Image image) {
+      return width;
+    }
+
+    public void setUrl(Image image, String url) {
+      image.changeState(new UnclippedState(image, url));
+    }
+
+    public void setUrlAndVisibleRect(Image image, String url, int left,
+        int top, int width, int height) {
+      if (!url.equals(url) || this.left != left || this.top != top
+          || this.width != width || this.height != height) {
+
+        this.url = url;
+        this.left = left;
+        this.top = top;
+        this.width = width;
+        this.height = height;
+
+        impl.adjust(image.getElement(), url, left, top, width, height);
+        fireSyntheticLoadEvent(image);
+      }
+    }
+
+    public void setVisibleRect(Image image, int left, int top, int width,
+        int height) {
+      /*
+       * In the event that the clipping rectangle has not changed, we want to
+       * skip all of the work required with a getImpl().adjust, and we do not
+       * want to fire a load event.
+       */
+      if (this.left != left || this.top != top || this.width != width
+          || this.height != height) {
+
+        this.left = left;
+        this.top = top;
+        this.width = width;
+        this.height = height;
+
+        impl.adjust(image.getElement(), url, left, top, width, height);
+        fireSyntheticLoadEvent(image);
+      }
+    }
+
+    /* This method is used only by unit tests */
+    protected String getStateName() {
+      return "clipped";
+    }
+  }
+
+  /**
    * This map is used to store prefetched images. If a reference is not kept to
    * the prefetched image objects, they can get garbage collected, which
    * sometimes keeps them from getting fully fetched.
@@ -59,23 +286,48 @@
   private LoadListenerCollection loadListeners;
   private MouseListenerCollection mouseListeners;
 
+  private State state;
+
   /**
    * Creates an empty image.
    */
   public Image() {
-    setElement(DOM.createImg());
-    sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD | Event.ONERROR);
+    changeState(new UnclippedState(this));
     setStyleName("gwt-Image");
   }
 
   /**
-   * Creates an image with a specified URL.
+   * Creates an image with a specified URL. The load event will be fired once
+   * the image at the given URL has been retrieved by the browser.
    * 
    * @param url the URL of the image to be displayed
    */
   public Image(String url) {
-    this();
-    setUrl(url);
+    changeState(new UnclippedState(this, url));
+    setStyleName("gwt-Image");
+  }
+
+  /**
+   * Creates a clipped image with a specified URL and visibility rectangle. The
+   * visibility rectangle is declared relative to the the rectangle which
+   * encompasses the entire image, which has an upper-left vertex of (0,0). The
+   * load event will be fired immediately after the object has been constructed
+   * (i.e. potentially before the image has been loaded in the browser). Since
+   * the width and height are specified explicitly by the user, this behavior
+   * will not cause problems with retrieving the width and height of a clipped
+   * image in a load event handler.
+   * 
+   * @param url the URL of the image to be displayed
+   * @param left the horizontal co-ordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param top the vertical co-ordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param width the width of the visibility rectangle
+   * @param height the height of the visibility rectangle
+   */
+  public Image(String url, int left, int top, int width, int height) {
+    changeState(new ClippedState(this, url, left, top, width, height));
+    setStyleName("gwt-Image");
   }
 
   public void addClickListener(ClickListener listener) {
@@ -100,12 +352,62 @@
   }
 
   /**
-   * Gets the URL of the image.
+   * Gets the height of the image. When the image is in the unclipped state, the
+   * height of the image is not known until the image has been loaded (i.e. load
+   * event has been fired for the image).
+   * 
+   * @return the height of the image, or 0 if the height is unknown
+   */
+  public int getHeight() {
+    return state.getHeight(this);
+  }
+
+  /**
+   * Gets the horizontal co-ordinate of the upper-left vertex of the image's
+   * visibility rectangle. If the image is in the unclipped state, then the
+   * visibility rectangle is assumed to be the rectangle which encompasses the
+   * entire image, which has an upper-left vertex of (0,0).
+   * 
+   * @return the horizontal co-ordinate of the upper-left vertex of the image's
+   *         visibility rectangle
+   */
+  public int getOriginLeft() {
+    return state.getOriginLeft();
+  }
+
+  /**
+   * Gets the vertical co-ordinate of the upper-left vertex of the image's
+   * visibility rectangle. If the image is in the unclipped state, then the
+   * visibility rectangle is assumed to be the rectangle which encompasses the
+   * entire image, which has an upper-left vertex of (0,0).
+   * 
+   * @return the vertical co-ordinate of the upper-left vertex of the image's
+   *         visibility rectangle
+   */
+  public int getOriginTop() {
+    return state.getOriginTop();
+  }
+
+  /**
+   * Gets the URL of the image. The URL that is returned is not necessarily the
+   * URL that was passed in by the user. It may have been transformed to an
+   * absolute URL.
    * 
    * @return the image URL
    */
   public String getUrl() {
-    return DOM.getAttribute(getElement(), "src");
+    return state.getUrl(this);
+  }
+
+  /**
+   * Gets the width of the image. When the image is in the unclipped state, the
+   * width of the image is not known until the image has been loaded (i.e. load
+   * event has been fired for the image).
+   * 
+   * @return the width of the image, or 0 if the width is unknown
+   */
+  public int getWidth() {
+    return state.getWidth(this);
   }
 
   public void onBrowserEvent(Event event) {
@@ -160,11 +462,60 @@
   }
 
   /**
-   * Sets the URL of the image to be displayed.
+   * Sets the URL of the image to be displayed. If the image is in the clipped
+   * state, a call to this method will cause a transition of the image to the
+   * unclipped state. Regardless of whether or not the image is in the clipped
+   * or unclipped state, a load event will be fired.
    * 
    * @param url the image URL
    */
   public void setUrl(String url) {
-    DOM.setAttribute(getElement(), "src", url);
+    state.setUrl(this, url);
   }
-}
\ No newline at end of file
+
+  /**
+   * Sets the url and the visibility rectangle for the image at the same time. A
+   * single load event will be fired if either the incoming url or visiblity
+   * rectangle co-ordinates differ from the image's current url or current
+   * visibility rectangle co-ordinates. If the image is currently in the
+   * unclipped state, a call to this method will cause a transition to the
+   * clipped state.
+   * 
+   * @param url the image URL
+   * @param left the horizontal coordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param top the vertical coordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param width the width of the visibility rectangle
+   * @param height the height of the visibility rectangle
+   */
+  public void setUrlAndVisibleRect(String url, int left, int top, int width,
+      int height) {
+    state.setUrlAndVisibleRect(this, url, left, top, width, height);
+  }
+
+  /**
+   * Sets the visibility rectangle of an image. The visibility rectangle is
+   * declared relative to the the rectangle which encompasses the entire image,
+   * which has an upper-left vertex of (0,0). Provided that any of the left,
+   * top, width, and height parameters are different than the those values that
+   * are currently set for the image, a load event will be fired. If the image
+   * is in the unclipped state, a call to this method will cause a transition of
+   * the image to the clipped state. This transition will cause a load event to
+   * fire.
+   * 
+   * @param left the horizontal coordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param top the vertical coordinate of the upper-left vertex of the
+   *          visibility rectangle
+   * @param width the width of the visibility rectangle
+   * @param height the height of the visibility rectangle
+   */
+  public void setVisibleRect(int left, int top, int width, int height) {
+    state.setVisibleRect(this, left, top, width, height);
+  }
+
+  private void changeState(State newState) {
+    state = newState;
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java
index 0d4d4aa..f969059 100644
--- a/user/src/com/google/gwt/user/client/ui/UIObject.java
+++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -340,10 +340,27 @@
   /**
    * Sets this object's browser element. UIObject subclasses must call this
    * method before attempting to call any other methods.
+   *
+   * If the browser element has already been set, then the current element's
+   * position is located in the DOM and removed. The new element is added into
+   * the previous element's position.
    * 
    * @param elem the object's new element
    */
   protected void setElement(Element elem) {
+    if (this.element != null) {
+      // replace this.element in its parent with elem.
+      replaceNode(this.element, elem);
+    }
     this.element = elem;
   }
+
+  private native void replaceNode(Element node, Element newNode) /*-{
+    var p = node.parentNode;
+    if (!p) {
+      return;
+    }
+    p.insertBefore(newNode, node);
+    p.removeChild(node);
+  }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index b9f6005..2d4a2ee 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -18,6 +18,7 @@
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.EventListener;
+import com.google.gwt.user.client.Element;
 
 /**
  * The base class for the majority of user-interface objects. Widget adds
@@ -115,6 +116,34 @@
   }
 
   /**
+    * Sets this object's browser element. Widget subclasses must call this
+    * method before attempting to call any other methods.
+    *
+    * If a browser element has already been attached, then it is replaced with
+    * the new element. The old event listeners are removed from the old browser
+    * element, and the event listeners are set up on the new browser element.
+    *
+    * @param elem the object's new element
+    */
+   protected void setElement(Element elem) {
+     if (attached) {
+       // Remove old event listener to avoid leaking. onDetach will not do this
+       // for us, because it is only called the widget itself is detached from
+       // the document.
+       DOM.setEventListener(getElement(), null);
+     }
+
+     super.setElement(elem);
+
+     if (attached) {
+       // Hook the event listener back up on the new element. onAttach will not
+       // do this for us, because it is only called when the widget itself is
+       // attached to the document.
+       DOM.setEventListener(elem, this);
+     }
+   }
+
+  /**
    * Gets the panel-defined layout data associated with this widget.
    * 
    * @return the widget's layout data
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
new file mode 100644
index 0000000..6d6f2cb
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 2007 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.user.client.ui.impl;

+

+import com.google.gwt.user.client.DOM;

+import com.google.gwt.user.client.Element;

+

+/**

+ * Uses a combination of a clear image and a background image to clip all except

+ * a desired portion of an underlying image.

+ *

+ * Do not use this class - it is used for implementation only, and its methods

+ * may change in the future.

+ */

+public class ClippedImageImpl {

+

+  public void adjust(Element img, String url, int left, int top, int width,

+                     int height) {

+    String style = "url(" + url + ") no-repeat " + (-left + "px ")

+        + (-top + "px");

+    DOM.setStyleAttribute(img, "background", style);

+    DOM.setStyleAttribute(img, "width", width + "px");

+    DOM.setStyleAttribute(img, "height", height + "px");

+  }

+

+  public Element createStructure(String url, int left, int top, int width,

+                                 int height) {

+    Element tmp = DOM.createSpan();

+    DOM.setInnerHTML(tmp, getHTML(url, left, top, width, height));

+    return DOM.getFirstChild(tmp);

+  }

+

+  public String getHTML(String url, int left, int top, int width, int height) {

+    String style = "width: " + width + "px; height: " + height

+        + "px; background: url(" + url + ") no-repeat " + (-left + "px ")

+        + (-top + "px");

+

+    String clippedImgHtml = "<img src='clear.cache.gif' style='"

+        + style + "' border='0'>";

+

+    return clippedImgHtml;

+  }

+}

diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
new file mode 100644
index 0000000..d6f0fbb
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
@@ -0,0 +1,68 @@
+/*

+ * Copyright 2007 Google Inc.

+ * 

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ * 

+ * http://www.apache.org/licenses/LICENSE-2.0

+ * 

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.user.client.ui.impl;

+

+import com.google.gwt.user.client.DOM;

+import com.google.gwt.user.client.Element;

+

+/**

+ * Implements the clipped image as a IMG inside a custom tag because we can't

+ * use the IE PNG transparency filter on background-image images.

+ *

+ * Do not use this class - it is used for implementation only, and its methods

+ * may change in the future.

+ */

+public class ClippedImageImplIE6 extends ClippedImageImpl {

+  

+  public void adjust(Element clipper, String url, int left, int top, int width,

+      int height) {

+

+    DOM.setStyleAttribute(clipper, "width", width + "px");

+    DOM.setStyleAttribute(clipper, "height", height + "px");

+

+    // Update the nested image's url.

+    Element img = DOM.getFirstChild(clipper);

+    DOM.setStyleAttribute(img, "filter",

+        "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url

+            + "',sizingMethod='crop')");

+    DOM.setStyleAttribute(img, "marginLeft", -left + "px");

+    DOM.setStyleAttribute(img, "marginTop", -top + "px");

+

+    // AlphaImageLoader requires that we size the image explicitly.

+    // It really only needs to be enough to show the revealed portion.

+    int imgWidth = left + width;

+    int imgHeight = top + height;

+    DOM.setIntAttribute(img, "width", imgWidth);

+    DOM.setIntAttribute(img, "height", imgHeight);

+  }

+

+  public String getHTML(String url, int left, int top, int width, int height) {

+    String clipperStyle = "overflow: hidden; width: " + width + "px; height: "

+        + height + "px; padding: 0px";

+

+    String imgStyle =

+        "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"

+        + url + "',sizingMethod='crop'); margin-left: "

+        + -left + "px; margin-top: " + -top + "px" + "border: 0px";

+

+    String clippedImgHtml = "<gwt:clipper style=\""

+        + clipperStyle + "\"><img src='clear.cache.gif' style=\"" + imgStyle

+        + "\" width=" + (left + width) + " height=" + (top + height)

+        + " border='0'></gwt:clipper>";

+

+    return clippedImgHtml;

+  }

+}

diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java
new file mode 100644
index 0000000..159d00c
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java
@@ -0,0 +1,57 @@
+/*

+ * Copyright 2007 Google Inc.

+ * 

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ * 

+ * http://www.apache.org/licenses/LICENSE-2.0

+ * 

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.user.client.ui.impl;

+

+import com.google.gwt.user.client.ui.Image;

+import com.google.gwt.user.client.ui.AbstractImagePrototype;

+import com.google.gwt.core.client.GWT;

+

+/**

+ * Implementation of {@link AbstractImagePrototype} for a clipped image. This

+ * class is used internally by the image bundle generator and is not intended

+ * for general use. It is subject to change without warning.

+ */

+public class ClippedImagePrototype extends AbstractImagePrototype {

+

+  private static final ClippedImageImpl impl = (ClippedImageImpl) GWT.create(ClippedImageImpl.class);

+

+  private int left = 0;

+  private int top = 0;

+  private int width = 0;

+  private int height = 0;

+  private String url = null;

+

+  public ClippedImagePrototype(String url, int left, int top, int width,

+      int height) {

+    this.url = url;

+    this.left = left;

+    this.top = top;

+    this.width = width;

+    this.height = height;

+  }

+

+  public void applyTo(Image image) {

+    image.setUrlAndVisibleRect(url, left, top, width, height);

+  }

+

+  public Image createImage() {

+    return new Image(url, left, top, width, height);

+  }

+

+  public String getHTML() {

+    return impl.getHTML(url, left, top, width, height);

+  }

+}

diff --git a/user/src/com/google/gwt/user/public/clear.cache.gif b/user/src/com/google/gwt/user/public/clear.cache.gif
new file mode 100644
index 0000000..e565824
--- /dev/null
+++ b/user/src/com/google/gwt/user/public/clear.cache.gif
Binary files differ