gradle: customizing of gradle project settings UI
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 24 Feb 2015 16:43:42 +0000 (19:43 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 24 Feb 2015 16:47:41 +0000 (19:47 +0300)
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/ExternalSystemSettingsControlCustomizer.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalSystemJdkComboBox.java
plugins/gradle/src/META-INF/plugin.xml
plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControlBuilder.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSettingsControlProvider.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/ImportFromGradleControl.java

index f1daae220d8540b2fc7d1454990f41efa6818084..91f363684185cb6f11f78b0a5acee647cf14c1a4 100644 (file)
@@ -40,12 +40,23 @@ public abstract class AbstractExternalProjectSettingsControl<S extends ExternalP
 
   @NotNull private S myInitialSettings;
 
+  @Nullable
   private JBCheckBox myUseAutoImportBox;
+  @Nullable
   private JBCheckBox myCreateEmptyContentRootDirectoriesBox;
-  private boolean myHideUseAutoImportBox;
+  @NotNull
+  private ExternalSystemSettingsControlCustomizer myCustomizer;
 
   protected AbstractExternalProjectSettingsControl(@NotNull S initialSettings) {
+    this(null, initialSettings, null);
+  }
+
+  protected AbstractExternalProjectSettingsControl(@Nullable Project project,
+                                                   @NotNull S initialSettings,
+                                                   @Nullable ExternalSystemSettingsControlCustomizer controlCustomizer) {
+    myProject = project;
     myInitialSettings = initialSettings;
+    myCustomizer = controlCustomizer == null ? new ExternalSystemSettingsControlCustomizer() : controlCustomizer;
   }
 
   @NotNull
@@ -53,27 +64,31 @@ public abstract class AbstractExternalProjectSettingsControl<S extends ExternalP
     return myInitialSettings;
   }
 
-  public void hideUseAutoImportBox() {
-    myHideUseAutoImportBox = true;
-  }
-
   @Override
   public void fillUi(@NotNull PaintAwarePanel canvas, int indentLevel) {
-    myUseAutoImportBox = new JBCheckBox(ExternalSystemBundle.message("settings.label.use.auto.import"));
-    myUseAutoImportBox.setVisible(!myHideUseAutoImportBox);
-    canvas.add(myUseAutoImportBox, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-    myCreateEmptyContentRootDirectoriesBox =
-      new JBCheckBox(ExternalSystemBundle.message("settings.label.create.empty.content.root.directories"));
-    canvas.add(myCreateEmptyContentRootDirectoriesBox, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    if (!myCustomizer.isUseAutoImportBoxHidden()) {
+      myUseAutoImportBox = new JBCheckBox(ExternalSystemBundle.message("settings.label.use.auto.import"));
+      canvas.add(myUseAutoImportBox, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
+    if (!myCustomizer.isCreateEmptyContentRootDirectoriesBoxHidden()) {
+      myCreateEmptyContentRootDirectoriesBox =
+        new JBCheckBox(ExternalSystemBundle.message("settings.label.create.empty.content.root.directories"));
+      canvas.add(myCreateEmptyContentRootDirectoriesBox, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
     fillExtraControls(canvas, indentLevel); 
   }
   
   protected abstract void fillExtraControls(@NotNull PaintAwarePanel content, int indentLevel);
 
   public boolean isModified() {
-    return myUseAutoImportBox.isSelected() != getInitialSettings().isUseAutoImport()
-           || myCreateEmptyContentRootDirectoriesBox.isSelected() != getInitialSettings().isCreateEmptyContentRootDirectories()
-           || isExtraSettingModified();
+    boolean result = false;
+    if (!myCustomizer.isUseAutoImportBoxHidden() && myUseAutoImportBox != null) {
+      result = myUseAutoImportBox.isSelected() != getInitialSettings().isUseAutoImport();
+    }
+    if (!myCustomizer.isCreateEmptyContentRootDirectoriesBoxHidden() && myCreateEmptyContentRootDirectoriesBox != null) {
+      result = result || myCreateEmptyContentRootDirectoriesBox.isSelected() != getInitialSettings().isCreateEmptyContentRootDirectories();
+    }
+    return result || isExtraSettingModified();
   }
 
   protected abstract boolean isExtraSettingModified();
@@ -83,8 +98,12 @@ public abstract class AbstractExternalProjectSettingsControl<S extends ExternalP
   }
 
   public void reset(boolean isDefaultModuleCreation) {
-    myUseAutoImportBox.setSelected(getInitialSettings().isUseAutoImport());
-    myCreateEmptyContentRootDirectoriesBox.setSelected(getInitialSettings().isCreateEmptyContentRootDirectories());
+    if (!myCustomizer.isUseAutoImportBoxHidden() && myUseAutoImportBox != null) {
+      myUseAutoImportBox.setSelected(getInitialSettings().isUseAutoImport());
+    }
+    if (!myCustomizer.isCreateEmptyContentRootDirectoriesBoxHidden() && myCreateEmptyContentRootDirectoriesBox != null) {
+      myCreateEmptyContentRootDirectoriesBox.setSelected(getInitialSettings().isCreateEmptyContentRootDirectories());
+    }
     resetExtraSettings(isDefaultModuleCreation);
   }
 
@@ -92,8 +111,13 @@ public abstract class AbstractExternalProjectSettingsControl<S extends ExternalP
 
   @Override
   public void apply(@NotNull S settings) {
-    settings.setUseAutoImport(myUseAutoImportBox.isSelected());
-    settings.setCreateEmptyContentRootDirectories(myCreateEmptyContentRootDirectoriesBox.isSelected());
+    if (!myCustomizer.isUseAutoImportBoxHidden() && myUseAutoImportBox != null) {
+      settings.setUseAutoImport(myUseAutoImportBox.isSelected());
+    }
+
+    if (!myCustomizer.isCreateEmptyContentRootDirectoriesBoxHidden() && myCreateEmptyContentRootDirectoriesBox != null) {
+      settings.setCreateEmptyContentRootDirectories(myCreateEmptyContentRootDirectoriesBox.isSelected());
+    }
     if (myInitialSettings.getExternalProjectPath() != null) {
       settings.setExternalProjectPath(myInitialSettings.getExternalProjectPath());
     }
@@ -112,8 +136,13 @@ public abstract class AbstractExternalProjectSettingsControl<S extends ExternalP
   }
 
   public void updateInitialSettings() {
-    myInitialSettings.setUseAutoImport(myUseAutoImportBox.isSelected());
-    myInitialSettings.setCreateEmptyContentRootDirectories(myCreateEmptyContentRootDirectoriesBox.isSelected());
+    if (!myCustomizer.isUseAutoImportBoxHidden() && myUseAutoImportBox != null) {
+      myInitialSettings.setUseAutoImport(myUseAutoImportBox.isSelected());
+    }
+
+    if (!myCustomizer.isCreateEmptyContentRootDirectoriesBoxHidden() && myCreateEmptyContentRootDirectoriesBox != null) {
+      myInitialSettings.setCreateEmptyContentRootDirectories(myCreateEmptyContentRootDirectoriesBox.isSelected());
+    }
     updateInitialExtraSettings();
   }
 
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/ExternalSystemSettingsControlCustomizer.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/ExternalSystemSettingsControlCustomizer.java
new file mode 100644 (file)
index 0000000..4291f99
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.externalSystem.service.settings;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 2/24/2015
+ */
+public class ExternalSystemSettingsControlCustomizer {
+
+  private boolean hideUseAutoImportBox;
+  private boolean hideCreateEmptyContentRootDirectoriesBox;
+
+  public ExternalSystemSettingsControlCustomizer() {
+  }
+
+  public ExternalSystemSettingsControlCustomizer(boolean hideUseAutoImportBox, boolean hideCreateEmptyContentRootDirectoriesBox) {
+    this.hideUseAutoImportBox = hideUseAutoImportBox;
+    this.hideCreateEmptyContentRootDirectoriesBox = hideCreateEmptyContentRootDirectoriesBox;
+  }
+
+  public boolean isUseAutoImportBoxHidden() {
+    return hideUseAutoImportBox;
+  }
+
+  public boolean isCreateEmptyContentRootDirectoriesBoxHidden() {
+    return hideCreateEmptyContentRootDirectoriesBox;
+  }
+}
index 34d9e0b0b75a4149b759f3c9ab60fbe5c9812f35..4a1d89817cfd2f9b1993dbd37854d50851f8bfeb 100644 (file)
@@ -92,6 +92,7 @@ public class ExternalSystemJdkComboBox extends ComboBoxWithWidePopup {
     myProject = project;
   }
 
+  @NotNull
   public ExternalSystemJdkComboBox withoutJre() {
     suggestJre = false;
     return this;
index 73c35b7aa75fa3d5d6c0a692d9c940a60f04994f..26407888482e1455c3d3ca9be06a89acc850e099 100644 (file)
@@ -46,6 +46,7 @@
     <extensionPoint name="projectResolve" interface="org.jetbrains.plugins.gradle.service.project.GradleProjectResolverExtension"/>
     <extensionPoint name="taskManager" interface="org.jetbrains.plugins.gradle.service.task.GradleTaskManagerExtension"/>
     <extensionPoint name="resolve.contributor" interface="org.jetbrains.plugins.gradle.service.resolve.GradleMethodContextContributor"/>
+    <extensionPoint name="settingsControlProvider" interface="org.jetbrains.plugins.gradle.service.settings.GradleSettingsControlProvider"/>
   </extensionPoints>
 
   <extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
index 55257bd4eec46d3a0a8765d8125c0fcb47fb4b43..37a7442e3eb42927ad87500bd69456dd367299d8 100644 (file)
  */
 package org.jetbrains.plugins.gradle.service.settings;
 
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.externalSystem.model.settings.LocationSettingType;
 import com.intellij.openapi.externalSystem.service.settings.AbstractExternalProjectSettingsControl;
 import com.intellij.openapi.externalSystem.service.settings.AbstractImportFromExternalSystemControl;
-import com.intellij.openapi.externalSystem.service.ui.ExternalSystemJdkComboBox;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
 import com.intellij.openapi.externalSystem.util.PaintAwarePanel;
-import com.intellij.openapi.fileChooser.FileChooserDescriptor;
 import com.intellij.openapi.options.ConfigurationException;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.MessageType;
-import com.intellij.openapi.ui.TextComponentAccessor;
-import com.intellij.openapi.ui.TextFieldWithBrowseButton;
-import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.ui.components.JBLabel;
-import com.intellij.ui.components.JBRadioButton;
-import com.intellij.util.Alarm;
-import com.intellij.util.Consumer;
-import com.intellij.util.ObjectUtils;
-import com.intellij.util.ui.UIUtil;
-import org.gradle.util.GradleVersion;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.jetbrains.plugins.gradle.service.GradleInstallationManager;
-import org.jetbrains.plugins.gradle.settings.DistributionType;
 import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
-import org.jetbrains.plugins.gradle.util.GradleBundle;
-import org.jetbrains.plugins.gradle.util.GradleConstants;
-import org.jetbrains.plugins.gradle.util.GradleUtil;
-
-import javax.swing.*;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.io.File;
-import java.util.concurrent.TimeUnit;
-
-import static com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.USE_PROJECT_JDK;
 
 /**
  * @author Denis Zhdanov
@@ -66,354 +30,49 @@ import static com.intellij.openapi.externalSystem.service.execution.ExternalSyst
  */
 public class GradleProjectSettingsControl extends AbstractExternalProjectSettingsControl<GradleProjectSettings> {
 
-  private static final long BALLOON_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
-
-  @NotNull private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
-
-  @NotNull private LocationSettingType myGradleHomeSettingType = LocationSettingType.UNKNOWN;
-
-  @NotNull private final GradleInstallationManager myInstallationManager;
-
-  @SuppressWarnings("FieldCanBeLocal") // Used implicitly by reflection at disposeUIResources() and showUi()
-  private JLabel                    myGradleHomeLabel;
-  @SuppressWarnings("FieldCanBeLocal") // Used implicitly by reflection at disposeUIResources() and showUi()
-  private JLabel myGradleJdkLabel;
-  private ExternalSystemJdkComboBox myGradleJdkComboBox;
-  private TextFieldWithBrowseButton myGradleHomePathField;
-  private JBRadioButton             myUseWrapperButton;
-  private JBRadioButton             myUseWrapperWithVerificationButton;
-  private JBLabel                   myUseWrapperVerificationLabel;
-  private JBRadioButton             myUseLocalDistributionButton;
-  private JBRadioButton             myUseBundledDistributionButton;
-
-  private boolean myShowBalloonIfNecessary;
+  private final GradleProjectSettingsControlBuilder myBuilder;
 
   public GradleProjectSettingsControl(@NotNull GradleProjectSettings initialSettings) {
-    super(initialSettings);
-    myInstallationManager = ServiceManager.getService(GradleInstallationManager.class);
+    this(GradleSettingsControlProvider.get().getProjectSettingsControlBuilder(initialSettings));
   }
 
-  @Override
-  protected void fillExtraControls(@NotNull PaintAwarePanel content, int indentLevel) {
-    content.setPaintCallback(new Consumer<Graphics>() {
-      @Override
-      public void consume(Graphics graphics) {
-        showBalloonIfNecessary();
-      }
-    });
-
-    content.addPropertyChangeListener(new PropertyChangeListener() {
-      @Override
-      public void propertyChange(PropertyChangeEvent evt) {
-        if (!"ancestor".equals(evt.getPropertyName())) {
-          return;
-        }
-
-        // Configure the balloon to show on initial configurable drawing.
-        myShowBalloonIfNecessary = evt.getNewValue() != null && evt.getOldValue() == null;
-
-        if (evt.getNewValue() == null && evt.getOldValue() != null) {
-          // Cancel delayed balloons when the configurable is hidden.
-          myAlarm.cancelAllRequests();
-        }
-      }
-    });
-
-    myGradleHomeLabel = new JBLabel(GradleBundle.message("gradle.settings.text.home.path"));
-    initGradleHome();
-    myGradleJdkLabel = new JBLabel(GradleBundle.message("gradle.settings.text.jvm.path"));
-    myGradleJdkComboBox = new ExternalSystemJdkComboBox().withoutJre();
-
-    initControls();
-    content.add(myUseWrapperButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-    content.add(myUseWrapperWithVerificationButton, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
-    content.add(myUseWrapperVerificationLabel,  ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-    //content.add(Box.createGlue(), ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-    // Hide bundled distribution option for a while
-    // content.add(myUseBundledDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-    content.add(myUseLocalDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
-
-    content.add(myGradleHomeLabel, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
-    content.add(myGradleHomePathField, ExternalSystemUiUtil.getFillLineConstraints(0));
-
-    content.add(myGradleJdkLabel, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
-    content.add(myGradleJdkComboBox, ExternalSystemUiUtil.getFillLineConstraints(0));
+  public GradleProjectSettingsControl(@NotNull GradleProjectSettingsControlBuilder builder) {
+    super(null, builder.getInitialSettings(), builder.getExternalSystemSettingsControlCustomizer());
+    myBuilder = builder;
   }
 
-  private void initControls() {
-    ActionListener listener = new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        boolean localDistributionEnabled = myUseLocalDistributionButton.isSelected();
-        myGradleHomePathField.setEnabled(localDistributionEnabled);
-        if (localDistributionEnabled) {
-          if(myGradleHomePathField.getText().isEmpty()){
-            deduceGradleHomeIfPossible();
-          } else {
-            if(myInstallationManager.isGradleSdkHome(myGradleHomePathField.getText())){
-              myGradleHomeSettingType = LocationSettingType.EXPLICIT_CORRECT;
-            } else {
-              myGradleHomeSettingType = LocationSettingType.EXPLICIT_INCORRECT;
-              myShowBalloonIfNecessary = true;
-            }
-          }
-          showBalloonIfNecessary();
-        }
-        else {
-          myAlarm.cancelAllRequests();
-        }
-      }
-    };
-
-    myUseWrapperButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.default_wrapper.configured"));
-    myUseWrapperButton.addActionListener(listener);
-    myUseWrapperWithVerificationButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.customizable_wrapper"));
-    myUseWrapperWithVerificationButton.addActionListener(listener);
-    myUseWrapperVerificationLabel = new JBLabel(GradleBundle.message("gradle.settings.text.wrapper.customization.compatibility"));
-    myUseWrapperVerificationLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.MINI));
-    myUseWrapperVerificationLabel.setIcon(UIUtil.getBalloonInformationIcon());
-
-    myUseLocalDistributionButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.local.distribution"));
-    myUseLocalDistributionButton.addActionListener(listener);
-
-    myUseBundledDistributionButton = new JBRadioButton(
-      GradleBundle.message("gradle.settings.text.use.bundled.distribution", GradleVersion.current().getVersion()));
-    myUseBundledDistributionButton.addActionListener(listener);
-    myUseBundledDistributionButton.setEnabled(false);
-
-    ButtonGroup buttonGroup = new ButtonGroup();
-    buttonGroup.add(myUseWrapperButton);
-    buttonGroup.add(myUseWrapperWithVerificationButton);
-    buttonGroup.add(myUseBundledDistributionButton);
-    buttonGroup.add(myUseLocalDistributionButton);
-  }
-
-  private void initGradleHome() {
-    myGradleHomePathField = new TextFieldWithBrowseButton();
-
-    FileChooserDescriptor fileChooserDescriptor = GradleUtil.getGradleHomeFileChooserDescriptor();
-
-    myGradleHomePathField.addBrowseFolderListener(
-      "",
-      GradleBundle.message("gradle.settings.text.home.path"),
-      null,
-      fileChooserDescriptor,
-      TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT,
-      false
-    );
-    myGradleHomePathField.getTextField().getDocument().addDocumentListener(new DocumentListener() {
-      @Override
-      public void insertUpdate(DocumentEvent e) {
-        myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
-      }
-
-      @Override
-      public void removeUpdate(DocumentEvent e) {
-        myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
-      }
-
-      @Override
-      public void changedUpdate(DocumentEvent e) {
-      }
-    });
+  @Override
+  protected void fillExtraControls(@NotNull PaintAwarePanel content, int indentLevel) {
+    myBuilder.createAndFillControls(content, indentLevel);
   }
 
   @Override
   public boolean validate(@NotNull GradleProjectSettings settings) throws ConfigurationException {
-    String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
-    if (myUseLocalDistributionButton.isSelected()) {
-      if (StringUtil.isEmpty(gradleHomePath)) {
-        myGradleHomeSettingType = LocationSettingType.UNKNOWN;
-        throw new ConfigurationException(GradleBundle.message("gradle.home.setting.type.explicit.empty", gradleHomePath));
-      }
-      else if (!myInstallationManager.isGradleSdkHome(new File(gradleHomePath))) {
-        myGradleHomeSettingType = LocationSettingType.EXPLICIT_INCORRECT;
-        new DelayedBalloonInfo(MessageType.ERROR, myGradleHomeSettingType, 0).run();
-        throw new ConfigurationException(GradleBundle.message("gradle.home.setting.type.explicit.incorrect", gradleHomePath));
-      }
-    }
-    return true;
+    return myBuilder.validate(settings);
   }
 
   @Override
   protected void applyExtraSettings(@NotNull GradleProjectSettings settings) {
-    String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
-    if (StringUtil.isEmpty(gradleHomePath)) {
-      settings.setGradleHome(null);
-    }
-    else {
-      settings.setGradleHome(gradleHomePath);
-      GradleUtil.storeLastUsedGradleHome(gradleHomePath);
-    }
-
-    final String gradleJvm = FileUtil.toCanonicalPath(myGradleJdkComboBox.getSelectedValue());
-    settings.setGradleJvm(StringUtil.isEmpty(gradleJvm) ? null : gradleJvm);
-
-    if (myUseLocalDistributionButton.isSelected()) {
-      settings.setDistributionType(DistributionType.LOCAL);
-    } else if(myUseWrapperButton.isSelected()) {
-      settings.setDistributionType(DistributionType.DEFAULT_WRAPPED);
-    } else if(myUseWrapperWithVerificationButton.isSelected() || myUseBundledDistributionButton.isSelected()) {
-      settings.setDistributionType(DistributionType.WRAPPED);
-    }
+    myBuilder.apply(settings);
   }
 
   @Override
   protected void updateInitialExtraSettings() {
-    String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
-    getInitialSettings().setGradleHome(StringUtil.isEmpty(gradleHomePath) ? null : gradleHomePath);
-    final String gradleJvm = FileUtil.toCanonicalPath(myGradleJdkComboBox.getSelectedValue());
-    getInitialSettings().setGradleJvm(StringUtil.isEmpty(gradleJvm) ? null : gradleJvm);
-    if (myUseLocalDistributionButton.isSelected()) {
-      getInitialSettings().setDistributionType(DistributionType.LOCAL);
-    } else if(myUseWrapperButton.isSelected()) {
-      getInitialSettings().setDistributionType(DistributionType.DEFAULT_WRAPPED);
-    } else if(myUseWrapperWithVerificationButton.isSelected() || myUseBundledDistributionButton.isSelected()) {
-      getInitialSettings().setDistributionType(DistributionType.WRAPPED);
-    }
+    myBuilder.apply(getInitialSettings());
   }
 
   @Override
   protected boolean isExtraSettingModified() {
-    DistributionType distributionType = getInitialSettings().getDistributionType();
-    if (myUseBundledDistributionButton.isSelected() && distributionType != DistributionType.BUNDLED) {
-      return true;
-    }
-
-    if (myUseWrapperButton.isSelected() && distributionType != DistributionType.DEFAULT_WRAPPED) {
-        return true;
-    }
-
-    if (myUseWrapperWithVerificationButton.isSelected() && distributionType != DistributionType.WRAPPED) {
-        return true;
-    }
-
-    if (myUseLocalDistributionButton.isSelected() && distributionType != DistributionType.LOCAL) {
-      return true;
-    }
-
-    if (!StringUtil.equals(myGradleJdkComboBox.getSelectedValue(), getInitialSettings().getGradleJvm())) {
-      return true;
-    }
-
-    String gradleHome = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
-    if (StringUtil.isEmpty(gradleHome)) {
-      return !StringUtil.isEmpty(getInitialSettings().getGradleHome());
-    }
-    else {
-      return !gradleHome.equals(getInitialSettings().getGradleHome());
-    }
+    return myBuilder.isModified(getInitialSettings());
   }
 
   @Override
   protected void resetExtraSettings(boolean isDefaultModuleCreation) {
-    String gradleHome = getInitialSettings().getGradleHome();
-    myGradleHomePathField.setText(gradleHome == null ? "" : gradleHome);
-    myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
-
-    resetGradleJdkComboBox(getProject());
-
-    updateWrapperControls(getInitialSettings().getExternalProjectPath(), isDefaultModuleCreation);
-    if (!myUseLocalDistributionButton.isSelected()) {
-      myGradleHomePathField.setEnabled(false);
-      return;
-    }
-
-    if (StringUtil.isEmpty(gradleHome)) {
-      myGradleHomeSettingType = LocationSettingType.UNKNOWN;
-      deduceGradleHomeIfPossible();
-    }
-    else {
-      myGradleHomeSettingType = myInstallationManager.isGradleSdkHome(new File(gradleHome)) ?
-                                LocationSettingType.EXPLICIT_CORRECT :
-                                LocationSettingType.EXPLICIT_INCORRECT;
-      myAlarm.cancelAllRequests();
-      if (myGradleHomeSettingType == LocationSettingType.EXPLICIT_INCORRECT &&
-          getInitialSettings().getDistributionType() == DistributionType.LOCAL) {
-        new DelayedBalloonInfo(MessageType.ERROR, myGradleHomeSettingType, 0).run();
-      }
-    }
-  }
-
-  public void updateWrapperControls(@Nullable String linkedProjectPath, boolean isDefaultModuleCreation) {
-    if(StringUtil.isEmpty(linkedProjectPath) && !isDefaultModuleCreation) {
-        myUseLocalDistributionButton.setSelected(true);
-        myGradleHomePathField.setEnabled(true);
-        return;
-    }
-
-    final boolean isGradleDefaultWrapperFilesExist = GradleUtil.isGradleDefaultWrapperFilesExist(linkedProjectPath);
-    if (isGradleDefaultWrapperFilesExist || isDefaultModuleCreation) {
-      myUseWrapperButton.setEnabled(true);
-      myUseWrapperButton.setSelected(true);
-      myGradleHomePathField.setEnabled(false);
-      myUseWrapperButton.setText(GradleBundle.message("gradle.settings.text.use.default_wrapper.configured"));
-    } else {
-      myUseWrapperButton.setEnabled(false);
-      myUseLocalDistributionButton.setSelected(true);
-      myGradleHomePathField.setEnabled(true);
-      myUseWrapperButton.setText(GradleBundle.message("gradle.settings.text.use.default_wrapper.not_configured"));
-    }
-
-    if(getInitialSettings().getDistributionType() == null) {
-      return;
-    }
-
-    switch (getInitialSettings().getDistributionType()) {
-      case LOCAL:
-        myGradleHomePathField.setEnabled(true);
-        myUseLocalDistributionButton.setSelected(true);
-        break;
-      case DEFAULT_WRAPPED:
-        myGradleHomePathField.setEnabled(false);
-        myUseWrapperButton.setSelected(true);
-        myUseWrapperButton.setEnabled(true);
-        break;
-      case WRAPPED:
-        myGradleHomePathField.setEnabled(false);
-        myUseWrapperWithVerificationButton.setSelected(true);
-        break;
-      case BUNDLED:
-        myGradleHomePathField.setEnabled(false);
-        myUseBundledDistributionButton.setSelected(true);
-        break;
-    }
+    myBuilder.reset(getProject(), getInitialSettings(), isDefaultModuleCreation);
   }
 
-  /**
-   * Updates GUI of the gradle configurable in order to show deduced path to gradle (if possible).
-   */
-  private void deduceGradleHomeIfPossible() {
-    File gradleHome = myInstallationManager.getAutodetectedGradleHome();
-    if (gradleHome == null) {
-      new DelayedBalloonInfo(MessageType.WARNING, LocationSettingType.UNKNOWN, BALLOON_DELAY_MILLIS).run();
-      return;
-    }
-    myGradleHomeSettingType = LocationSettingType.DEDUCED;
-    new DelayedBalloonInfo(MessageType.INFO, LocationSettingType.DEDUCED, BALLOON_DELAY_MILLIS).run();
-    myGradleHomePathField.setText(gradleHome.getPath());
-    myGradleHomePathField.getTextField().setForeground(LocationSettingType.DEDUCED.getColor());
-  }
-  
-  void showBalloonIfNecessary() {
-    if (!myShowBalloonIfNecessary || !myGradleHomePathField.isEnabled()) {
-      return;
-    }
-    myShowBalloonIfNecessary = false;
-    MessageType messageType = null;
-    switch (myGradleHomeSettingType) {
-      case DEDUCED:
-        messageType = MessageType.INFO;
-        break;
-      case EXPLICIT_INCORRECT:
-      case UNKNOWN:
-        messageType = MessageType.ERROR;
-        break;
-      default:
-    }
-    if (messageType != null) {
-      new DelayedBalloonInfo(messageType, myGradleHomeSettingType, BALLOON_DELAY_MILLIS).run();
-    }
+  public void update(@Nullable String linkedProjectPath, boolean isDefaultModuleCreation) {
+    myBuilder.update(linkedProjectPath, getInitialSettings(), isDefaultModuleCreation);
   }
 
   /**
@@ -421,47 +80,12 @@ public class GradleProjectSettingsControl extends AbstractExternalProjectSetting
    */
   public void setCurrentProject(@Nullable Project project) {
     super.setCurrentProject(project);
-    resetGradleJdkComboBox(project);
+    myBuilder.reset(getProject(), getInitialSettings(), false);
   }
 
-  private void resetGradleJdkComboBox(@Nullable final Project project) {
-    final String gradleJvm = getInitialSettings().getGradleJvm();
-    myGradleJdkComboBox.setProject(project);
-
-    final String sdkItem = ObjectUtils.nullizeByCondition(gradleJvm, new Condition<String>() {
-      @Override
-      public boolean value(String s) {
-        return (project == null && StringUtil.equals(USE_PROJECT_JDK, s)) || StringUtil.isEmpty(s);
-      }
-    });
-
-    myGradleJdkComboBox.refreshData(sdkItem);
-  }
-
-  private class DelayedBalloonInfo implements Runnable {
-    private final MessageType myMessageType;
-    private final String      myText;
-    private final long        myTriggerTime;
-
-    DelayedBalloonInfo(@NotNull MessageType messageType, @NotNull LocationSettingType settingType, long delayMillis) {
-      myMessageType = messageType;
-      myText = settingType.getDescription(GradleConstants.SYSTEM_ID);
-      myTriggerTime = System.currentTimeMillis() + delayMillis;
-    }
-
-    @Override
-    public void run() {
-      long diff = myTriggerTime - System.currentTimeMillis();
-      if (diff > 0) {
-        myAlarm.cancelAllRequests();
-        myAlarm.addRequest(this, diff);
-        return;
-      }
-      if (myGradleHomePathField == null || !myGradleHomePathField.isShowing()) {
-        // Don't schedule the balloon if the configurable is hidden.
-        return;
-      }
-      ExternalSystemUiUtil.showBalloon(myGradleHomePathField, myMessageType, myText);
-    }
+  @Override
+  public void disposeUIResources() {
+    super.disposeUIResources();
+    myBuilder.disposeUIResources();
   }
 }
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControlBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControlBuilder.java
new file mode 100644 (file)
index 0000000..cde018b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.plugins.gradle.service.settings;
+
+import com.intellij.openapi.externalSystem.service.settings.ExternalSystemSettingsControlCustomizer;
+import com.intellij.openapi.externalSystem.util.PaintAwarePanel;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 2/24/2015
+ */
+public interface GradleProjectSettingsControlBuilder {
+
+  GradleProjectSettings getInitialSettings();
+
+  IdeaGradleProjectSettingsControlBuilder addGradleHomeComponents(PaintAwarePanel content, int indentLevel);
+
+  IdeaGradleProjectSettingsControlBuilder addGradleJdkComponents(PaintAwarePanel content, int indentLevel);
+
+  IdeaGradleProjectSettingsControlBuilder addGradleChooserComponents(PaintAwarePanel content, int indentLevel);
+
+  void disposeUIResources();
+
+  boolean validate(GradleProjectSettings settings) throws ConfigurationException;
+
+  void apply(GradleProjectSettings settings);
+
+  boolean isModified(GradleProjectSettings settings);
+
+  void reset(Project project, GradleProjectSettings settings, boolean isDefaultModuleCreation);
+
+  void createAndFillControls(PaintAwarePanel content, int indentLevel);
+
+  void update(String linkedProjectPath, GradleProjectSettings settings, boolean isDefaultModuleCreation);
+
+  @Nullable
+  ExternalSystemSettingsControlCustomizer getExternalSystemSettingsControlCustomizer();
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSettingsControlProvider.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSettingsControlProvider.java
new file mode 100644 (file)
index 0000000..ce9cc90
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.plugins.gradle.service.settings;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.PlatformUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 2/24/2015
+ */
+public abstract class GradleSettingsControlProvider {
+
+  private static final ExtensionPointName<GradleSettingsControlProvider> EP_NAME =
+    ExtensionPointName.create("org.jetbrains.plugins.gradle.settingsControlProvider");
+
+  public abstract String getPlatformPrefix();
+
+  public abstract GradleProjectSettingsControlBuilder getProjectSettingsControlBuilder(@NotNull GradleProjectSettings initialSettings);
+
+  @NotNull
+  public static GradleSettingsControlProvider get() {
+    GradleSettingsControlProvider result = null;
+    if (!PlatformUtils.isIntelliJ()) {
+      final String platformPrefix = PlatformUtils.getPlatformPrefix();
+      for (GradleSettingsControlProvider provider : EP_NAME.getExtensions()) {
+        if (StringUtil.equals(platformPrefix, provider.getPlatformPrefix())) {
+          assert result == null : "Multiple GradleSettingsControlProvider extensions found";
+          result = provider;
+        }
+      }
+    }
+    return ObjectUtils.notNull(result, new GradleSettingsControlProvider() {
+      @Override
+      public String getPlatformPrefix() {
+        return null;
+      }
+
+      @Override
+      public GradleProjectSettingsControlBuilder getProjectSettingsControlBuilder(@NotNull GradleProjectSettings initialSettings) {
+        return new IdeaGradleProjectSettingsControlBuilder(initialSettings)
+          // Hide bundled distribution option for a while
+          .dropUseBundledDistributionButton();
+      }
+    });
+  }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/IdeaGradleProjectSettingsControlBuilder.java
new file mode 100644 (file)
index 0000000..891ba0f
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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 org.jetbrains.plugins.gradle.service.settings;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.externalSystem.model.settings.LocationSettingType;
+import com.intellij.openapi.externalSystem.service.settings.ExternalSystemSettingsControlCustomizer;
+import com.intellij.openapi.externalSystem.service.ui.ExternalSystemJdkComboBox;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
+import com.intellij.openapi.externalSystem.util.PaintAwarePanel;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.TextComponentAccessor;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBRadioButton;
+import com.intellij.util.Alarm;
+import com.intellij.util.Consumer;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.ui.UIUtil;
+import org.gradle.util.GradleVersion;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.service.GradleInstallationManager;
+import org.jetbrains.plugins.gradle.settings.DistributionType;
+import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
+import org.jetbrains.plugins.gradle.util.GradleBundle;
+import org.jetbrains.plugins.gradle.util.GradleConstants;
+import org.jetbrains.plugins.gradle.util.GradleUtil;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import static com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.USE_PROJECT_JDK;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 2/24/2015
+ */
+public class IdeaGradleProjectSettingsControlBuilder implements GradleProjectSettingsControlBuilder {
+
+  private static final long BALLOON_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
+  @NotNull
+  private final GradleInstallationManager myInstallationManager;
+  @NotNull
+  private final GradleProjectSettings myInitialSettings;
+  @NotNull
+  private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
+  @NotNull
+  private LocationSettingType myGradleHomeSettingType = LocationSettingType.UNKNOWN;
+  private boolean myShowBalloonIfNecessary;
+  private ActionListener myActionListener;
+
+
+  private boolean dropUseAutoImportBox;
+  private boolean dropCreateEmptyContentRootDirectoriesBox;
+
+  @SuppressWarnings("FieldCanBeLocal") // Used implicitly by reflection at disposeUIResources() and showUi()
+  @Nullable
+  private JLabel myGradleHomeLabel;
+  @Nullable
+  private TextFieldWithBrowseButton myGradleHomePathField;
+  private boolean dropGradleHomePathComponents;
+
+  @SuppressWarnings("FieldCanBeLocal") // Used implicitly by reflection at disposeUIResources() and showUi()
+  @Nullable
+  private JLabel myGradleJdkLabel;
+  @Nullable
+  private ExternalSystemJdkComboBox myGradleJdkComboBox;
+  private boolean dropGradleJdkComponents;
+
+  @Nullable
+  private JBRadioButton myUseWrapperButton;
+  private boolean dropUseWrapperButton;
+
+  @Nullable
+  private JBRadioButton myUseWrapperWithVerificationButton;
+  @SuppressWarnings("FieldCanBeLocal") // Used implicitly by reflection at disposeUIResources() and showUi()
+  @Nullable
+  private JBLabel myUseWrapperVerificationLabel;
+  private boolean dropCustomizableWrapperButton;
+
+  @Nullable
+  private JBRadioButton myUseLocalDistributionButton;
+  private boolean dropUseLocalDistributionButton;
+
+  @Nullable
+  private JBRadioButton myUseBundledDistributionButton;
+  private boolean dropUseBundledDistributionButton;
+
+  public IdeaGradleProjectSettingsControlBuilder(@NotNull GradleProjectSettings initialSettings) {
+    myInstallationManager = ServiceManager.getService(GradleInstallationManager.class);
+    myInitialSettings = initialSettings;
+
+    myActionListener = new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        if (myGradleHomePathField == null) return;
+
+        boolean localDistributionEnabled = myUseLocalDistributionButton != null && myUseLocalDistributionButton.isSelected();
+        myGradleHomePathField.setEnabled(localDistributionEnabled);
+        if (localDistributionEnabled) {
+          if (myGradleHomePathField.getText().isEmpty()) {
+            deduceGradleHomeIfPossible();
+          }
+          else {
+            if (myInstallationManager.isGradleSdkHome(myGradleHomePathField.getText())) {
+              myGradleHomeSettingType = LocationSettingType.EXPLICIT_CORRECT;
+            }
+            else {
+              myGradleHomeSettingType = LocationSettingType.EXPLICIT_INCORRECT;
+              myShowBalloonIfNecessary = true;
+            }
+          }
+          showBalloonIfNecessary();
+        }
+        else {
+          myAlarm.cancelAllRequests();
+        }
+      }
+    };
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropGradleJdkComponents() {
+    dropGradleJdkComponents = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropUseWrapperButton() {
+    dropUseWrapperButton = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropGradleHomePathComponents() {
+    dropGradleHomePathComponents = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropCustomizableWrapperButton() {
+    dropCustomizableWrapperButton = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropUseLocalDistributionButton() {
+    dropUseLocalDistributionButton = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropUseBundledDistributionButton() {
+    dropUseBundledDistributionButton = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropUseAutoImportBox() {
+    dropUseAutoImportBox = true;
+    return this;
+  }
+
+  public IdeaGradleProjectSettingsControlBuilder dropCreateEmptyContentRootDirectoriesBox() {
+    dropCreateEmptyContentRootDirectoriesBox = true;
+    return this;
+  }
+
+  @NotNull
+  public GradleProjectSettings getInitialSettings() {
+    return myInitialSettings;
+  }
+
+  @Override
+  public ExternalSystemSettingsControlCustomizer getExternalSystemSettingsControlCustomizer() {
+    return new ExternalSystemSettingsControlCustomizer(dropUseAutoImportBox, dropCreateEmptyContentRootDirectoriesBox);
+  }
+
+  @Override
+  public void createAndFillControls(PaintAwarePanel content, int indentLevel) {
+    content.setPaintCallback(new Consumer<Graphics>() {
+      @Override
+      public void consume(Graphics graphics) {
+        showBalloonIfNecessary();
+      }
+    });
+
+    content.addPropertyChangeListener(new PropertyChangeListener() {
+      @Override
+      public void propertyChange(PropertyChangeEvent evt) {
+        if (!"ancestor".equals(evt.getPropertyName())) {
+          return;
+        }
+
+        // Configure the balloon to show on initial configurable drawing.
+        myShowBalloonIfNecessary = evt.getNewValue() != null && evt.getOldValue() == null;
+
+        if (evt.getNewValue() == null && evt.getOldValue() != null) {
+          // Cancel delayed balloons when the configurable is hidden.
+          myAlarm.cancelAllRequests();
+        }
+      }
+    });
+
+    addGradleChooserComponents(content, indentLevel);
+    addGradleHomeComponents(content, indentLevel);
+    addGradleJdkComponents(content, indentLevel);
+  }
+
+  @Override
+  public void disposeUIResources() {
+    ExternalSystemUiUtil.disposeUi(this);
+  }
+
+  /**
+   * Updates GUI of the gradle configurable in order to show deduced path to gradle (if possible).
+   */
+  private void deduceGradleHomeIfPossible() {
+    if (myGradleHomePathField == null) return;
+
+    File gradleHome = myInstallationManager.getAutodetectedGradleHome();
+    if (gradleHome == null) {
+      new DelayedBalloonInfo(MessageType.WARNING, LocationSettingType.UNKNOWN, BALLOON_DELAY_MILLIS).run();
+      return;
+    }
+    myGradleHomeSettingType = LocationSettingType.DEDUCED;
+    new DelayedBalloonInfo(MessageType.INFO, LocationSettingType.DEDUCED, BALLOON_DELAY_MILLIS).run();
+    myGradleHomePathField.setText(gradleHome.getPath());
+    myGradleHomePathField.getTextField().setForeground(LocationSettingType.DEDUCED.getColor());
+  }
+
+  @Override
+  public IdeaGradleProjectSettingsControlBuilder addGradleJdkComponents(PaintAwarePanel content, int indentLevel) {
+    if(!dropGradleJdkComponents) {
+      myGradleJdkLabel = new JBLabel(GradleBundle.message("gradle.settings.text.jvm.path"));
+      myGradleJdkComboBox = new ExternalSystemJdkComboBox().withoutJre();
+
+      content.add(myGradleJdkLabel, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
+      content.add(myGradleJdkComboBox, ExternalSystemUiUtil.getFillLineConstraints(0));
+    }
+    return this;
+  }
+
+  @Override
+  public IdeaGradleProjectSettingsControlBuilder addGradleChooserComponents(PaintAwarePanel content, int indentLevel) {
+    ButtonGroup buttonGroup = new ButtonGroup();
+
+    if(!dropUseWrapperButton) {
+      myUseWrapperButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.default_wrapper.configured"));
+      myUseWrapperButton.addActionListener(myActionListener);
+      buttonGroup.add(myUseWrapperButton);
+      content.add(myUseWrapperButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
+
+    if(!dropCustomizableWrapperButton) {
+      myUseWrapperWithVerificationButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.customizable_wrapper"));
+      myUseWrapperWithVerificationButton.addActionListener(myActionListener);
+      myUseWrapperVerificationLabel = new JBLabel(GradleBundle.message("gradle.settings.text.wrapper.customization.compatibility"));
+      myUseWrapperVerificationLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.MINI));
+      myUseWrapperVerificationLabel.setIcon(UIUtil.getBalloonInformationIcon());
+      buttonGroup.add(myUseWrapperWithVerificationButton);
+      content.add(myUseWrapperWithVerificationButton, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
+      content.add(myUseWrapperVerificationLabel, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
+
+    if(!dropUseLocalDistributionButton) {
+      myUseLocalDistributionButton = new JBRadioButton(GradleBundle.message("gradle.settings.text.use.local.distribution"));
+      myUseLocalDistributionButton.addActionListener(myActionListener);
+      buttonGroup.add(myUseLocalDistributionButton);
+      content.add(myUseLocalDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
+
+    if(!dropUseBundledDistributionButton) {
+      myUseBundledDistributionButton = new JBRadioButton(
+        GradleBundle.message("gradle.settings.text.use.bundled.distribution", GradleVersion.current().getVersion()));
+      myUseBundledDistributionButton.addActionListener(myActionListener);
+      buttonGroup.add(myUseBundledDistributionButton);
+      //content.add(Box.createGlue(), ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+      content.add(myUseBundledDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel));
+    }
+
+    return this;
+  }
+
+  @Override
+  public boolean validate(GradleProjectSettings settings) throws ConfigurationException {
+    if (myGradleHomePathField == null) return true;
+
+    String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
+    if (myUseLocalDistributionButton != null && myUseLocalDistributionButton.isSelected()) {
+      if (StringUtil.isEmpty(gradleHomePath)) {
+        myGradleHomeSettingType = LocationSettingType.UNKNOWN;
+        throw new ConfigurationException(GradleBundle.message("gradle.home.setting.type.explicit.empty", gradleHomePath));
+      }
+      else if (!myInstallationManager.isGradleSdkHome(new File(gradleHomePath))) {
+        myGradleHomeSettingType = LocationSettingType.EXPLICIT_INCORRECT;
+        new DelayedBalloonInfo(MessageType.ERROR, myGradleHomeSettingType, 0).run();
+        throw new ConfigurationException(GradleBundle.message("gradle.home.setting.type.explicit.incorrect", gradleHomePath));
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public void apply(GradleProjectSettings settings) {
+    if (myGradleHomePathField != null) {
+      String gradleHomePath = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
+      if (StringUtil.isEmpty(gradleHomePath)) {
+        settings.setGradleHome(null);
+      }
+      else {
+        settings.setGradleHome(gradleHomePath);
+        GradleUtil.storeLastUsedGradleHome(gradleHomePath);
+      }
+    }
+
+    if (myGradleJdkComboBox != null) {
+      final String gradleJvm = FileUtil.toCanonicalPath(myGradleJdkComboBox.getSelectedValue());
+      settings.setGradleJvm(StringUtil.isEmpty(gradleJvm) ? null : gradleJvm);
+    }
+
+    if (myUseLocalDistributionButton != null && myUseLocalDistributionButton.isSelected()) {
+      settings.setDistributionType(DistributionType.LOCAL);
+    }
+    else if (myUseWrapperButton != null && myUseWrapperButton.isSelected()) {
+      settings.setDistributionType(DistributionType.DEFAULT_WRAPPED);
+    }
+    else if ((myUseWrapperWithVerificationButton != null && myUseWrapperWithVerificationButton.isSelected()) ||
+             (myUseBundledDistributionButton != null && myUseBundledDistributionButton.isSelected())) {
+      settings.setDistributionType(DistributionType.WRAPPED);
+    }
+  }
+
+  @Override
+  public boolean isModified(GradleProjectSettings settings) {
+    DistributionType distributionType = settings.getDistributionType();
+    if (myUseBundledDistributionButton != null &&
+        myUseBundledDistributionButton.isSelected() &&
+        distributionType != DistributionType.BUNDLED) {
+      return true;
+    }
+
+    if (myUseWrapperButton != null && myUseWrapperButton.isSelected() && distributionType != DistributionType.DEFAULT_WRAPPED) {
+      return true;
+    }
+
+    if (myUseWrapperWithVerificationButton != null &&
+        myUseWrapperWithVerificationButton.isSelected() &&
+        distributionType != DistributionType.WRAPPED) {
+      return true;
+    }
+
+    if (myUseLocalDistributionButton != null && myUseLocalDistributionButton.isSelected() && distributionType != DistributionType.LOCAL) {
+      return true;
+    }
+
+    if (myGradleJdkComboBox != null && !StringUtil.equals(myGradleJdkComboBox.getSelectedValue(), settings.getGradleJvm())) {
+      return true;
+    }
+
+    if (myGradleHomePathField == null) return false;
+    String gradleHome = FileUtil.toCanonicalPath(myGradleHomePathField.getText());
+    if (StringUtil.isEmpty(gradleHome)) {
+      return !StringUtil.isEmpty(settings.getGradleHome());
+    }
+    else {
+      return !gradleHome.equals(settings.getGradleHome());
+    }
+  }
+
+  @Override
+  public void reset(Project project, GradleProjectSettings settings, boolean isDefaultModuleCreation) {
+    String gradleHome = settings.getGradleHome();
+    if (myGradleHomePathField != null) {
+      myGradleHomePathField.setText(gradleHome == null ? "" : gradleHome);
+      myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
+    }
+
+    resetGradleJdkComboBox(project, settings);
+    resetWrapperControls(settings.getExternalProjectPath(), settings, isDefaultModuleCreation);
+
+    if (myUseLocalDistributionButton != null && !myUseLocalDistributionButton.isSelected()) {
+      myGradleHomePathField.setEnabled(false);
+      return;
+    }
+
+    if (StringUtil.isEmpty(gradleHome)) {
+      myGradleHomeSettingType = LocationSettingType.UNKNOWN;
+      deduceGradleHomeIfPossible();
+    }
+    else {
+      myGradleHomeSettingType = myInstallationManager.isGradleSdkHome(new File(gradleHome)) ?
+                                LocationSettingType.EXPLICIT_CORRECT :
+                                LocationSettingType.EXPLICIT_INCORRECT;
+      myAlarm.cancelAllRequests();
+      if (myGradleHomeSettingType == LocationSettingType.EXPLICIT_INCORRECT &&
+          settings.getDistributionType() == DistributionType.LOCAL) {
+        new DelayedBalloonInfo(MessageType.ERROR, myGradleHomeSettingType, 0).run();
+      }
+    }
+  }
+
+  @Override
+  public void update(String linkedProjectPath, GradleProjectSettings settings, boolean isDefaultModuleCreation) {
+    resetWrapperControls(linkedProjectPath, settings, isDefaultModuleCreation);
+  }
+
+  @Override
+  public IdeaGradleProjectSettingsControlBuilder addGradleHomeComponents(PaintAwarePanel content, int indentLevel) {
+    if(dropGradleHomePathComponents) return this;
+
+    myGradleHomeLabel = new JBLabel(GradleBundle.message("gradle.settings.text.home.path"));
+    myGradleHomePathField = new TextFieldWithBrowseButton();
+
+    myGradleHomePathField.addBrowseFolderListener(
+      "",
+      GradleBundle.message("gradle.settings.text.home.path"),
+      null,
+      GradleUtil.getGradleHomeFileChooserDescriptor(),
+      TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT,
+      false
+    );
+    myGradleHomePathField.getTextField().getDocument().addDocumentListener(new DocumentListener() {
+      @Override
+      public void insertUpdate(DocumentEvent e) {
+        myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e) {
+        myGradleHomePathField.getTextField().setForeground(LocationSettingType.EXPLICIT_CORRECT.getColor());
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e) {
+      }
+    });
+
+    content.add(myGradleHomeLabel, ExternalSystemUiUtil.getLabelConstraints(indentLevel));
+    content.add(myGradleHomePathField, ExternalSystemUiUtil.getFillLineConstraints(0));
+
+    return this;
+  }
+
+  private void resetGradleJdkComboBox(@Nullable final Project project, GradleProjectSettings settings) {
+    if (myGradleJdkComboBox == null) return;
+
+    final String gradleJvm = settings.getGradleJvm();
+    myGradleJdkComboBox.setProject(project);
+
+    final String sdkItem = ObjectUtils.nullizeByCondition(gradleJvm, new Condition<String>() {
+      @Override
+      public boolean value(String s) {
+        return (project == null && StringUtil.equals(USE_PROJECT_JDK, s)) || StringUtil.isEmpty(s);
+      }
+    });
+
+    myGradleJdkComboBox.refreshData(sdkItem);
+  }
+
+  private void resetWrapperControls(String linkedProjectPath, @NotNull GradleProjectSettings settings, boolean isDefaultModuleCreation) {
+    if (StringUtil.isEmpty(linkedProjectPath) && !isDefaultModuleCreation) {
+      if (myUseLocalDistributionButton != null) {
+        myUseLocalDistributionButton.setSelected(true);
+      }
+      if (myGradleHomePathField != null) {
+        myGradleHomePathField.setEnabled(true);
+      }
+      return;
+    }
+
+    final boolean isGradleDefaultWrapperFilesExist = GradleUtil.isGradleDefaultWrapperFilesExist(linkedProjectPath);
+    if (myUseWrapperButton != null && (isGradleDefaultWrapperFilesExist || isDefaultModuleCreation)) {
+      myUseWrapperButton.setEnabled(true);
+      myUseWrapperButton.setSelected(true);
+      if (myGradleHomePathField != null) {
+        myGradleHomePathField.setEnabled(false);
+      }
+      myUseWrapperButton.setText(GradleBundle.message("gradle.settings.text.use.default_wrapper.configured"));
+    }
+    else {
+      if (myUseWrapperButton != null) {
+        myUseWrapperButton.setEnabled(false);
+        myUseWrapperButton.setText(GradleBundle.message("gradle.settings.text.use.default_wrapper.not_configured"));
+      }
+      if (myUseLocalDistributionButton != null) {
+        myUseLocalDistributionButton.setSelected(true);
+      }
+      if (myGradleHomePathField != null) {
+        myGradleHomePathField.setEnabled(true);
+      }
+    }
+
+    if (settings.getDistributionType() == null) {
+      return;
+    }
+
+    switch (settings.getDistributionType()) {
+      case LOCAL:
+        if (myGradleHomePathField != null) {
+          myGradleHomePathField.setEnabled(true);
+        }
+        if (myUseLocalDistributionButton != null) {
+          myUseLocalDistributionButton.setSelected(true);
+        }
+        break;
+      case DEFAULT_WRAPPED:
+        if (isGradleDefaultWrapperFilesExist) {
+          if (myGradleHomePathField != null) {
+            myGradleHomePathField.setEnabled(false);
+          }
+          if (myUseWrapperButton != null) {
+            myUseWrapperButton.setSelected(true);
+            myUseWrapperButton.setEnabled(true);
+          }
+        }
+        break;
+      case WRAPPED:
+        if (myGradleHomePathField != null) {
+          myGradleHomePathField.setEnabled(false);
+        }
+        if (myUseWrapperWithVerificationButton != null) {
+          myUseWrapperWithVerificationButton.setSelected(true);
+        }
+        break;
+      case BUNDLED:
+        if (myGradleHomePathField != null) {
+          myGradleHomePathField.setEnabled(false);
+        }
+        if (myUseBundledDistributionButton != null) {
+          myUseBundledDistributionButton.setSelected(true);
+        }
+        break;
+    }
+  }
+
+  void showBalloonIfNecessary() {
+    if (!myShowBalloonIfNecessary || (myGradleHomePathField != null && !myGradleHomePathField.isEnabled())) {
+      return;
+    }
+    myShowBalloonIfNecessary = false;
+    MessageType messageType = null;
+    switch (myGradleHomeSettingType) {
+      case DEDUCED:
+        messageType = MessageType.INFO;
+        break;
+      case EXPLICIT_INCORRECT:
+      case UNKNOWN:
+        messageType = MessageType.ERROR;
+        break;
+      default:
+    }
+    if (messageType != null) {
+      new DelayedBalloonInfo(messageType, myGradleHomeSettingType, BALLOON_DELAY_MILLIS).run();
+    }
+  }
+
+  private class DelayedBalloonInfo implements Runnable {
+    private final MessageType myMessageType;
+    private final String myText;
+    private final long myTriggerTime;
+
+    DelayedBalloonInfo(@NotNull MessageType messageType, @NotNull LocationSettingType settingType, long delayMillis) {
+      myMessageType = messageType;
+      myText = settingType.getDescription(GradleConstants.SYSTEM_ID);
+      myTriggerTime = System.currentTimeMillis() + delayMillis;
+    }
+
+    @Override
+    public void run() {
+      long diff = myTriggerTime - System.currentTimeMillis();
+      if (diff > 0) {
+        myAlarm.cancelAllRequests();
+        myAlarm.addRequest(this, diff);
+        return;
+      }
+      if (myGradleHomePathField == null || !myGradleHomePathField.isShowing()) {
+        // Don't schedule the balloon if the configurable is hidden.
+        return;
+      }
+      ExternalSystemUiUtil.showBalloon(myGradleHomePathField, myMessageType, myText);
+    }
+  }
+}
index 605cd3667fe3350a7d59ef73d2ed392b5959a245..7bfad346e35337b76286e7f001bbccd0a7d00122 100644 (file)
@@ -52,9 +52,7 @@ public class ImportFromGradleControl
   @NotNull
   @Override
   protected ExternalSystemSettingsControl<GradleProjectSettings> createProjectSettingsControl(@NotNull GradleProjectSettings settings) {
-    GradleProjectSettingsControl settingsControl = new GradleProjectSettingsControl(settings);
-    settingsControl.hideUseAutoImportBox();
-    return settingsControl;
+    return new GradleProjectSettingsControl(settings);
   }
 
   @Nullable
@@ -65,7 +63,7 @@ public class ImportFromGradleControl
 
   @Override
   protected void onLinkedProjectPathChange(@NotNull String path) {
-    ((GradleProjectSettingsControl)getProjectSettingsControl()).updateWrapperControls(path, false);
+    ((GradleProjectSettingsControl)getProjectSettingsControl()).update(path, false);
   }
 
   @Override