FileUpload now implements HasChangeHandlers, which is fired when a user changes the file in the widget.

Patch by: jlabanca
Review by: ecc (desk)
Issue: 3187



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4768 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3187.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3187.java
new file mode 100644
index 0000000..162a816
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3187.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 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.museum.client.defaultmuseum;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.FileUpload;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * {@link FileUpload} onChange event fires correctly in all browsers.
+ */
+public class Issue3187 extends AbstractIssue {
+  @Override
+  public Widget createIssue() {
+    FileUpload fileUpload = new FileUpload();
+    fileUpload.addChangeHandler(new ChangeHandler() {
+      public void onChange(ChangeEvent event) {
+        Window.alert("Value Changed");
+      }
+    });
+    return fileUpload;
+  }
+
+  @Override
+  public String getInstructions() {
+    return "Change the file path and verify an alert dialog appears.  In "
+        + "Opera, the onChange event should only be fired when the box is "
+        + "blurred.";
+  }
+
+  @Override
+  public String getSummary() {
+    return "FileUpload supports change events";
+  }
+
+  @Override
+  public boolean hasCSS() {
+    return false;
+  }
+}
diff --git a/user/src/com/google/gwt/user/FileUpload.gwt.xml b/user/src/com/google/gwt/user/FileUpload.gwt.xml
new file mode 100644
index 0000000..56cfadd
--- /dev/null
+++ b/user/src/com/google/gwt/user/FileUpload.gwt.xml
@@ -0,0 +1,28 @@
+<!--                                                                        -->
+<!-- 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<!-- Deferred binding rules for FileUpload.                                 -->
+<!--                                                                        -->
+<!-- This module is typically inherited via com.google.gwt.user.User        -->
+<!--                                                                        -->
+<module>
+	<inherits name="com.google.gwt.core.Core"/>
+	<inherits name="com.google.gwt.user.UserAgent"/>
+
+	<!-- Opera fires change events on every character typed -->
+	<replace-with class="com.google.gwt.user.client.ui.FileUpload.FileUploadImplOpera">
+		<when-type-is class="com.google.gwt.user.client.ui.FileUpload.FileUploadImpl"/>
+		<when-property-is name="user.agent" value="opera"/>
+	</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 af4102c..87c4bb7 100644
--- a/user/src/com/google/gwt/user/User.gwt.xml
+++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -41,6 +41,7 @@
    <inherits name="com.google.gwt.user.Window" />
    <inherits name="com.google.gwt.user.Tree"/>
    <inherits name="com.google.gwt.user.Hyperlink"/>
+   <inherits name="com.google.gwt.user.FileUpload"/>
    <inherits name="com.google.gwt.user.datepicker.DatePicker"/>
 
    <super-source path="translatable"/>
diff --git a/user/src/com/google/gwt/user/client/ui/FileUpload.java b/user/src/com/google/gwt/user/client/ui/FileUpload.java
index f1bbf02..dba1f02 100644
--- a/user/src/com/google/gwt/user/client/ui/FileUpload.java
+++ b/user/src/com/google/gwt/user/client/ui/FileUpload.java
@@ -15,9 +15,15 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.HasChangeHandlers;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Event;
 
 /**
  * A widget that wraps the HTML &lt;input type='file'&gt; element. This widget
@@ -29,7 +35,70 @@
  * {@example com.google.gwt.examples.FormPanelExample}
  * </p>
  */
-public class FileUpload extends Widget implements HasName {
+public class FileUpload extends Widget implements HasName, HasChangeHandlers {
+  /**
+   * Implementation class for {@link FileUpload}.
+   */
+  private static class FileUploadImpl {
+    /**
+     * Initialize the impl class.
+     * 
+     * @param fileUpload the {@link FileUpload} to handle
+     */
+    public void init(FileUpload fileUpload) {
+    }
+
+    /**
+     * Handle the browser event.
+     * 
+     * @param event the native event
+     * @return true to fire the event normally, false to ignore it
+     */
+    public boolean onBrowserEvent(Event event) {
+      return true;
+    }
+  }
+
+  /**
+   * Opera fires an onChange event every time a character is typed, but we only
+   * want to fire one when the input element is blurred.
+   */
+  @SuppressWarnings("unused")
+  private static class FileUploadImplOpera extends FileUploadImpl {
+    private FileUpload fileUpload;
+    private boolean eventPending;
+    private boolean allowEvent;
+
+    @Override
+    public void init(FileUpload fileUpload) {
+      this.fileUpload = fileUpload;
+      fileUpload.sinkEvents(Event.ONBLUR);
+    }
+
+    @Override
+    public boolean onBrowserEvent(Event event) {
+      switch (event.getTypeInt()) {
+        case Event.ONCHANGE:
+          // When we fire the change event onBlur, we allow it to pass to
+          // Widget#onBrowserEvent().
+          if (!allowEvent) {
+            eventPending = true;
+            return false;
+          }
+          break;
+        case Event.ONBLUR:
+          // Trigger a change event now.
+          if (eventPending) {
+            allowEvent = true;
+            fileUpload.getElement().dispatchEvent(Document.get().createChangeEvent());
+            allowEvent = false;
+            eventPending = false;
+          }
+          break;
+      }
+      return true;
+    }
+  }
 
   /**
    * Creates a FileUpload widget that wraps an existing &lt;input
@@ -54,12 +123,16 @@
     return fileUpload;
   }
 
+  private FileUploadImpl impl;
+
   /**
    * Constructs a new file upload widget.
    */
   public FileUpload() {
     setElement(Document.get().createFileInputElement());
     setStyleName("gwt-FileUpload");
+    impl = GWT.create(FileUploadImpl.class);
+    impl.init(this);
   }
 
   /**
@@ -74,6 +147,10 @@
     setElement(element);
   }
 
+  public HandlerRegistration addChangeHandler(ChangeHandler handler) {
+    return addDomHandler(handler, ChangeEvent.getType());
+  }
+
   /**
    * Gets the filename selected by the user. This property has no mutator, as
    * browser security restrictions preclude setting it.
@@ -88,6 +165,13 @@
     return getInputElement().getName();
   }
 
+  @Override
+  public void onBrowserEvent(Event event) {
+    if (impl.onBrowserEvent(event)) {
+      super.onBrowserEvent(event);
+    }
+  }
+
   public void setName(String name) {
     getInputElement().setName(name);
   }