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><img></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