handle inter-step dependencies in initial configuration wizard (IDEA-50225)
authorDmitry Jemerov <yole@jetbrains.com>
Fri, 20 Aug 2010 19:53:10 +0000 (23:53 +0400)
committerDmitry Jemerov <yole@jetbrains.com>
Fri, 20 Aug 2010 19:53:10 +0000 (23:53 +0400)
platform/platform-impl/src/com/intellij/ide/startupWizard/SelectPluginsStep.java
platform/platform-impl/src/com/intellij/ide/startupWizard/StartupWizardModel.java
platform/platform-impl/src/com/intellij/ui/wizard/WizardModel.java

index a2a15c7415fc5547fdf20f406af70066575ba190..6d51c89dc38b2ae245fa19fd3e0883b2ddc0b5f3 100644 (file)
@@ -17,10 +17,11 @@ package com.intellij.ide.startupWizard;
 
 import com.intellij.ide.plugins.IdeaPluginDescriptor;
 import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.CollectionListModel;
 import com.intellij.ui.wizard.WizardNavigationState;
 import com.intellij.ui.wizard.WizardStep;
-import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.Nullable;
 
@@ -29,7 +30,9 @@ import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import java.awt.*;
 import java.awt.event.*;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -42,15 +45,15 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
   private JButton myEnableAllButton;
   private JButton myDisableAllButton;
   private final List<IdeaPluginDescriptor> myPlugins = new ArrayList<IdeaPluginDescriptor>();
-  private final Set<String> myDisabledPluginIds;
+  private final StartupWizardModel myModel;
   private final String myRequirePlugin;
 
   private static final String[] ourSuffixes = new String[] { "integration", "support", "plugin" };
 
-  public SelectPluginsStep(final String title, final Set<String> disabledPluginIds, final String requirePlugin) {
+  public SelectPluginsStep(final String title, final StartupWizardModel model, final String requirePlugin) {
     super(title, "Select the plugins to enable. Disabling unused plugins will improve IDE startup speed and performance.\n\nTo change plugin settings later, go to Settings | Plugins.",
           null);
-    myDisabledPluginIds = disabledPluginIds;
+    myModel = model;
     myRequirePlugin = requirePlugin;
     myPluginsList.setCellRenderer(new ListCellRenderer() {
       private final JCheckBox myCheckbox = new JCheckBox();
@@ -60,6 +63,8 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
                                                     final int index,
                                                     final boolean isSelected,
                                                     final boolean cellHasFocus) {
+        IdeaPluginDescriptor descriptor = (IdeaPluginDescriptor)value;
+        myCheckbox.setEnabled(!myModel.isForceEnable(descriptor));
         if (isSelected) {
           myCheckbox.setBackground(UIUtil.getListSelectionBackground());
           myCheckbox.setForeground(UIUtil.getListSelectionForeground());
@@ -68,9 +73,8 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
           myCheckbox.setBackground(UIUtil.getListBackground());
           myCheckbox.setForeground(UIUtil.getListForeground());
         }
-        IdeaPluginDescriptor descriptor = (IdeaPluginDescriptor)value;
         myCheckbox.setText(getAbbreviatedName(descriptor) + buildRequires(descriptor));
-        myCheckbox.setSelected(!isDisabledPlugin(descriptor));
+        myCheckbox.setSelected(!myModel.isDisabledPlugin(descriptor));
         return myCheckbox;
       }
     });
@@ -117,7 +121,7 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
 
   private String buildRequires(final IdeaPluginDescriptor descriptor) {
     StringBuffer requiresBuffer = new StringBuffer();
-    for (PluginId id : getNonOptionalDependencies(descriptor)) {
+    for (PluginId id : StartupWizardModel.getNonOptionalDependencies(descriptor)) {
       final IdeaPluginDescriptor dependent = findPlugin(id);
       if (dependent != null) {
         String name = getAbbreviatedName(dependent);
@@ -130,23 +134,28 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
         requiresBuffer.append(name);
       }
     }
+    List<IdeaPluginDescriptor> requiredBy = myModel.getDependentsOnEarlierPages(descriptor, false);
+    if (requiredBy.size() > 0) {
+      if (requiresBuffer.length() > 0) {
+        requiresBuffer.append(", ");
+      }
+      else {
+        requiresBuffer.append(" (");
+      }
+      requiresBuffer.append("required by ");
+      requiresBuffer.append(StringUtil.join(requiredBy, new Function<IdeaPluginDescriptor, String>() {
+        @Override
+        public String fun(IdeaPluginDescriptor ideaPluginDescriptor) {
+          return getAbbreviatedName(ideaPluginDescriptor);
+        }
+      }, ", "));
+    }
     if (requiresBuffer.length() > 0) {
       requiresBuffer.append(")");
     }
     return requiresBuffer.toString();
   }
 
-  private static List<PluginId> getNonOptionalDependencies(final IdeaPluginDescriptor descriptor) {
-    List<PluginId> result = new ArrayList<PluginId>();
-    for (PluginId pluginId : descriptor.getDependentPluginIds()) {
-      if (pluginId.getIdString().equals("com.intellij")) continue;
-      if (!ArrayUtil.contains(pluginId, descriptor.getOptionalDependentPluginIds())) {
-        result.add(pluginId);
-      }
-    }
-    return result;
-  }
-
   private static String getAbbreviatedName(final IdeaPluginDescriptor descriptor) {
     final String name = descriptor.getName();
     for (String suffix : ourSuffixes) {
@@ -157,52 +166,31 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
     return name;
   }
 
-  private boolean isDisabledPlugin(final IdeaPluginDescriptor descriptor) {
-    return myDisabledPluginIds.contains(descriptor.getPluginId().toString());
-  }
-
   private void toggleSelection() {
     final IdeaPluginDescriptor descriptor = getSelectedPlugin();
-    if (descriptor == null) return;
-    boolean willDisable = !isDisabledPlugin(descriptor);
+    if (descriptor == null || myModel.isForceEnable(descriptor)) return;
+    boolean willDisable = !myModel.isDisabledPlugin(descriptor);
     final Object[] selection = myPluginsList.getSelectedValues();
     for (Object o : selection) {
       IdeaPluginDescriptor desc = (IdeaPluginDescriptor) o;
       if (!willDisable) {
-        setPluginEnabledWithDependencies(desc);
+        myModel.setPluginEnabledWithDependencies(desc);
       }
       else {
-        setPluginDisabledWithDependents(desc);
+        myModel.setPluginDisabledWithDependents(desc);
       }
     }
     myPluginsList.repaint();
   }
 
-  private void setPluginDisabledWithDependents(final IdeaPluginDescriptor desc) {
-    setPluginEnabled(desc, false);
-    for (IdeaPluginDescriptor plugin : myPlugins) {
-      if (ArrayUtil.contains(desc.getPluginId(), plugin.getDependentPluginIds()) &&
-          !ArrayUtil.contains(desc.getPluginId(), plugin.getOptionalDependentPluginIds())) {
-        setPluginDisabledWithDependents(plugin);
-      }
-    }
-  }
-
   private void setAllPluginsEnabled(boolean value) {
     for(IdeaPluginDescriptor descriptor: myPlugins) {
-      setPluginEnabled(descriptor, value);
-    }
-    myPluginsList.repaint();
-  }
-
-  private void setPluginEnabledWithDependencies(final IdeaPluginDescriptor desc) {
-    setPluginEnabled(desc, true);
-    for(PluginId id: getNonOptionalDependencies(desc)) {
-      final IdeaPluginDescriptor dependent = findPlugin(id);
-      if (dependent != null) {
-        setPluginEnabledWithDependencies(dependent);
+      if (!value && myModel.isForceEnable(descriptor)) {
+        continue;
       }
+      myModel.setPluginEnabled(descriptor, value);
     }
+    myPluginsList.repaint();
   }
 
   @Nullable
@@ -215,15 +203,6 @@ public class SelectPluginsStep extends WizardStep<StartupWizardModel> {
     return null;
   }
 
-  private void setPluginEnabled(final IdeaPluginDescriptor desc, boolean value) {
-    if (value) {
-      myDisabledPluginIds.remove(desc.getPluginId().toString());
-    }
-    else {
-      myDisabledPluginIds.add(desc.getPluginId().toString());
-    }
-  }
-
   @Nullable
   private IdeaPluginDescriptor getSelectedPlugin() {
     final int leadSelectionIndex = myPluginsList.getSelectionModel().getLeadSelectionIndex();
index a71da36b47507e2e4249ba587011ea4b92183592..10461f4ae62dfe796c7949283c1714d8e101f94a 100644 (file)
@@ -21,8 +21,12 @@ import com.intellij.ide.plugins.PluginManager;
 import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.application.ex.ApplicationInfoEx;
+import com.intellij.openapi.extensions.PluginId;
 import com.intellij.ui.wizard.WizardModel;
+import com.intellij.util.ArrayUtil;
 import com.intellij.util.containers.HashSet;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.util.*;
@@ -33,7 +37,11 @@ import java.util.*;
 public class StartupWizardModel extends WizardModel {
   private final Set<String> myDisabledPluginIds = new HashSet<String>();
   private final Map<String, SelectPluginsStep> myStepMap = new HashMap<String, SelectPluginsStep>();
+  private Map<PluginId, SelectPluginsStep> myPluginToStepMap = new HashMap<PluginId, SelectPluginsStep>();
+  private MultiMap<IdeaPluginDescriptor, IdeaPluginDescriptor> myBackwardDependencies =
+    new MultiMap<IdeaPluginDescriptor, IdeaPluginDescriptor>();
   private SelectPluginsStep myOtherStep;
+  private IdeaPluginDescriptor[] myAllPlugins;
 
   public StartupWizardModel(final List<ApplicationInfoEx.PluginChooserPage> pluginChooserPages) {
     super(ApplicationNamesInfo.getInstance().getFullProductName() + " Initial Configuration Wizard");
@@ -41,7 +49,7 @@ public class StartupWizardModel extends WizardModel {
 
     for (ApplicationInfoEx.PluginChooserPage page : pluginChooserPages) {
       if (page.getCategory() == null) {
-        myOtherStep = new SelectPluginsStep(page.getTitle(), myDisabledPluginIds, null);
+        myOtherStep = new SelectPluginsStep(page.getTitle(), this, null);
       }
       else {
         addSelectPluginsStep(page.getCategory(), page.getTitle(), page.getDependentPlugin());
@@ -51,19 +59,28 @@ public class StartupWizardModel extends WizardModel {
       add(myOtherStep);
     }
 
-    final IdeaPluginDescriptor[] pluginDescriptors = PluginManager.loadDescriptors();
-    for (IdeaPluginDescriptor pluginDescriptor : pluginDescriptors) {
+    myAllPlugins = PluginManager.loadDescriptors();
+    for (IdeaPluginDescriptor pluginDescriptor : myAllPlugins) {
       if (pluginDescriptor.getPluginId().getIdString().equals("com.intellij")) {
         // skip 'IDEA CORE' plugin
         continue;
       }
       PluginManager.initClassLoader(getClass().getClassLoader(), (IdeaPluginDescriptorImpl) pluginDescriptor);
       SelectPluginsStep step = myStepMap.get(pluginDescriptor.getCategory());
+      if (step == null) {
+        step = myOtherStep;
+      }
       if (step != null) {
         step.addPlugin(pluginDescriptor);
-      }
-      else if (myOtherStep != null) {
-        myOtherStep.addPlugin(pluginDescriptor);
+        myPluginToStepMap.put(pluginDescriptor.getPluginId(), step);
+        for (PluginId pluginId : pluginDescriptor.getDependentPluginIds()) {
+          if (!ArrayUtil.contains(pluginId, pluginDescriptor.getOptionalDependentPluginIds())) {
+            IdeaPluginDescriptor dependee = findPlugin(pluginId);
+            if (dependee != null) {
+              myBackwardDependencies.putValue(dependee, pluginDescriptor);
+            }
+          }
+        }
       }
     }
     for (SelectPluginsStep step : myStepMap.values()) {
@@ -72,8 +89,29 @@ public class StartupWizardModel extends WizardModel {
     myOtherStep.fillPlugins();
   }
 
+  @Nullable
+  private IdeaPluginDescriptor findPlugin(PluginId pluginId) {
+    for (IdeaPluginDescriptor pluginDescriptor : myAllPlugins) {
+      if (pluginDescriptor.getPluginId().equals(pluginId)) {
+        return pluginDescriptor;
+      }
+    }
+    return null;
+  }
+
+  static List<PluginId> getNonOptionalDependencies(final IdeaPluginDescriptor descriptor) {
+    List<PluginId> result = new ArrayList<PluginId>();
+    for (PluginId pluginId : descriptor.getDependentPluginIds()) {
+      if (pluginId.getIdString().equals("com.intellij")) continue;
+      if (!ArrayUtil.contains(pluginId, descriptor.getOptionalDependentPluginIds())) {
+        result.add(pluginId);
+      }
+    }
+    return result;
+  }
+
   private SelectPluginsStep addSelectPluginsStep(final String category, final String title, final String requirePlugin) {
-    final SelectPluginsStep step = new SelectPluginsStep(title, myDisabledPluginIds, requirePlugin);
+    final SelectPluginsStep step = new SelectPluginsStep(title, this, requirePlugin);
     add(step);
     myStepMap.put(category, step);
     return step;
@@ -86,4 +124,59 @@ public class StartupWizardModel extends WizardModel {
   public Collection<String> getDisabledPluginIds() {
     return myDisabledPluginIds;
   }
+
+  public boolean isDisabledPlugin(IdeaPluginDescriptor descriptor) {
+    return myDisabledPluginIds.contains(descriptor.getPluginId().toString());
+  }
+
+  public void setPluginEnabled(final IdeaPluginDescriptor desc, boolean value) {
+    if (value) {
+      myDisabledPluginIds.remove(desc.getPluginId().toString());
+    }
+    else {
+      myDisabledPluginIds.add(desc.getPluginId().toString());
+    }
+  }
+
+  public void setPluginEnabledWithDependencies(final IdeaPluginDescriptor desc) {
+    setPluginEnabled(desc, true);
+    for(PluginId id: getNonOptionalDependencies(desc)) {
+      final IdeaPluginDescriptor dependent = findPlugin(id);
+      if (dependent != null) {
+        setPluginEnabledWithDependencies(dependent);
+      }
+    }
+  }
+
+  public void setPluginDisabledWithDependents(final IdeaPluginDescriptor desc) {
+    setPluginEnabled(desc, false);
+    for (IdeaPluginDescriptor plugin : myAllPlugins) {
+      if (ArrayUtil.contains(desc.getPluginId(), plugin.getDependentPluginIds()) &&
+          !ArrayUtil.contains(desc.getPluginId(), plugin.getOptionalDependentPluginIds())) {
+        setPluginDisabledWithDependents(plugin);
+      }
+    }
+  }
+
+  public boolean isForceEnable(IdeaPluginDescriptor descriptor) {
+    return getDependentsOnEarlierPages(descriptor, true).size() > 0;
+  }
+
+  public List<IdeaPluginDescriptor> getDependentsOnEarlierPages(IdeaPluginDescriptor descriptor, boolean includeSamePage) {
+    List<IdeaPluginDescriptor> dependents = new ArrayList<IdeaPluginDescriptor>();
+    int thisStep = getPluginStepIndex(descriptor);
+    for (IdeaPluginDescriptor dependent : myBackwardDependencies.get(descriptor)) {
+      if (!myDisabledPluginIds.contains(dependent.getPluginId().toString())) {
+        int index = getPluginStepIndex(dependent);
+        if (index < thisStep || (includeSamePage && index == thisStep && getDependentsOnEarlierPages(dependent, true).size() > 0)) {
+          dependents.add(dependent);
+        }
+      }
+    }
+    return dependents;
+  }
+
+  private int getPluginStepIndex(IdeaPluginDescriptor descriptor) {
+    return getStepIndex(myPluginToStepMap.get(descriptor.getPluginId()));
+  }
 }
index 44c24e49f3bc83ca0f5877b09785489badd0381c..4e172350d636cac0c1fc56987b91f1609d4acfab 100644 (file)
@@ -201,4 +201,8 @@ public class WizardModel {
   public String getTitle() {
     return myTitle;
   }
+
+  public int getStepIndex(WizardStep step) {
+    return mySteps.indexOf(step);
+  }
 }