Merge "Update the "SDK valid" error message while user edits textfield" into idea133
diff --git a/android/src/com/android/tools/idea/gradle/eclipse/AdtImportSdkStep.java b/android/src/com/android/tools/idea/gradle/eclipse/AdtImportSdkStep.java
index 459e907..cf41d87 100644
--- a/android/src/com/android/tools/idea/gradle/eclipse/AdtImportSdkStep.java
+++ b/android/src/com/android/tools/idea/gradle/eclipse/AdtImportSdkStep.java
@@ -30,7 +30,9 @@
 
   AdtImportSdkStep(WizardContext context) {
     super(context);
-    myConfigurable = new DefaultSdksConfigurable();
+    // TODO: Pass in a context here which allows the configurable to request
+    // validation when the text field is edited
+    myConfigurable = new DefaultSdksConfigurable(null);
   }
 
   @Override
diff --git a/android/src/com/android/tools/idea/gradle/structure/AndroidProjectStructureConfigurable.java b/android/src/com/android/tools/idea/gradle/structure/AndroidProjectStructureConfigurable.java
index 793ba03..3193eac 100644
--- a/android/src/com/android/tools/idea/gradle/structure/AndroidProjectStructureConfigurable.java
+++ b/android/src/com/android/tools/idea/gradle/structure/AndroidProjectStructureConfigurable.java
@@ -26,6 +26,7 @@
 import com.intellij.ide.util.PropertiesComponent;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.editor.colors.EditorColors;
 import com.intellij.openapi.editor.colors.EditorColorsManager;
@@ -69,7 +70,8 @@
 /**
  * Contents of the "Project Structure" dialog, for Gradle-based Android projects, in Android Studio.
  */
-public class AndroidProjectStructureConfigurable extends BaseConfigurable implements GradleSyncListener, SearchableConfigurable {
+public class AndroidProjectStructureConfigurable extends BaseConfigurable implements GradleSyncListener, SearchableConfigurable,
+                                                                                     ConfigurableHost {
   public static final DataKey<AndroidProjectStructureConfigurable> KEY = DataKey.create("AndroidProjectStructureConfiguration");
   @NotNull private final Project myProject;
   @NotNull private final Disposable myDisposable;
@@ -83,7 +85,7 @@
   @NotNull private final Wrapper myDetails = new Wrapper();
   @NotNull private final UiState myUiState;
 
-  @NotNull private final DefaultSdksConfigurable mySdksConfigurable = new DefaultSdksConfigurable();
+  @NotNull private final DefaultSdksConfigurable mySdksConfigurable = new DefaultSdksConfigurable(this);
   @NotNull private final Map<String, AndroidModuleConfigurable> myModuleConfigurablesByName = Maps.newHashMap();
 
   private JComponent myToFocus;
@@ -401,6 +403,18 @@
     c.repaint();
   }
 
+  @Override
+  public void requestValidation() {
+    ApplicationManager.getApplication().invokeLater(new Runnable() {
+      @Override
+      public void run() {
+        if (myErrorsPanel != null) {
+          validateState();
+        }
+      }
+    });
+  }
+
   private class MainPanel extends JPanel implements DataProvider {
     MainPanel() {
       super(new BorderLayout());
diff --git a/android/src/com/android/tools/idea/gradle/structure/ConfigurableHost.java b/android/src/com/android/tools/idea/gradle/structure/ConfigurableHost.java
new file mode 100644
index 0000000..4021fe6
--- /dev/null
+++ b/android/src/com/android/tools/idea/gradle/structure/ConfigurableHost.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.tools.idea.gradle.structure;
+
+import com.intellij.openapi.options.Configurable;
+
+/** Interface implemented by containers holding {@link Configurable} */
+public interface ConfigurableHost {
+  void requestValidation();
+}
diff --git a/android/src/com/android/tools/idea/gradle/structure/ConfigurationErrorsPanel.java b/android/src/com/android/tools/idea/gradle/structure/ConfigurationErrorsPanel.java
index 7564d02..bf3016e 100644
--- a/android/src/com/android/tools/idea/gradle/structure/ConfigurationErrorsPanel.java
+++ b/android/src/com/android/tools/idea/gradle/structure/ConfigurationErrorsPanel.java
@@ -154,7 +154,7 @@
   }
 
   void removeAllErrors() {
-    myListModel.getErrors().clear();
+    myListModel.removeErrors();
   }
 
   boolean hasCriticalErrors() {
@@ -690,6 +690,14 @@
     private List<ProjectConfigurationError> getErrors() {
       return myAllErrors;
     }
+
+    public void removeErrors() {
+      boolean hadErrors = !myAllErrors.isEmpty();
+      myAllErrors.clear();
+      if (hadErrors) {
+        fireContentsChanged(this, 0, 0);
+      }
+    }
   }
 
   private enum ViewType {
diff --git a/android/src/com/android/tools/idea/gradle/structure/DefaultSdksConfigurable.java b/android/src/com/android/tools/idea/gradle/structure/DefaultSdksConfigurable.java
index e83e3b3..ffd5aa2 100644
--- a/android/src/com/android/tools/idea/gradle/structure/DefaultSdksConfigurable.java
+++ b/android/src/com/android/tools/idea/gradle/structure/DefaultSdksConfigurable.java
@@ -33,6 +33,7 @@
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
 import com.intellij.util.Function;
 import org.jetbrains.android.actions.RunAndroidSdkManagerAction;
 import org.jetbrains.android.sdk.AndroidSdkData;
@@ -41,6 +42,7 @@
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import javax.swing.event.DocumentEvent;
 import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -55,6 +57,7 @@
 public class DefaultSdksConfigurable extends BaseConfigurable implements ValidationAwareConfigurable {
   private static final String CHOOSE_VALID_JDK_DIRECTORY_ERR = "Please choose a valid JDK directory.";
   private static final String CHOOSE_VALID_SDK_DIRECTORY_ERR = "Please choose a valid Android SDK directory.";
+  private final ConfigurableHost myHost;
 
   // These paths are system-dependent.
   @NotNull private String myOriginalJdkHomePath;
@@ -66,7 +69,8 @@
 
   private DetailsComponent myDetailsComponent;
 
-  public DefaultSdksConfigurable() {
+  public DefaultSdksConfigurable(@Nullable ConfigurableHost host) {
+    myHost = host;
     myWholePanel.setPreferredSize(new Dimension(700, 500));
 
     myDetailsComponent = new DetailsComponent();
@@ -118,7 +122,8 @@
       }
     });
 
-    mySdkLocationTextField = new TextFieldWithBrowseButton(new ActionListener() {
+    JTextField textField = new JTextField(10);
+    mySdkLocationTextField = new TextFieldWithBrowseButton(textField, new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent e) {
         VirtualFile suggestedDir = null;
@@ -133,6 +138,7 @@
         }
       }
     });
+    installValidationListener(textField);
   }
 
   private void createJdkLocationTextField() {
@@ -146,7 +152,8 @@
       }
     });
 
-    myJdkLocationTextField = new TextFieldWithBrowseButton(new ActionListener() {
+    JTextField textField = new JTextField(10);
+    myJdkLocationTextField = new TextFieldWithBrowseButton(textField, new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent e) {
         VirtualFile suggestedDir = null;
@@ -161,6 +168,18 @@
         }
       }
     });
+    installValidationListener(textField);
+  }
+
+  private void installValidationListener(JTextField textField) {
+    if (myHost != null) {
+      textField.getDocument().addDocumentListener(new DocumentAdapter() {
+        @Override
+        protected void textChanged(DocumentEvent e) {
+          myHost.requestValidation();
+        }
+      });
+    }
   }
 
   @NotNull