Joel,

Would you please review the attached patch that solves a problem related
when widgets need to be attached/detached from panels for the Maps
InfoWindow.

It addresses issue 156 in gwt-google-apis:
http://code.google.com/p/gwt-google-apis/issues/detail?id=156

M      maps/maps/src/com/google/gwt/maps/client/InfoWindow.java

Thanks,
-Eric.
-- 
Eric Z. Ayers - GWT Team - Atlanta, GA USA
http://code.google.com/webtoolkit/

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Index: maps/maps/src/com/google/gwt/maps/client/InfoWindow.java
===================================================================
--- maps/maps/src/com/google/gwt/maps/client/InfoWindow.java	(revision 751)
+++ maps/maps/src/com/google/gwt/maps/client/InfoWindow.java	(working copy)
@@ -22,6 +22,7 @@
 import com.google.gwt.maps.client.event.InfoWindowRestoreClickHandler;
 import com.google.gwt.maps.client.event.InfoWindowRestoreEndHandler;
 import com.google.gwt.maps.client.event.MapInfoWindowCloseHandler;
+import com.google.gwt.maps.client.event.MapInfoWindowOpenHandler;
 import com.google.gwt.maps.client.event.InfoWindowCloseClickHandler.InfoWindowCloseClickEvent;
 import com.google.gwt.maps.client.event.InfoWindowMaximizeClickHandler.InfoWindowMaximizeClickEvent;
 import com.google.gwt.maps.client.event.InfoWindowMaximizeEndHandler.InfoWindowMaximizeEndEvent;
@@ -75,6 +76,28 @@
     }
   }
 
+  /**
+   * Handlers that cover attaching and removing widgets from the virtual panels
+   * created above.
+   */
+  private MapInfoWindowOpenHandler attachInfoWindowOpenHandler;
+  private MapInfoWindowCloseHandler attachMapInfoWindowCloseHandler;
+  private InfoWindowMaximizeClickHandler attachMaximizeClickHandler;
+  private InfoWindowMaximizeEndHandler attachMaximizeEndHandler;
+  private InfoWindowRestoreClickHandler attachRestoreClickHandler;
+  private InfoWindowRestoreEndHandler attachRestoreEndHandler;
+
+  /**
+   * Private copies of the widgets passed in from InfoWindowContent. Make copies
+   * so that an unsuspecting user doesn't overwrite this information.
+   */
+  private Widget contentMaxWidget;
+  private List<Widget> contentWidgets;
+  private boolean contentWidgetsAttached;
+
+  /**
+   * Tracks handlers attached to this InfoWindow instance.
+   */
   private HandlerCollection<InfoWindowCloseClickHandler> infoWindowCloseClickHandlers;
   private HandlerCollection<InfoWindowMaximizeClickHandler> infoWindowMaximizeClickHandlers;
   private HandlerCollection<InfoWindowMaximizeEndHandler> infoWindowMaximizeEndHandlers;
@@ -82,13 +105,14 @@
   private HandlerCollection<InfoWindowRestoreEndHandler> infoWindowRestoreEndHandlers;
 
   private final JavaScriptObject jsoPeer;
-
   private final MapWidget map;
 
   /**
-   * The virtual panel is used as a point to attach.
+   * Virtual Panels used as a point to attach the maximized/restored state
+   * widgets. This plugs the widget into the GWT Event system.
    */
-  private final VirtualPanel virtualPanel = new VirtualPanel();
+  private final VirtualPanel virtualMaximizedPanel = new VirtualPanel();
+  private final VirtualPanel virtualRestoredPanel = new VirtualPanel();
 
   /**
    * Package-private constructor to prevent instantiation from outside of the
@@ -275,7 +299,8 @@
    *          InfoWindow.
    */
   public void open(LatLng point, InfoWindowContent content) {
-    beginAttach(content);
+    addAttachHandlers(content);
+
     switch (content.getType()) {
       case InfoWindowContent.TYPE_ELEMENT:
         MapImpl.impl.openInfoWindow(map, point, content.getContent(),
@@ -289,7 +314,6 @@
         MapImpl.impl.showMapBlowup(map, point, content.getOptions());
         break;
     }
-    finishAttach(content);
   }
 
   /**
@@ -300,7 +324,8 @@
    *          InfoWindow.
    */
   public void open(Marker marker, InfoWindowContent content) {
-    beginAttach(content);
+    addAttachHandlers(content);
+
     switch (content.getType()) {
       case InfoWindowContent.TYPE_ELEMENT:
         MarkerImpl.impl.openInfoWindow(marker, content.getContent(),
@@ -314,12 +339,12 @@
         MarkerImpl.impl.showMapBlowup(marker, content.getOptions());
         break;
     }
-    finishAttach(content);
   }
 
   /**
    * Removes a single handler of this map previously added with
-   * [EMAIL PROTECTED] InfoWindow#addInfoWindowCloseClickHandler(InfoWindowCloseClickHandler)}.
+   * [EMAIL PROTECTED] InfoWindow#addInfoWindowCloseClickHandler(InfoWindowCloseClickHandler)}
+   * .
    * 
    * @param handler the handler to remove
    */
@@ -332,7 +357,8 @@
 
   /**
    * Removes a single handler of this map previously added with
-   * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeClickHandler(InfoWindowMaximizeClickHandler)}.
+   * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeClickHandler(InfoWindowMaximizeClickHandler)}
+   * .
    * 
    * @param handler the handler to remove
    */
@@ -346,7 +372,8 @@
 
   /**
    * Removes a single handler of this map previously added with
-   * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeEndHandler(InfoWindowMaximizeEndHandler)}.
+   * [EMAIL PROTECTED] InfoWindow#addInfoWindowMaximizeEndHandler(InfoWindowMaximizeEndHandler)}
+   * .
    * 
    * @param handler the handler to remove
    */
@@ -359,7 +386,8 @@
 
   /**
    * Removes a single handler of this map previously added with
-   * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreClickHandler(InfoWindowRestoreClickHandler)}.
+   * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreClickHandler(InfoWindowRestoreClickHandler)}
+   * .
    * 
    * @param handler the handler to remove
    */
@@ -370,11 +398,10 @@
     }
   }
 
-  // TODO(zundel): Implement reset?
-
   /**
    * Removes a single handler of this map previously added with
-   * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreEndHandler(InfoWindowRestoreEndHandler)}.
+   * [EMAIL PROTECTED] InfoWindow#addInfoWindowRestoreEndHandler(InfoWindowRestoreEndHandler)}
+   * .
    * 
    * @param handler the handler to remove
    */
@@ -387,7 +414,8 @@
 
   /**
    * Restores the info window to its default (non-maximized) state. The
-   * infowindow must have been opened with maxContent or maxTitle options
+   * [EMAIL PROTECTED] InfoWindow} must have been opened with maxContent or maxTitle
+   * options
    */
   public void restore() {
     InfoWindowImpl.impl.restore(jsoPeer);
@@ -403,6 +431,8 @@
     InfoWindowImpl.impl.selectTab(jsoPeer, index);
   }
 
+  // TODO(zundel): Implement reset?
+
   /**
    * Enables or disables maximization of the info window. A maximizable info
    * window expands to fill most of the map with contents specified via the
@@ -412,7 +442,7 @@
    * maxTitle will have maximization enabled by default.
    * 
    * Note that if the info window is currently opened and this method is set to
-   * disable maximizing, this function will remove the maximize buton but will
+   * disable maximizing, this function will remove the maximize button but will
    * not restore the window to its minimized state.
    */
   public void setMaximizeEnabled(boolean enabled) {
@@ -495,30 +525,147 @@
     infoWindowRestoreEndHandlers.trigger();
   }
 
-  private void beginAttach(InfoWindowContent content) {
-    List<Widget> contentWidgets = content.getWidgets();
-    if (content.getMaxContent() != null) {
-      contentWidgets.add(content.getMaxContent());
+  /**
+   * Adds handlers that take care of attaching and detaching widgets from the
+   * hierarchy.
+   * 
+   * @param content contains the widgets to attach/detach.
+   */
+  private void addAttachHandlers(InfoWindowContent content) {
+
+    // Make a private copy of the widgets passed in from the content object
+    // so no one can pull the rug out from under our attach handlers.
+    contentWidgets = content.getWidgets();
+    contentMaxWidget = content.getMaxContent();
+
+    attachInfoWindowOpenHandler = new MapInfoWindowOpenHandler() {
+
+      public void onInfoWindowOpen(MapInfoWindowOpenEvent event) {
+        finishAttach();
+      }
+
+    };
+    map.addInfoWindowOpenHandler(attachInfoWindowOpenHandler);
+
+    addMaxContentHandlers();
+    addCloseHandler();
+
+    // Everything is setup. Go ahead and start to attach the widgets for the
+    // restored view.
+    beginAttach();
+  }
+
+  /**
+   * Removes the handlers used to attach / detach widgets.
+   */
+  private void addCloseHandler() {
+
+    attachMapInfoWindowCloseHandler = new MapInfoWindowCloseHandler() {
+
+      public void onInfoWindowClose(MapInfoWindowCloseEvent event) {
+
+        if (contentWidgetsAttached) {
+          removeContentWidgets();
+          contentWidgetsAttached = false;
+        }
+        if (attachRestoreClickHandler != null) {
+          removeInfoWindowRestoreClickHandler(attachRestoreClickHandler);
+          attachRestoreClickHandler = null;
+        }
+        if (attachRestoreEndHandler != null) {
+          removeInfoWindowRestoreEndHandler(attachRestoreEndHandler);
+          attachRestoreEndHandler = null;
+        }
+        if (attachMaximizeEndHandler != null) {
+          removeInfoWindowMaximizeEndHandler(attachMaximizeEndHandler);
+          attachMaximizeEndHandler = null;
+        }
+        if (attachMaximizeClickHandler != null) {
+          removeInfoWindowMaximizeClickHandler(attachMaximizeClickHandler);
+          attachMaximizeClickHandler = null;
+        }
+        if (attachInfoWindowOpenHandler != null) {
+          map.removeInfoWindowOpenHandler(attachInfoWindowOpenHandler);
+          attachInfoWindowOpenHandler = null;
+        }
+
+        // Remove this handler too.
+        if (attachMapInfoWindowCloseHandler != null) {
+          map.removeInfoWindowCloseHandler(attachMapInfoWindowCloseHandler);
+          attachMapInfoWindowCloseHandler = null;
+        }
+      }
+
+    };
+    map.addInfoWindowCloseHandler(attachMapInfoWindowCloseHandler);
+  }
+
+  /**
+   * When the info window is maximized and restored, the widgets need to be
+   * detached and re-attached from the 2 virtual panels, as they are detached
+   * and re-attached to the DOM.
+   */
+  private void addMaxContentHandlers() {
+    if (contentMaxWidget == null) {
+      return;
     }
+
+    attachMaximizeClickHandler = new InfoWindowMaximizeClickHandler() {
+
+      public void onMaximizeClick(InfoWindowMaximizeClickEvent event) {
+        virtualMaximizedPanel.beginAttach(contentMaxWidget);
+      }
+
+    };
+    addInfoWindowMaximizeClickHandler(attachMaximizeClickHandler);
+
+    attachMaximizeEndHandler = new InfoWindowMaximizeEndHandler() {
+      public void onMaximizeEnd(InfoWindowMaximizeEndEvent event) {
+        virtualMaximizedPanel.finishAttach(contentMaxWidget);
+        contentWidgetsAttached = false;
+        removeContentWidgets();
+      }
+    };
+    addInfoWindowMaximizeEndHandler(attachMaximizeEndHandler);
+
+    attachRestoreClickHandler = new InfoWindowRestoreClickHandler() {
+
+      public void onRestoreClick(InfoWindowRestoreClickEvent event) {
+        virtualMaximizedPanel.remove(contentMaxWidget);
+        beginAttach();
+      }
+
+    };
+    addInfoWindowRestoreClickHandler(attachRestoreClickHandler);
+
+    attachRestoreEndHandler = new InfoWindowRestoreEndHandler() {
+
+      public void onRestoreEnd(InfoWindowRestoreEndEvent event) {
+        contentWidgetsAttached = false;
+        finishAttach();
+      }
+    };
+    addInfoWindowRestoreEndHandler(attachRestoreEndHandler);
+  }
+
+  /**
+   * Begins attaching the restored view widgets to the InfoWindow.
+   */
+  private void beginAttach() {
     for (int i = 0; i < contentWidgets.size(); i++) {
-      virtualPanel.beginAttach(contentWidgets.get(i));
+      virtualRestoredPanel.beginAttach(contentWidgets.get(i));
     }
   }
 
-  private void finishAttach(InfoWindowContent content) {
-    final List<Widget> contentWidgets = content.getWidgets();
+  /**
+   * Finish the attach step (must be done after elements are actually attached
+   * to the DOM).
+   */
+  private void finishAttach() {
     for (int i = 0; i < contentWidgets.size(); i++) {
-      virtualPanel.finishAttach(contentWidgets.get(i));
+      virtualRestoredPanel.finishAttach(contentWidgets.get(i));
     }
-    
-    map.addInfoWindowCloseHandler(new MapInfoWindowCloseHandler() {
-     
-      public void onInfoWindowClose(MapInfoWindowCloseEvent event) {
-        for (int i = 0; i < contentWidgets.size(); i++) {
-          virtualPanel.remove(contentWidgets.get(i));
-        }        
-      }
-    });
+    contentWidgetsAttached = true;
   }
 
   /**
@@ -570,4 +717,10 @@
           jsoPeer, MapEvent.RESTOREEND);
     }
   }
+
+  private void removeContentWidgets() {
+    for (int i = 0; i < contentWidgets.size(); i++) {
+      virtualRestoredPanel.remove(contentWidgets.get(i));
+    }
+  }
 }

Reply via email to