KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > user > client > ui > Image


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.user.client.ui;
17
18 import com.google.gwt.user.client.DOM;
19 import com.google.gwt.user.client.DeferredCommand;
20 import com.google.gwt.user.client.Command;
21 import com.google.gwt.user.client.Element;
22 import com.google.gwt.user.client.Event;
23 import com.google.gwt.user.client.ui.impl.ClippedImageImpl;
24 import com.google.gwt.core.client.GWT;
25
26 import java.util.HashMap JavaDoc;
27
28 /**
29  * A widget that displays the image at a given URL. The image can be in
30  * 'unclipped' mode (the default) or 'clipped' mode. In clipped mode, a viewport
31  * is overlaid on top of the image so that a subset of the image will be
32  * displayed. In unclipped mode, there is no viewport - the entire image will be
33  * visible. Whether an image is in clipped or unclipped mode depends on how the
34  * image is constructed, and how it is transformed after construction. Methods
35  * will operate differently depending on the mode that the image is in. These
36  * differences are detailed in the documentation for each method.
37  *
38  * <p>
39  * If an image transitions between clipped mode and unclipped mode, any
40  * {@link Element}-specific attributes added by the user (including style
41  * attributes, style names, and style modifiers), except for event listeners,
42  * will be lost.
43  * </p>
44  *
45  * <h3>CSS Style Rules</h3>
46  * <ul class="css">
47  * <li>.gwt-Image { }</li>
48  * </ul>
49  *
50  * Tranformations between clipped and unclipped state will result in a loss of
51  * any style names that were set/added; the only style names that are preserved
52  * are those that are mentioned in the static CSS style rules. Due to
53  * browser-specific HTML constructions needed to achieve the clipping effect,
54  * certain CSS attributes, such as padding and background, may not work as
55  * expected when an image is in clipped mode. These limitations can usually be
56  * easily worked around by encapsulating the image in a container widget that
57  * can itself be styled.
58  *
59  * <p>
60  * <h3>Example</h3>
61  * {@example com.google.gwt.examples.ImageExample}
62  * </p>
63  */

64 public class Image extends Widget implements SourcesClickEvents,
65     SourcesLoadEvents, SourcesMouseEvents, SourcesMouseWheelEvents {
66
67   /**
68    * Abstract class which is used to hold the state associated with an image
69    * object.
70    */

71   private abstract static class State {
72
73     public abstract int getHeight(Image image);
74
75     public abstract int getOriginLeft();
76
77     public abstract int getOriginTop();
78
79     public abstract String JavaDoc getUrl(Image image);
80
81     public abstract int getWidth(Image image);
82
83     public abstract void setUrl(Image image, String JavaDoc url);
84
85     public abstract void setUrlAndVisibleRect(Image image, String JavaDoc url,
86         int left, int top, int width, int height);
87
88     public abstract void setVisibleRect(Image image, int left, int top,
89         int width, int height);
90
91     // This method is used only by unit tests.
92
protected abstract String JavaDoc getStateName();
93   }
94
95   /**
96    * Implementation of behaviors associated with the unclipped state of an
97    * image.
98    */

99   private static class UnclippedState extends State {
100
101     UnclippedState(Image image) {
102       image.setElement(DOM.createImg());
103       image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD
104           | Event.ONERROR | Event.ONMOUSEWHEEL);
105     }
106
107     UnclippedState(Image image, String JavaDoc url) {
108       this(image);
109       setUrl(image, url);
110     }
111
112     public int getHeight(Image image) {
113       return DOM.getElementPropertyInt(image.getElement(), "height");
114     }
115
116     public int getOriginLeft() {
117       return 0;
118     }
119
120     public int getOriginTop() {
121       return 0;
122     }
123
124     public String JavaDoc getUrl(Image image) {
125       return DOM.getImgSrc(image.getElement());
126     }
127
128     public int getWidth(Image image) {
129       return DOM.getElementPropertyInt(image.getElement(), "width");
130     }
131
132     public void setUrl(Image image, String JavaDoc url) {
133       DOM.setImgSrc(image.getElement(), url);
134     }
135
136     public void setUrlAndVisibleRect(Image image, String JavaDoc url, int left,
137         int top, int width, int height) {
138       image.changeState(new ClippedState(image, url, left, top, width, height));
139     }
140
141     public void setVisibleRect(Image image, int left, int top, int width,
142         int height) {
143       image.changeState(new ClippedState(image, getUrl(image), left, top,
144           width, height));
145     }
146
147     // This method is used only by unit tests.
148
protected String JavaDoc getStateName() {
149       return "unclipped";
150     }
151   }
152
153   /**
154    * Implementation of behaviors associated with the clipped state of an image.
155    */

156   private static class ClippedState extends State {
157
158     private static final ClippedImageImpl impl = (ClippedImageImpl) GWT.create(ClippedImageImpl.class);
159
160     private int left = 0;
161     private int top = 0;
162     private int width = 0;
163     private int height = 0;
164     private String JavaDoc url = null;
165
166     ClippedState(Image image, String JavaDoc url, int left, int top, int width,
167         int height) {
168       this.left = left;
169       this.top = top;
170       this.width = width;
171       this.height = height;
172       this.url = url;
173       image.setElement(impl.createStructure(url, left, top, width, height));
174       image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONMOUSEWHEEL);
175       fireSyntheticLoadEvent(image);
176     }
177
178     private void fireSyntheticLoadEvent(final Image image) {
179       /*
180        * We need to synthesize a load event, because the native events that are
181        * fired would correspond to the loading of clear.cache.gif, which is
182        * incorrect. A native event would not even fire in Internet Explorer,
183        * because the root element is a wrapper element around the <img> element.
184        * Since we are synthesizing a load event, we do not need to sink the
185        * onload event.
186        *
187        * We use a deferred command here to simulate the native version of the
188        * load event as closely as possible. In the native event case, it is
189        * unlikely that a second load event would occur while you are in the load
190        * event handler.
191        */

192       DeferredCommand.addCommand(new Command() {
193         public void execute() {
194           if (image.loadListeners != null) {
195             image.loadListeners.fireLoad(image);
196           }
197         }
198       });
199     }
200
201     public int getHeight(Image image) {
202       return height;
203     }
204
205     public int getOriginLeft() {
206       return left;
207     }
208
209     public int getOriginTop() {
210       return top;
211     }
212
213     public String JavaDoc getUrl(Image image) {
214       return url;
215     }
216
217     public int getWidth(Image image) {
218       return width;
219     }
220
221     public void setUrl(Image image, String JavaDoc url) {
222       image.changeState(new UnclippedState(image, url));
223     }
224
225     public void setUrlAndVisibleRect(Image image, String JavaDoc url, int left,
226         int top, int width, int height) {
227       if (!url.equals(url) || this.left != left || this.top != top
228           || this.width != width || this.height != height) {
229
230         this.url = url;
231         this.left = left;
232         this.top = top;
233         this.width = width;
234         this.height = height;
235
236         impl.adjust(image.getElement(), url, left, top, width, height);
237         fireSyntheticLoadEvent(image);
238       }
239     }
240
241     public void setVisibleRect(Image image, int left, int top, int width,
242         int height) {
243       /*
244        * In the event that the clipping rectangle has not changed, we want to
245        * skip all of the work required with a getImpl().adjust, and we do not
246        * want to fire a load event.
247        */

248       if (this.left != left || this.top != top || this.width != width
249           || this.height != height) {
250
251         this.left = left;
252         this.top = top;
253         this.width = width;
254         this.height = height;
255
256         impl.adjust(image.getElement(), url, left, top, width, height);
257         fireSyntheticLoadEvent(image);
258       }
259     }
260
261     /* This method is used only by unit tests */
262     protected String JavaDoc getStateName() {
263       return "clipped";
264     }
265   }
266
267   /**
268    * This map is used to store prefetched images. If a reference is not kept to
269    * the prefetched image objects, they can get garbage collected, which
270    * sometimes keeps them from getting fully fetched.
271    */

272   private static HashMap JavaDoc prefetchImages = new HashMap JavaDoc();
273
274   /**
275    * Causes the browser to pre-fetch the image at a given URL.
276    *
277    * @param url the URL of the image to be prefetched
278    */

279   public static void prefetch(String JavaDoc url) {
280     Element img = DOM.createImg();
281     DOM.setImgSrc(img, url);
282     prefetchImages.put(url, img);
283   }
284
285   private ClickListenerCollection clickListeners;
286   private LoadListenerCollection loadListeners;
287   private MouseListenerCollection mouseListeners;
288   private MouseWheelListenerCollection mouseWheelListeners;
289
290   private State state;
291
292   /**
293    * Creates an empty image.
294    */

295   public Image() {
296     changeState(new UnclippedState(this));
297     setStyleName("gwt-Image");
298   }
299
300   /**
301    * Creates an image with a specified URL. The load event will be fired once
302    * the image at the given URL has been retrieved by the browser.
303    *
304    * @param url the URL of the image to be displayed
305    */

306   public Image(String JavaDoc url) {
307     changeState(new UnclippedState(this, url));
308     setStyleName("gwt-Image");
309   }
310
311   /**
312    * Creates a clipped image with a specified URL and visibility rectangle. The
313    * visibility rectangle is declared relative to the the rectangle which
314    * encompasses the entire image, which has an upper-left vertex of (0,0). The
315    * load event will be fired immediately after the object has been constructed
316    * (i.e. potentially before the image has been loaded in the browser). Since
317    * the width and height are specified explicitly by the user, this behavior
318    * will not cause problems with retrieving the width and height of a clipped
319    * image in a load event handler.
320    *
321    * @param url the URL of the image to be displayed
322    * @param left the horizontal co-ordinate of the upper-left vertex of the
323    * visibility rectangle
324    * @param top the vertical co-ordinate of the upper-left vertex of the
325    * visibility rectangle
326    * @param width the width of the visibility rectangle
327    * @param height the height of the visibility rectangle
328    */

329   public Image(String JavaDoc url, int left, int top, int width, int height) {
330     changeState(new ClippedState(this, url, left, top, width, height));
331     setStyleName("gwt-Image");
332   }
333
334   public void addClickListener(ClickListener listener) {
335     if (clickListeners == null) {
336       clickListeners = new ClickListenerCollection();
337     }
338     clickListeners.add(listener);
339   }
340
341   public void addLoadListener(LoadListener listener) {
342     if (loadListeners == null) {
343       loadListeners = new LoadListenerCollection();
344     }
345     loadListeners.add(listener);
346   }
347
348   public void addMouseListener(MouseListener listener) {
349     if (mouseListeners == null) {
350       mouseListeners = new MouseListenerCollection();
351     }
352     mouseListeners.add(listener);
353   }
354
355   public void addMouseWheelListener(MouseWheelListener listener) {
356     if (mouseWheelListeners == null) {
357       mouseWheelListeners = new MouseWheelListenerCollection();
358     }
359     mouseWheelListeners.add(listener);
360   }
361
362   /**
363    * Gets the height of the image. When the image is in the unclipped state, the
364    * height of the image is not known until the image has been loaded (i.e. load
365    * event has been fired for the image).
366    *
367    * @return the height of the image, or 0 if the height is unknown
368    */

369   public int getHeight() {
370     return state.getHeight(this);
371   }
372
373   /**
374    * Gets the horizontal co-ordinate of the upper-left vertex of the image's
375    * visibility rectangle. If the image is in the unclipped state, then the
376    * visibility rectangle is assumed to be the rectangle which encompasses the
377    * entire image, which has an upper-left vertex of (0,0).
378    *
379    * @return the horizontal co-ordinate of the upper-left vertex of the image's
380    * visibility rectangle
381    */

382   public int getOriginLeft() {
383     return state.getOriginLeft();
384   }
385
386   /**
387    * Gets the vertical co-ordinate of the upper-left vertex of the image's
388    * visibility rectangle. If the image is in the unclipped state, then the
389    * visibility rectangle is assumed to be the rectangle which encompasses the
390    * entire image, which has an upper-left vertex of (0,0).
391    *
392    * @return the vertical co-ordinate of the upper-left vertex of the image's
393    * visibility rectangle
394    */

395   public int getOriginTop() {
396     return state.getOriginTop();
397   }
398
399   /**
400    * Gets the URL of the image. The URL that is returned is not necessarily the
401    * URL that was passed in by the user. It may have been transformed to an
402    * absolute URL.
403    *
404    * @return the image URL
405    */

406   public String JavaDoc getUrl() {
407     return state.getUrl(this);
408   }
409
410   /**
411    * Gets the width of the image. When the image is in the unclipped state, the
412    * width of the image is not known until the image has been loaded (i.e. load
413    * event has been fired for the image).
414    *
415    * @return the width of the image, or 0 if the width is unknown
416    */

417   public int getWidth() {
418     return state.getWidth(this);
419   }
420
421   public void onBrowserEvent(Event event) {
422     switch (DOM.eventGetType(event)) {
423       case Event.ONCLICK: {
424         if (clickListeners != null) {
425           clickListeners.fireClick(this);
426         }
427         break;
428       }
429       case Event.ONMOUSEDOWN:
430       case Event.ONMOUSEUP:
431       case Event.ONMOUSEMOVE:
432       case Event.ONMOUSEOVER:
433       case Event.ONMOUSEOUT: {
434         if (mouseListeners != null) {
435           mouseListeners.fireMouseEvent(this, event);
436         }
437         break;
438       }
439       case Event.ONMOUSEWHEEL:
440         if (mouseWheelListeners != null) {
441           mouseWheelListeners.fireMouseWheelEvent(this, event);
442         }
443         break;
444       case Event.ONLOAD: {
445         if (loadListeners != null) {
446           loadListeners.fireLoad(this);
447         }
448         break;
449       }
450       case Event.ONERROR: {
451         if (loadListeners != null) {
452           loadListeners.fireError(this);
453         }
454         break;
455       }
456     }
457   }
458
459   public void removeClickListener(ClickListener listener) {
460     if (clickListeners != null) {
461       clickListeners.remove(listener);
462     }
463   }
464
465   public void removeLoadListener(LoadListener listener) {
466     if (loadListeners != null) {
467       loadListeners.remove(listener);
468     }
469   }
470
471   public void removeMouseListener(MouseListener listener) {
472     if (mouseListeners != null) {
473       mouseListeners.remove(listener);
474     }
475   }
476
477   public void removeMouseWheelListener(MouseWheelListener listener) {
478     if (mouseWheelListeners != null) {
479       mouseWheelListeners.remove(listener);
480     }
481   }
482
483   /**
484    * Sets the URL of the image to be displayed. If the image is in the clipped
485    * state, a call to this method will cause a transition of the image to the
486    * unclipped state. Regardless of whether or not the image is in the clipped
487    * or unclipped state, a load event will be fired.
488    *
489    * @param url the image URL
490    */

491   public void setUrl(String JavaDoc url) {
492     state.setUrl(this, url);
493   }
494
495   /**
496    * Sets the url and the visibility rectangle for the image at the same time. A
497    * single load event will be fired if either the incoming url or visiblity
498    * rectangle co-ordinates differ from the image's current url or current
499    * visibility rectangle co-ordinates. If the image is currently in the
500    * unclipped state, a call to this method will cause a transition to the
501    * clipped state.
502    *
503    * @param url the image URL
504    * @param left the horizontal coordinate of the upper-left vertex of the
505    * visibility rectangle
506    * @param top the vertical coordinate of the upper-left vertex of the
507    * visibility rectangle
508    * @param width the width of the visibility rectangle
509    * @param height the height of the visibility rectangle
510    */

511   public void setUrlAndVisibleRect(String JavaDoc url, int left, int top, int width,
512       int height) {
513     state.setUrlAndVisibleRect(this, url, left, top, width, height);
514   }
515
516   /**
517    * Sets the visibility rectangle of an image. The visibility rectangle is
518    * declared relative to the the rectangle which encompasses the entire image,
519    * which has an upper-left vertex of (0,0). Provided that any of the left,
520    * top, width, and height parameters are different than the those values that
521    * are currently set for the image, a load event will be fired. If the image
522    * is in the unclipped state, a call to this method will cause a transition of
523    * the image to the clipped state. This transition will cause a load event to
524    * fire.
525    *
526    * @param left the horizontal coordinate of the upper-left vertex of the
527    * visibility rectangle
528    * @param top the vertical coordinate of the upper-left vertex of the
529    * visibility rectangle
530    * @param width the width of the visibility rectangle
531    * @param height the height of the visibility rectangle
532    */

533   public void setVisibleRect(int left, int top, int width, int height) {
534     state.setVisibleRect(this, left, top, width, height);
535   }
536
537   private void changeState(State newState) {
538     state = newState;
539   }
540 }
541
Popular Tags