001    package com.google.gwt.maps.client;
002    
003    import java.util.Collection;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    
007    import com.google.gwt.core.client.JavaScriptObject;
008    import com.google.gwt.dom.client.Element;
009    import com.google.gwt.event.shared.HandlerRegistration;
010    import com.google.gwt.maps.client.base.LatLng;
011    import com.google.gwt.maps.client.base.LatLngBounds;
012    import com.google.gwt.maps.client.controls.ControlPosition;
013    import com.google.gwt.maps.client.events.MapHandlerRegistration;
014    import com.google.gwt.maps.client.events.MapPanel;
015    import com.google.gwt.maps.client.events.bounds.BoundsChangeMapHandler;
016    import com.google.gwt.maps.client.events.center.CenterChangeMapHandler;
017    import com.google.gwt.maps.client.events.click.ClickMapHandler;
018    import com.google.gwt.maps.client.events.dblclick.DblClickMapHandler;
019    import com.google.gwt.maps.client.events.drag.DragMapHandler;
020    import com.google.gwt.maps.client.events.dragend.DragEndMapHandler;
021    import com.google.gwt.maps.client.events.dragstart.DragStartMapHandler;
022    import com.google.gwt.maps.client.events.heading.HeadingChangeMapHandler;
023    import com.google.gwt.maps.client.events.idle.IdleMapHandler;
024    import com.google.gwt.maps.client.events.maptypeid.MapTypeIdChangeMapHandler;
025    import com.google.gwt.maps.client.events.mousemove.MouseMoveMapHandler;
026    import com.google.gwt.maps.client.events.mouseout.MouseOutMapHandler;
027    import com.google.gwt.maps.client.events.mouseover.MouseOverMapHandler;
028    import com.google.gwt.maps.client.events.projection.ProjectionChangeMapHandler;
029    import com.google.gwt.maps.client.events.resize.ResizeMapHandler;
030    import com.google.gwt.maps.client.events.rightclick.RightClickMapHandler;
031    import com.google.gwt.maps.client.events.tiles.TilesLoadedMapHandler;
032    import com.google.gwt.maps.client.events.tilt.TiltChangeMapHandler;
033    import com.google.gwt.maps.client.events.zoom.ZoomChangeMapHandler;
034    import com.google.gwt.maps.client.maptypes.MapTypeRegistry;
035    import com.google.gwt.maps.client.maptypes.Projection;
036    import com.google.gwt.maps.client.maptypes.StyledMapType;
037    import com.google.gwt.maps.client.mvc.MVCArray;
038    import com.google.gwt.maps.client.mvc.MVCObjectWidget;
039    import com.google.gwt.maps.client.streetview.StreetViewPanoramaImpl;
040    import com.google.gwt.user.client.DOM;
041    import com.google.gwt.user.client.ui.Widget;
042    
043    public class MapWidget extends MVCObjectWidget<MapImpl> {
044    
045            /**
046             * added controls, must remove them when finished
047             */
048            private HashMap<Integer, Widget> controls = null;
049    
050            /**
051             * Reconstruct the mapWidget from JSO
052             * 
053             * @param impl
054             * @return NULL if input is NULL
055             */
056            public static MapWidget newInstance(MapImpl impl) {
057                    if (impl == null) {
058                            return null;
059                    }
060                    return new MapWidget(impl);
061            }
062    
063            /**
064             * create a new Map Widget
065             * 
066             * @param options
067             *            {@link MapOptions}
068             */
069            public MapWidget(MapOptions options) {
070                    Element div = DOM.createDiv();
071                    setElement(div);
072                    impl = MapImpl.newInstance(div, options);
073                    setStyleName("gwt-map-MapWidget-div");
074            }
075    
076            @Override
077            protected void onDetach() {
078                    removeControls();
079                    super.onDetach();
080            }
081    
082            private void removeControls() {
083                    if (controls == null) {
084                            return;
085                    }
086                    Collection<Widget> widgets = controls.values();
087                    Iterator<Widget> itr = widgets.iterator();
088                    while (itr.hasNext()) {
089                            Widget w = itr.next();
090                            w.removeFromParent();
091                    }
092            }
093    
094            /**
095             * reconstruct the mapWidget from jso
096             * 
097             * @param impl
098             *            JavaScriptObject
099             */
100            private MapWidget(MapImpl impl) {
101                    this.impl = impl;
102                    setElement(impl.getDiv());
103                    setStyleName("gwt-map-MapWidget-div");
104            }
105    
106            /**
107             * get JavaScriptObject instance.
108             * 
109             * @return {@link MapImpl}
110             */
111            public MapImpl getJso() {
112                    return impl;
113            }
114    
115            /**
116             * sets JavaScriptObject instance.
117             * 
118             * @param impl
119             *            {@link MapImpl}
120             */
121            public void setJso(MapImpl impl) {
122                    this.impl = impl;
123            }
124    
125            /**
126             * Sets the viewport to contain the given bounds.
127             * 
128             * @param bounds
129             *            {@link LatLngBounds}
130             */
131            public void fitBounds(LatLngBounds bounds) {
132                    impl.fitBounds(bounds);
133            }
134    
135            /**
136             * Returns the lat/lng bounds of the current viewport. If the map is not yet
137             * initialized (i.e. the mapType is still null), or center and zoom have not
138             * been set then the result is null or undefined.
139             * 
140             * @return {@link LatLngBounds}
141             */
142            public LatLngBounds getBounds() {
143                    return impl.getBounds();
144            }
145    
146            /**
147             * get Center
148             * 
149             * @return {@link LatLng}
150             */
151            public LatLng getCenter() {
152                    return impl.getCenter();
153            }
154    
155            /**
156             * get Element
157             */
158            public Element getDiv() {
159                    return impl.getDiv();
160            }
161    
162            /**
163             * Returns the compass heading of aerial imagery. The heading value is
164             * measured in degrees (clockwise) from cardinal direction North.
165             * 
166             * @return int
167             */
168            public int getHeading() {
169                    return impl.getHeading();
170            }
171    
172            /**
173             * gets {@link MapTypeId}
174             * 
175             * @return {@link MapTypeId}
176             */
177            public final MapTypeId getMapTypeId() {
178                    return impl.getMapTypeId();
179            }
180    
181            /**
182             * Returns the current Projection. If the map is not yet initialized (i.e.
183             * the mapType is still null) then the result is null. Listen to
184             * projection_changed and check its value to ensure it is not null.
185             * 
186             * @return {@link Projection}
187             */
188            public Projection getProjection() {
189                    return impl.getProjection();
190            }
191    
192            /**
193             * Returns the default StreetViewPanorama bound to the map, which may be a
194             * default panorama embedded within the map, or the panorama set using
195             * setStreetView(). Changes to the map's streetViewControl will be reflected
196             * in the display of such a bound panorama.
197             * 
198             * @return {@link StreetViewPanoramaImpl}
199             */
200            public StreetViewPanoramaImpl getStreetView() {
201                    return impl.getStreetView();
202            };
203    
204            /**
205             * gets Returns the angle of incidence for aerial imagery (available for
206             * SATELLITE and HYBRID map types) measured in degrees from the viewport
207             * plane to the map plane. A value of 0 indicates no angle of incidence (no
208             * tilt) while 45° imagery will return a value of 45.
209             */
210            public int getTilt() {
211                    return impl.getTilt();
212            };
213    
214            /**
215             * get zoom
216             */
217            public int getZoom() {
218                    return impl.getZoom();
219            };
220    
221            /**
222             * Changes the center of the map by the given distance in pixels. If the
223             * distance is less than both the width and height of the map, the
224             * transition will be smoothly animated. Note that the map coordinate system
225             * increases from west to east (for x values) and north to south (for y
226             * values).
227             * 
228             * @param x
229             * @param y
230             */
231            public void panBy(int x, int y) {
232                    impl.panBy(x, y);
233            };
234    
235            /**
236             * Changes the center of the map to the given LatLng. If the change is less
237             * than both the width and height of the map, the transition will be
238             * smoothly animated.
239             * 
240             * @param latLng
241             */
242            public void panTo(LatLng latLng) {
243                    impl.panTo(latLng);
244            };
245    
246            /**
247             * Pans the map by the minimum amount necessary to contain the given
248             * LatLngBounds. It makes no guarantee where on the map the bounds will be,
249             * except that as much of the bounds as possible will be visible. The bounds
250             * will be positioned inside the area bounded by the map type and navigation
251             * (pan, zoom, and Street View) controls, if they are present on the map. If
252             * the bounds is larger than the map, the map will be shifted to include the
253             * northwest corner of the bounds. If the change in the map's position is
254             * less than both the width and height of the map, the transition will be
255             * smoothly animated.
256             * 
257             * @param latLngBounds
258             */
259            public void panToBounds(LatLngBounds latLngBounds) {
260                    impl.panToBounds(latLngBounds);
261            };
262    
263            /**
264             * set center
265             * 
266             * @param latlng
267             */
268            public void setCenter(LatLng latlng) {
269                    impl.setCenter(latlng);
270            };
271    
272            /**
273             * Sets the compass heading for aerial imagery measured in degrees from
274             * cardinal direction North.
275             * 
276             * @param heading
277             */
278            public void setHeading(int heading) {
279                    impl.setHeading(heading);
280            };
281    
282            /**
283             * set MapTypeId
284             * 
285             * @param mapTypeId
286             */
287            public final void setMapTypeId(MapTypeId mapTypeId) {
288                    impl.setMapTypeId(mapTypeId);
289            }
290    
291            /**
292             * set MapOptions
293             * 
294             * @param options
295             */
296            public void setOptions(MapOptions options) {
297                    impl.setOptions(options);
298            };
299    
300            /**
301             * Sets the angle of incidence for aerial imagery (available for SATELLITE
302             * and HYBRID map types) measured in degrees from the viewport plane to the
303             * map plane. The only supported values are 0, indicating no angle of
304             * incidence (no tilt), and 45 indicating a tilt of 45&deg;.
305             * 
306             * @param panorama
307             */
308            public void setStreetView(StreetViewPanoramaImpl panorama) {
309                    impl.setStreetView(panorama);
310            };
311    
312            /**
313             * Sets the angle of incidence for aerial imagery (available for SATELLITE
314             * and HYBRID map types) measured in degrees from the viewport plane to the
315             * map plane. The only supported values are 0, indicating no angle of
316             * incidence (no tilt), and 45 indicating a tilt of 45&deg;.
317             * 
318             * @param tilt
319             */
320            public void setTilt(int tilt) {
321                    impl.setTilt(tilt);
322            };
323    
324            /**
325             * sets map zoom
326             * 
327             * @param zoom
328             */
329            public void setZoom(int zoom) {
330                    impl.setZoom(zoom);
331            };
332    
333            /**
334             * TODO sets Additional controls to attach to the map. To add a control to
335             * the map, add the control's <code>&lt;div&gt;</code> to the MVCArray
336             * corresponding to the ControlPosition where it should be rendered.
337             * 
338             * @param controls
339             */
340            public void setControls(MVCArray<Element> controls) {
341                    impl.setControls(controls);
342            };
343    
344            /**
345             * sets Additional controls to attach to the map. To add a control to the
346             * map, add the control's <code>&lt;div&gt;</code> to the MVCArray
347             * corresponding to the ControlPosition where it should be rendered.
348             * 
349             * @param controlPosition
350             * @param element
351             */
352            public void setControls(ControlPosition controlPosition, Element element) {
353                    impl.setControls(controlPosition, element);
354            };
355    
356            /**
357             * sets Additional controls to attach to the map. To add a control to the
358             * map, add the control's <code>&lt;div&gt;</code> to the MVCArray
359             * corresponding to the ControlPosition where it should be rendered.
360             * 
361             * @param controlPosition
362             * @param widget
363             */
364            public void setControls(ControlPosition controlPosition, Widget widget) {
365    
366                    // remove any pre-existing controls
367                    removePriorControlFirst(controlPosition);
368    
369                    // use a panel that controls onAttatch
370                    MapPanel mapPanel = new MapPanel();
371                    mapPanel.add(widget);
372    
373                    // add it to array for detaching
374                    if (controls == null) {
375                            controls = new HashMap<Integer, Widget>();
376                    }
377                    controls.put(controlPosition.value(), mapPanel);
378    
379                    // add it to the map
380                    impl.setControls(controlPosition, mapPanel.getElement());
381            };
382    
383            /**
384             * remove an pre-existing control so we don't get a mem leak
385             * 
386             * @param controlPosition
387             * @param widget
388             */
389            private void removePriorControlFirst(ControlPosition controlPosition) {
390                    if (controls == null) {
391                            return;
392                    }
393                    Widget w = controls.get(controlPosition.value());
394                    if (w == null) {
395                            return;
396                    }
397                    w.removeFromParent();
398            }
399    
400            /**
401             * TODO gets Additional controls to attach to the map. To add a control to
402             * the map, add the control's <code>&lt;div&gt;</code> to the MVCArray
403             * corresponding to the ControlPosition where it should be rendered.
404             */
405            public MVCArray<Element> getControls() {
406                    return impl.getControls();
407            };
408    
409            /**
410             * TODO sets A registry of MapType instances by string ID.
411             * 
412             * @param mapTypes
413             */
414            public void setMapTypesRegistry(MapTypeRegistry mapTypes) {
415                    impl.setMapTypesRegistry(mapTypes);
416            };
417    
418            /**
419             * Set the custom map type into the map's registry
420             * 
421             * @param mapTypeId
422             * @param styledType
423             */
424            public void setCustomMapType(String mapTypeId, StyledMapType styledType) {
425                    impl.setCustomMapType(mapTypeId, styledType);
426            }
427    
428            /**
429             * gets a registry of MapType instances
430             */
431            public MapTypeRegistry getMapTypeRegistry() {
432                    return impl.getMapTypeRegistry();
433            };
434    
435            /**
436             * TODO sets Additional map types to overlay.
437             * 
438             * @param overlayMapTypes
439             */
440            public void setOverlayMapTypes(MVCArray<JavaScriptObject> overlayMapTypes) {
441                    impl.setOverlayMapTypes(overlayMapTypes);
442            };
443    
444            /**
445             * TODO gets Additional map types to overlay.
446             */
447            public MVCArray<JavaScriptObject> getOverlayMapTypes() {
448                    return impl.getOverlayMapTypes();
449            };
450    
451            /**
452             * This event is fired when the viewport bounds have changed.
453             * 
454             * @param handler
455             *            {@link BoundsChangeMapHandler}
456             * @return {@link MapHandlerRegistration}
457             */
458            public HandlerRegistration addBoundsChangeHandler(
459                            BoundsChangeMapHandler handler) {
460                    return impl.addBoundsChangeHandler(handler);
461            }
462    
463            /**
464             * This event is fired when the map center property changes.
465             * 
466             * @param handler
467             *            {@link CenterChangeMapHandler}
468             * @return {@link MapHandlerRegistration}
469             */
470            public HandlerRegistration addCenterChangeHandler(
471                            CenterChangeMapHandler handler) {
472                    return impl.addCenterChangeHandler(handler);
473            }
474    
475            /**
476             * This event is fired when the user clicks on the map (but not when they
477             * click on a marker or infowindow).
478             * 
479             * @param handler
480             *            {@link ClickMapHandler}
481             * @return {@link MapHandlerRegistration}
482             */
483            public HandlerRegistration addClickHandler(ClickMapHandler handler) {
484                    return impl.addClickHandler(handler);
485            }
486    
487            /**
488             * This event is fired when the user double-clicks on the map. Note that the
489             * click event will also fire, right before this one.
490             * 
491             * @param handler
492             *            {@link DblClickMapHandler}
493             * @return {@link MapHandlerRegistration}
494             */
495            public HandlerRegistration addDblClickHandler(DblClickMapHandler handler) {
496                    return impl.addDblClickHandler(handler);
497            }
498    
499            /**
500             * This event is repeatedly fired while the user drags the map.
501             * 
502             * @param handler
503             *            {@link DragMapHandler}
504             * @return {@link MapHandlerRegistration}
505             */
506            public HandlerRegistration addDragHandler(DragMapHandler handler) {
507                    return impl.addDragHandler(handler);
508            }
509    
510            /**
511             * This event is fired when the user stops dragging the map.
512             * 
513             * @param handler
514             *            {@link DragEndMapHandler}
515             * @return {@link MapHandlerRegistration}
516             */
517            public HandlerRegistration addDragEndHandler(DragEndMapHandler handler) {
518                    return impl.addDragEndHandler(handler);
519            }
520    
521            /**
522             * This event is fired when the user starts dragging the map.
523             * 
524             * @param handler
525             *            {@link DragStartMapHandler}
526             * @return {@link MapHandlerRegistration}
527             */
528            public HandlerRegistration addDragStartHandler(DragStartMapHandler handler) {
529                    return impl.addDragStartHandler(handler);
530            }
531    
532            /**
533             * This event is fired when the map heading property changes.
534             * 
535             * @param handler
536             *            {@link HeadingChangeMapHandler}
537             * @return {@link MapHandlerRegistration}
538             */
539            public HandlerRegistration addHeadingChangeHandler(
540                            HeadingChangeMapHandler handler) {
541                    return impl.addHeadingChangeHandler(handler);
542            }
543    
544            /**
545             * This event is fired when the map becomes idle after panning or zooming.
546             * 
547             * @param handler
548             *            {@link IdleMapHandler}
549             * @return {@link MapHandlerRegistration}
550             */
551            public HandlerRegistration addIdleHandler(IdleMapHandler handler) {
552                    return impl.addIdleHandler(handler);
553            }
554    
555            /**
556             * This event is fired when the mapTypeId property changes.
557             * 
558             * @param handler
559             *            {@link MapTypeIdChangeMapHandler}
560             * @return {@link MapHandlerRegistration}
561             */
562            public HandlerRegistration addMapTypeIdChangeHandler(
563                            MapTypeIdChangeMapHandler handler) {
564                    return impl.addMapTypeIdChangeHandler(handler);
565            }
566    
567            /**
568             * This event is fired whenever the user's mouse moves over the map
569             * container.
570             * 
571             * @param handler
572             *            {@link MouseMoveMapHandler}
573             * @return {@link MapHandlerRegistration}
574             */
575            public HandlerRegistration addMouseMoveHandler(MouseMoveMapHandler handler) {
576                    return impl.addMouseMoveHandler(handler);
577            }
578    
579            /**
580             * This event is fired when the user's mouse exits the map container.
581             * 
582             * @param handler
583             *            {@link MouseOutMapHandler}
584             * @return {@link MapHandlerRegistration}
585             */
586            public HandlerRegistration addMouseOutMoveHandler(MouseOutMapHandler handler) {
587                    return impl.addMouseOutMoveHandler(handler);
588            }
589    
590            /**
591             * This event is fired when the user's mouse enters the map container.
592             * 
593             * @param handler
594             *            {@link MouseOverMapHandler}
595             * @return {@link MapHandlerRegistration}
596             */
597            public HandlerRegistration addMouseOverHandler(MouseOverMapHandler handler) {
598                    return impl.addMouseOverHandler(handler);
599            }
600    
601            /**
602             * This event is fired when the projection has changed.
603             * 
604             * @param handler
605             *            {@link ProjectionChangeMapHandler}
606             * @return {@link MapHandlerRegistration}
607             */
608            public HandlerRegistration addProjectionChangeHandler(
609                            ProjectionChangeMapHandler handler) {
610                    return impl.addProjectionChangeHandler(handler);
611            }
612    
613            /**
614             * Developers should trigger this event on the map when the div changes
615             * size: google.maps.event.trigger(map, 'resize') .
616             * 
617             * @param handler
618             *            {@link ResizeMapHandler}
619             * @return {@link MapHandlerRegistration}
620             */
621            public HandlerRegistration addResizeHandler(ResizeMapHandler handler) {
622                    return impl.addResizeHandler(handler);
623            }
624    
625            /**
626             * This event is fired when the DOM contextmenu event is fired on the map
627             * container.
628             * 
629             * @param handler
630             *            {@link RightClickMapHandler}
631             * @return {@link MapHandlerRegistration}
632             */
633            public HandlerRegistration addRightClickHandler(RightClickMapHandler handler) {
634                    return impl.addRightClickHandler(handler);
635            }
636    
637            /**
638             * This event is fired when the visible tiles have finished loading.
639             * 
640             * @param handler
641             *            {@link TilesLoadedMapHandler}
642             * @return {@link MapHandlerRegistration}
643             */
644            public HandlerRegistration addTilesLoadedHandler(
645                            TilesLoadedMapHandler handler) {
646                    return impl.addTilesLoadedHandler(handler);
647            }
648    
649            /**
650             * This event is fired when the map tilt property changes.
651             * 
652             * @param handler
653             *            {@link TiltChangeMapHandler}
654             * @return {@link MapHandlerRegistration}
655             */
656            public HandlerRegistration addTiltChangeHandler(TiltChangeMapHandler handler) {
657                    return impl.addTiltChangeHandler(handler);
658            }
659    
660            /**
661             * This event is fired when the map zoom property changes.
662             * 
663             * @param handler
664             *            {@link ZoomChangeMapHandler}
665             * @return {@link MapHandlerRegistration}
666             */
667            public HandlerRegistration addZoomChangeHandler(ZoomChangeMapHandler handler) {
668                    return impl.addZoomChangeHandler(handler);
669            }
670    
671    }