gradle tool window rework initial commit
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Mon, 8 Dec 2014 08:52:08 +0000 (11:52 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Mon, 8 Dec 2014 09:00:14 +0000 (12:00 +0300)
78 files changed:
platform/external-system-api/resources/i18n/ExternalSystemBundle.properties
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/TaskData.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewState.java [new file with mode: 0644]
platform/external-system-impl/resources/icons/taskGroup.png [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/EditExternalSystemRunConfigurationAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalActionUtil.java [deleted file]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemActionUtil.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemNodeAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemRunConfigurationMenu.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemToggleAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemTreeAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemViewGearAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/OpenExternalConfigAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshExternalProjectAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RemoveExternalSystemRunConfigurationAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ShowExternalSystemSettingsAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ToggleAutoImportAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignRunConfigurationShortcutAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignShortcutAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/GroupTasksAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/RunExternalSystemTaskAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterCompileTasksAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterRebuildTasksAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterSyncTaskAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeCompileTasksAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRebuildTasksAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRunTaskAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleTaskActivationAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/ExternalSystemDataKeys.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/AbstractExternalSystemTaskConfigurationType.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/ProjectStructureHelper.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsManager.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsState.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemRunManagerListener.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemTaskActivator.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataServiceImpl.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/TaskActivationState.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/AbstractToolWindowService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ToolWindowModuleService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ToolWindowTaskService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/AbstractExternalSystemToolWindowFactory.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModel.java [deleted file]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTasksList.java [deleted file]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemTasksPanel.java [deleted file]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUiUtil.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsStructure.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsView.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewContributor.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewDefaultContributor.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ModuleNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ProjectNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationsNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TaskNode.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TasksNode.java [new file with mode: 0644]
platform/external-system-impl/src/icons/ExternalSystemIcons.java
platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModelTest.java [deleted file]
platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml
platform/platform-resources/src/META-INF/ExternalSystemExtensions.xml
platform/platform-resources/src/idea/ExternalSystemActions.xml
plugins/gradle/resources/icons/offlineMode.png [new file with mode: 0644]
plugins/gradle/src/META-INF/plugin.xml
plugins/gradle/src/icons/GradleIcons.java
plugins/gradle/src/org/jetbrains/plugins/gradle/action/ToggleOfflineAction.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy

index 42cdd96b2f7d15f2c2dc7586e6616a18a4f260ba..e89aa54a1238cb0afa39235511dc80aab73ec77f 100644 (file)
@@ -32,7 +32,9 @@ error.resolve.generic=Resolve error
 error.resolve.already.running=Another 'refresh project' task is currently running for the project:  {0}
 
 # Tool window
+tool.window.title.projects=projects
 tool.window.title.tasks=tasks
+external.system.view.nodes.run_configurations.name = Run Configurations
 
 # Action.
 action.refresh.all.projects.text=Refresh all {0} projects
@@ -46,14 +48,29 @@ action.detach.external.project.description=Detach selected external project
 action.open.config.text=Open {0} config
 action.open.config.description=Allows to open project file of the linked {0} project at the editor
 
+action.refresh.project.auto.text=Auto-import
+action.refresh.project.auto.description=Enable/disable project auto-import
+
+action.open.settings.text={0} Settings
+action.open.settings.description=Edit {0} settings for the current project
+
 # Notification
 notification.project.refresh.fail.title={0} ''{1}'' project refresh failed
 notification.messages.project.sync.tab.name={0} Sync
+notification.messages.task.execution.tab.name={0} Task
 
 # Tasks.
 tasks.recent.title=Recent tasks
 tasks.all.title=All tasks
 
+external.system.keymap.group=External Build Systems
+external.system.task.after.sync=After Import
+external.system.task.before.compile=Before Make
+external.system.task.after.compile=After Make
+external.system.task.before.rebuild=Before Rebuild
+external.system.task.after.rebuild=After Rebuild
+external.system.task.before.run=Before Run
+
 # Execution
 run.configuration.description={0} build
 run.configuration.tooltip.choose.registered.project=Choose one of registered {0} projects
index 325c7cdd45b2856f860fe0f729c8f3ac2e44e160..869731d2d90b3782124b3ef8b76f6afd05c7d608 100644 (file)
@@ -53,7 +53,7 @@ public class DataNode<T> implements Serializable {
   private transient T myData;
   private byte[] myRawData;
 
-  @Nullable private final DataNode<?> myParent;
+  @Nullable private DataNode<?> myParent;
 
   public DataNode(@NotNull Key<T> key, @NotNull T data, @Nullable DataNode<?> parent) {
     myKey = key;
@@ -176,6 +176,8 @@ public class DataNode<T> implements Serializable {
       };
       myData = (T)oIn.readObject();
       myRawData = null;
+
+      assert myData != null;
     }
     catch (IOException e) {
       throw new IllegalStateException(
@@ -239,18 +241,32 @@ public class DataNode<T> implements Serializable {
   }
 
   private void writeObject(ObjectOutputStream out) throws IOException {
+    try {
+      myRawData = getDataBytes();
+    }
+    catch (IOException e) {
+      LOG.warn("Unable to serialize the data node - " + toString());
+      throw e;
+    }
+    out.defaultWriteObject();
+  }
+
+  public byte[] getDataBytes() throws IOException {
+    if (myRawData != null) return myRawData;
+
     ByteArrayOutputStream bOut = new ByteArrayOutputStream();
     ObjectOutputStream oOut = new ObjectOutputStream(bOut);
     try {
       oOut.writeObject(myData);
+      final byte[] bytes = bOut.toByteArray();
+      myRawData = bytes;
+      return bytes;
     }
     finally {
       oOut.close();
     }
-    myRawData = bOut.toByteArray();
-    out.defaultWriteObject();
   }
-  
+
   @Override
   public int hashCode() {
     int result = myChildren.hashCode();
@@ -285,4 +301,19 @@ public class DataNode<T> implements Serializable {
     }
     return String.format("%s: %s", myKey, dataDescription);
   }
+
+  public void clear(boolean removeFromGraph) {
+    if (removeFromGraph && myParent != null) {
+      for (Iterator<DataNode<?>> iterator = myParent.getChildren().iterator(); iterator.hasNext(); ) {
+        DataNode<?> dataNode = iterator.next();
+        if (System.identityHashCode(dataNode) == System.identityHashCode(this)) {
+          iterator.remove();
+          break;
+        }
+      }
+    }
+    myParent = null;
+    myRawData = null;
+    myChildren.clear();
+  }
 }
index d5279404c8e3621604240146239abe0bdfc60ce4..04296dc7730628d693ba6577c0c74e9bb33a18e2 100644 (file)
@@ -33,6 +33,7 @@ public class TaskData extends AbstractExternalEntityData implements ExternalConf
 
   @NotNull private final String myName;
   @NotNull private final String myLinkedExternalProjectPath;
+  @Nullable private String myGroup;
 
   @Nullable private final String myDescription;
 
@@ -58,12 +59,22 @@ public class TaskData extends AbstractExternalEntityData implements ExternalConf
     return myDescription;
   }
 
+  @Nullable
+  public String getGroup() {
+    return myGroup;
+  }
+
+  public void setGroup(@Nullable String group) {
+    myGroup = group;
+  }
+
   @Override
   public int hashCode() {
     int result = super.hashCode();
     result = 31 * result + myName.hashCode();
     result = 31 * result + myLinkedExternalProjectPath.hashCode();
     result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0);
+    result = 31 * result + (myGroup != null ? myGroup.hashCode() : 0);
     return result;
   }
 
@@ -76,6 +87,7 @@ public class TaskData extends AbstractExternalEntityData implements ExternalConf
     TaskData data = (TaskData)o;
 
     if (myDescription != null ? !myDescription.equals(data.myDescription) : data.myDescription != null) return false;
+    if (myGroup != null ? !myGroup.equals(data.myGroup) : data.myGroup != null) return false;
     if (!myLinkedExternalProjectPath.equals(data.myLinkedExternalProjectPath)) return false;
     if (!myName.equals(data.myName)) return false;
 
index aa695093339061847587f6ec0e4e62a450bf5cc6..4d4245648c15ca8f19219d1c042a653c06a4f54b 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsViewState;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.util.SystemProperties;
@@ -73,6 +74,10 @@ public abstract class AbstractExternalSystemLocalSettings {
                                                                                                              myExternalConfigModificationStamps =
     new AtomicReference<Map<String, Long>>(ContainerUtilRt.<String, Long>newHashMap());
 
+  private final AtomicReference<ExternalProjectsViewState> myExternalProjectsViewState = new AtomicReference<ExternalProjectsViewState>(
+    new ExternalProjectsViewState()
+  );
+
   @NotNull private final ProjectSystemId myExternalSystemId;
   @NotNull private final Project         myProject;
   @NotNull private final PlatformFacade  myPlatformFacade;
@@ -189,6 +194,16 @@ public abstract class AbstractExternalSystemLocalSettings {
     myProjectBuildClasspath.set(projectsBuildClasspath);
   }
 
+  public ExternalProjectsViewState getExternalProjectsViewState() {
+    return myExternalProjectsViewState.get();
+  }
+
+  @SuppressWarnings("UnusedDeclaration")
+  public void setExternalProjectsViewState(ExternalProjectsViewState externalProjectsViewState) {
+    // Required for IJ serialization.
+    myExternalProjectsViewState.set(externalProjectsViewState);
+  }
+
   public void fillState(@NotNull State state) {
     if (PRESERVE_EXPAND_STATE) {
       state.tasksExpandState = myExpandStates.get();
@@ -201,6 +216,7 @@ public abstract class AbstractExternalSystemLocalSettings {
     state.availableTasks = myAvailableTasks.get();
     state.modificationStamps = myExternalConfigModificationStamps.get();
     state.projectBuildClasspath = myProjectBuildClasspath.get();
+    state.externalProjectsViewState = myExternalProjectsViewState.get();
   }
 
   public void loadState(@NotNull State state) {
@@ -209,6 +225,7 @@ public abstract class AbstractExternalSystemLocalSettings {
     setIfNotNull(myAvailableTasks, state.availableTasks);
     setIfNotNull(myExternalConfigModificationStamps, state.modificationStamps);
     setIfNotNull(myProjectBuildClasspath, state.projectBuildClasspath);
+    myExternalProjectsViewState.set(state.externalProjectsViewState);
     if (state.recentTasks != null) {
       List<ExternalTaskExecutionInfo> recentTasks = myRecentTasks.get();
       if (recentTasks != state.recentTasks) {
@@ -270,5 +287,6 @@ public abstract class AbstractExternalSystemLocalSettings {
     public Map<String/* linked project path */, Long/* last config modification stamp */> modificationStamps
       = ContainerUtilRt.newHashMap();
     public Map<String/* linked project path */, ExternalProjectBuildClasspathPojo> projectBuildClasspath = ContainerUtilRt.newHashMap();
+    public ExternalProjectsViewState externalProjectsViewState;
   }
 }
index 88da2ad4dee931f39c949b9d110edf1a386b2c73..d14d908cf03b5cca23895ab8c78bf62c478af51a 100644 (file)
@@ -23,15 +23,14 @@ import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware;
 import com.intellij.openapi.externalSystem.ExternalSystemManager;
-import com.intellij.openapi.externalSystem.model.DataNode;
-import com.intellij.openapi.externalSystem.model.ExternalSystemException;
-import com.intellij.openapi.externalSystem.model.Key;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.*;
 import com.intellij.openapi.externalSystem.model.project.LibraryData;
 import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings;
 import com.intellij.openapi.externalSystem.service.ParametersEnhancer;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener;
 import com.intellij.openapi.fileTypes.FileTypes;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
@@ -46,9 +45,12 @@ import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.JarFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.*;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.Stack;
 import com.intellij.util.containers.TransferToEDTQueue;
 import com.intellij.util.lang.UrlClassLoader;
+import com.intellij.util.messages.MessageBusConnection;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.Contract;
 import org.jetbrains.annotations.NotNull;
@@ -361,6 +363,84 @@ public class ExternalSystemApiUtil {
     return result == null ? Collections.<DataNode<T>>emptyList() : result;
   }
 
+  public static void visit(@Nullable DataNode node, @NotNull Consumer<DataNode> consumer) {
+    if(node == null) return;
+
+    Stack<DataNode> toProcess = ContainerUtil.newStack(node);
+    while (!toProcess.isEmpty()) {
+      DataNode<?> node0 = toProcess.pop();
+      consumer.consume(node0);
+      for (DataNode<?> child : node0.getChildren()) {
+        toProcess.push(child);
+      }
+    }
+  }
+
+  @NotNull
+  public static <T> Collection<DataNode<T>> findAllRecursively(@Nullable final DataNode<?> node,
+                                                               @NotNull final Key<T> key) {
+    if (node == null) return Collections.emptyList();
+
+    final Collection<DataNode<?>> nodes = findAllRecursively(node.getChildren(), new BooleanFunction<DataNode<?>>() {
+      @Override
+      public boolean fun(DataNode<?> node) {
+        return node.getKey().equals(key);
+      }
+    });
+    //noinspection unchecked
+    return new SmartList(nodes);
+  }
+
+  public static Collection<DataNode<?>> findAllRecursively(@NotNull Collection<DataNode<?>> nodes) {
+    return findAllRecursively(nodes, null);
+  }
+
+  @NotNull
+  public static Collection<DataNode<?>> findAllRecursively(@Nullable DataNode<?> node,
+                                                           @Nullable BooleanFunction<DataNode<?>> predicate) {
+    if (node == null) return Collections.emptyList();
+    return findAllRecursively(node.getChildren(), predicate);
+  }
+
+  public static Collection<DataNode<?>> findAllRecursively(@NotNull Collection<DataNode<?>> nodes,
+                                                           @Nullable BooleanFunction<DataNode<?>> predicate) {
+    SmartList<DataNode<?>> result = new SmartList<DataNode<?>>();
+    for (DataNode<?> node : nodes) {
+      if (predicate == null || predicate.fun(node)) {
+        result.add(node);
+      }
+    }
+    for (DataNode<?> node : nodes) {
+      result.addAll(findAllRecursively(node.getChildren(), predicate));
+    }
+    return result;
+  }
+
+  public static DataNode<?> findFirstRecursively(@NotNull DataNode<?> parentNode,
+                                                 @NotNull BooleanFunction<DataNode<?>> predicate) {
+    Queue<DataNode<?>> queue = new LinkedList<DataNode<?>>();
+    queue.add(parentNode);
+    return findInQueue(queue, predicate);
+  }
+
+  public static DataNode<?> findFirstRecursively(@NotNull Collection<DataNode<?>> nodes,
+                                                 @NotNull BooleanFunction<DataNode<?>> predicate) {
+    return findInQueue(new LinkedList<DataNode<?>>(nodes), predicate);
+  }
+
+  private static DataNode<?> findInQueue(@NotNull Queue<DataNode<?>> queue,
+                                         @NotNull BooleanFunction<DataNode<?>> predicate) {
+    while (!queue.isEmpty()) {
+      DataNode node = (DataNode)queue.remove();
+      if (predicate.fun(node)) {
+        return node;
+      }
+      //noinspection unchecked
+      queue.addAll(node.getChildren());
+    }
+    return null;
+  }
+
   public static void executeProjectChangeAction(@NotNull final DisposeAwareProjectChange task) {
     executeProjectChangeAction(false, task);
   }
@@ -682,8 +762,20 @@ public class ExternalSystemApiUtil {
     return module != null && !module.isDisposed() ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY) : null;
   }
 
+  @Nullable
+  public static String getExternalRootProjectPath(@Nullable Module module) {
+    return module != null && !module.isDisposed() ? module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY) : null;
+  }
+
   @Nullable
   public static String getExternalProjectId(@Nullable Module module) {
     return module != null && !module.isDisposed() ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_ID_KEY) : null;
   }
+
+  public static void subscribe(@NotNull Project project,
+                               @NotNull ProjectSystemId systemId,
+                               @NotNull ExternalSystemSettingsListener listener) {
+    MessageBusConnection connection = project.getMessageBus().connect(project);
+    connection.subscribe(getSettings(project, systemId).getChangesTopic(), listener);
+  }
 }
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewState.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewState.java
new file mode 100644 (file)
index 0000000..6bf6d86
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.util.xmlb.annotations.Property;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jdom.Element;
+
+import java.io.Serializable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/26/2014
+ */
+@Tag("projects_view")
+public class ExternalProjectsViewState {
+  public boolean groupTasks = true;
+  @Tag("tree_state")
+  public Element treeState;
+}
diff --git a/platform/external-system-impl/resources/icons/taskGroup.png b/platform/external-system-impl/resources/icons/taskGroup.png
new file mode 100644 (file)
index 0000000..7604d83
Binary files /dev/null and b/platform/external-system-impl/resources/icons/taskGroup.png differ
index 88f2c26a68a1f83d438ffaf0095fc4642bc845c8..80e1e0f3a9da9e6eb6b06add5159261f4faf079d 100644 (file)
 package com.intellij.openapi.externalSystem.action;
 
 import com.intellij.icons.AllIcons;
-import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
+import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemRecentTasksList;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemTasksTreeModel;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
-import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
-import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.SystemInfoRt;
+import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.Collections;
 import java.util.List;
@@ -49,63 +46,71 @@ import java.util.List;
  * @author Denis Zhdanov
  * @since 6/13/13 5:42 PM
  */
-public class DetachExternalProjectAction extends AnAction implements DumbAware {
+public class DetachExternalProjectAction extends ExternalSystemNodeAction<AbstractExternalEntityData> implements DumbAware {
 
   public DetachExternalProjectAction() {
+    super(AbstractExternalEntityData.class);
     getTemplatePresentation().setText(ExternalSystemBundle.message("action.detach.external.project.text", "external"));
     getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.detach.external.project.description"));
     getTemplatePresentation().setIcon(SystemInfoRt.isMac ? AllIcons.ToolbarDecorator.Mac.Remove : AllIcons.ToolbarDecorator.Remove);
   }
 
   @Override
-  public void update(AnActionEvent e) {
-    ExternalActionUtil.MyInfo info = ExternalActionUtil.getProcessingInfo(e.getDataContext());
-    e.getPresentation().setEnabled(info.externalProject != null);
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return false;
+    final Object externalData = selectedNodes.get(0).getData();
+    return (externalData instanceof ProjectData || externalData instanceof ModuleData);
   }
 
   @Override
-  public void actionPerformed(AnActionEvent e) {
-    ExternalActionUtil.MyInfo info = ExternalActionUtil.getProcessingInfo(e.getDataContext());
-    if (info.settings == null || info.localSettings == null || info.externalProject == null || info.ideProject == null
-        || info.externalSystemId == null)
-    {
-      return;
-    }
-    
+  public void perform(@NotNull final Project project,
+                      @NotNull ProjectSystemId projectSystemId,
+                      @NotNull AbstractExternalEntityData entityData,
+                      @NotNull AnActionEvent e) {
+
     e.getPresentation().setText(
-      ExternalSystemBundle.message("action.detach.external.project.text",info.externalSystemId.getReadableName())
+      ExternalSystemBundle.message("action.detach.external.project.text", projectSystemId.getReadableName())
     );
 
-    ExternalSystemTasksTreeModel allTasksModel = ExternalSystemDataKeys.ALL_TASKS_MODEL.getData(e.getDataContext());
-    if (allTasksModel != null) {
-      allTasksModel.pruneNodes(info.externalProject);
-    }
 
-    ExternalSystemRecentTasksList recentTasksList = ExternalSystemDataKeys.RECENT_TASKS_LIST.getData(e.getDataContext());
-    if (recentTasksList != null) {
-      recentTasksList.getModel().forgetTasksFrom(info.externalProject.getPath());
-    }
-    
-    info.localSettings.forgetExternalProjects(Collections.singleton(info.externalProject.getPath()));
-    info.settings.unlinkExternalProject(info.externalProject.getPath());
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    final ExternalSystemNode<?> externalSystemNode = ContainerUtil.getFirstItem(selectedNodes);
+    assert externalSystemNode != null;
+    final ProjectNode projectNode =
+      externalSystemNode instanceof ProjectNode ? (ProjectNode)externalSystemNode : externalSystemNode.findParent(ProjectNode.class);
+    assert projectNode != null;
+
+    final ProjectData projectData = projectNode.getData();
+    assert projectData != null;
+    ExternalSystemApiUtil.getLocalSettings(project, projectSystemId).
+      forgetExternalProjects(Collections.singleton(projectData.getLinkedExternalProjectPath()));
+    ExternalSystemApiUtil.getSettings(project, projectSystemId).unlinkExternalProject(projectData.getLinkedExternalProjectPath());
+
+    ExternalProjectsManager.getInstance(project).forgetExternalProjectData(projectSystemId, projectData.getLinkedExternalProjectPath());
 
     // Process orphan modules.
     PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    String externalSystemIdAsString = info.externalSystemId.toString();
     List<Module> orphanModules = ContainerUtilRt.newArrayList();
-    for (Module module : platformFacade.getModules(info.ideProject)) {
-      String systemId = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
-      if (!externalSystemIdAsString.equals(systemId)) {
-        continue;
-      }
-      String path = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
-      if (info.externalProject.getPath().equals(path)) {
+    for (Module module : platformFacade.getModules(project)) {
+      if (!ExternalSystemApiUtil.isExternalSystemAwareModule(projectSystemId, module)) continue;
+
+      String path = ExternalSystemApiUtil.getExternalRootProjectPath(module);
+      if (projectData.getLinkedExternalProjectPath().equals(path)) {
         orphanModules.add(module);
       }
     }
 
     if (!orphanModules.isEmpty()) {
-      ExternalSystemUtil.ruleOrphanModules(orphanModules, info.ideProject, info.externalSystemId);
+      ExternalSystemUtil.ruleOrphanModules(orphanModules, project, projectSystemId, new Consumer<Boolean>() {
+        @Override
+        public void consume(Boolean result) {
+          if (result != null && result) {
+            projectNode.getGroup().remove(projectNode);
+          }
+        }
+      });
     }
   }
 }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/EditExternalSystemRunConfigurationAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/EditExternalSystemRunConfigurationAction.java
new file mode 100644 (file)
index 0000000..a368a1e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.impl.EditConfigurationsDialog;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.RunConfigurationNode;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/13/2014
+ */
+public class EditExternalSystemRunConfigurationAction extends ExternalSystemAction {
+
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return false;
+    return selectedNodes.get(0) instanceof RunConfigurationNode;
+  }
+
+  @Override
+  public void actionPerformed(@NotNull AnActionEvent e) {
+    Project project = getProject(e);
+    assert project != null;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1 || !(selectedNodes.get(0) instanceof RunConfigurationNode)) return;
+
+    RunnerAndConfigurationSettings settings = ((RunConfigurationNode)selectedNodes.get(0)).getSettings();
+    assert settings != null;
+    RunManager.getInstance(project).setSelectedConfiguration(settings);
+    EditConfigurationsDialog dialog = new EditConfigurationsDialog(project);
+    dialog.show();
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalActionUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalActionUtil.java
deleted file mode 100644 (file)
index 9543edf..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2000-2013 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.action;
-
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.actionSystem.PlatformDataKeys;
-import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
-import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
-import com.intellij.openapi.project.Project;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * @author Vladislav.Soroka
- * @since 9/18/13
- */
-public class ExternalActionUtil {
-  @NotNull
-  public static MyInfo getProcessingInfo(@NotNull DataContext context) {
-    ExternalProjectPojo externalProject = ExternalSystemDataKeys.SELECTED_PROJECT.getData(context);
-    if (externalProject == null) {
-      return MyInfo.EMPTY;
-    }
-
-    ProjectSystemId externalSystemId = ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(context);
-    if (externalSystemId == null) {
-      return MyInfo.EMPTY;
-    }
-
-    Project ideProject = CommonDataKeys.PROJECT.getData(context);
-    if (ideProject == null) {
-      return MyInfo.EMPTY;
-    }
-
-    AbstractExternalSystemSettings<?, ?, ?> settings = ExternalSystemApiUtil.getSettings(ideProject, externalSystemId);
-    ExternalProjectSettings externalProjectSettings = settings.getLinkedProjectSettings(externalProject.getPath());
-    AbstractExternalSystemLocalSettings localSettings = ExternalSystemApiUtil.getLocalSettings(ideProject, externalSystemId);
-
-    return new MyInfo(externalProjectSettings == null ? null : settings,
-                      localSettings == null ? null : localSettings,
-                      externalProjectSettings == null ? null : externalProject,
-                      ideProject,
-                      externalSystemId);
-  }
-
-  public static class MyInfo {
-
-    public static final MyInfo EMPTY = new MyInfo(null, null, null, null, null);
-
-    @Nullable public final AbstractExternalSystemSettings<?, ?, ?> settings;
-    @Nullable public final AbstractExternalSystemLocalSettings  localSettings;
-    @Nullable public final ExternalProjectPojo                  externalProject;
-    @Nullable public final Project                              ideProject;
-    @Nullable public final ProjectSystemId                      externalSystemId;
-
-    MyInfo(@Nullable AbstractExternalSystemSettings<?, ?, ?> settings,
-           @Nullable AbstractExternalSystemLocalSettings localSettings,
-           @Nullable ExternalProjectPojo externalProject,
-           @Nullable Project ideProject,
-           @Nullable ProjectSystemId externalSystemId)
-    {
-      this.settings = settings;
-      this.localSettings = localSettings;
-      this.externalProject = externalProject;
-      this.ideProject = ideProject;
-      this.externalSystemId = externalSystemId;
-    }
-  }
-}
-
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemAction.java
new file mode 100644 (file)
index 0000000..2d13ff8
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public abstract class ExternalSystemAction extends AnAction implements DumbAware {
+
+  @Override
+  public void update(@NotNull AnActionEvent e) {
+    super.update(e);
+    Presentation p = e.getPresentation();
+    final boolean visible = isVisible(e);
+    p.setVisible(visible);
+    p.setEnabled(visible && isEnabled(e));
+  }
+
+  protected boolean isEnabled(AnActionEvent e) {
+    return hasProject(e) && getSystemId(e) != null;
+  }
+
+  protected boolean isVisible(AnActionEvent e) {
+    return true;
+  }
+
+  protected Project getProject(AnActionEvent e) {
+    return CommonDataKeys.PROJECT.getData(e.getDataContext());
+  }
+
+  protected ProjectSystemId getSystemId(AnActionEvent e) {
+    return ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
+  }
+
+  protected boolean hasProject(AnActionEvent e) {
+    return getProject(e) != null;
+  }
+}
\ No newline at end of file
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemActionUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemActionUtil.java
new file mode 100644 (file)
index 0000000..268664d
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.module.Module;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/18/13
+ */
+public class ExternalSystemActionUtil {
+
+  public static void executeAction(final String actionId, final InputEvent e) {
+    final ActionManager actionManager = ActionManager.getInstance();
+    final AnAction action = actionManager.getAction(actionId);
+    if (action != null) {
+      final Presentation presentation = new Presentation();
+      final AnActionEvent event =
+        new AnActionEvent(e, DataManager.getInstance().getDataContext(e.getComponent()), "", presentation, actionManager, 0);
+      action.update(event);
+      if (presentation.isEnabled()) {
+        action.actionPerformed(event);
+      }
+    }
+  }
+
+  @Nullable
+  public static Module getModule(DataContext context) {
+    final Module module = LangDataKeys.MODULE.getData(context);
+    return module != null ? module : LangDataKeys.MODULE_CONTEXT.getData(context);
+  }
+
+  public static <E> void setElements(ElementsChooser<E> chooser, Collection<E> all, Collection<E> selected, Comparator<E> comparator) {
+    List<E> selection = chooser.getSelectedElements();
+    chooser.clear();
+    Collection<E> sorted = new TreeSet<E>(comparator);
+    sorted.addAll(all);
+    for (E element : sorted) {
+      chooser.addElement(element, selected.contains(element));
+    }
+    chooser.selectElements(selection);
+  }
+
+  public static void installCheckboxRenderer(final SimpleTree tree, final CheckboxHandler handler) {
+    final JCheckBox checkbox = new JCheckBox();
+
+    final JPanel panel = new JPanel(new BorderLayout());
+    panel.add(checkbox, BorderLayout.WEST);
+
+    final TreeCellRenderer baseRenderer = tree.getCellRenderer();
+    tree.setCellRenderer(new TreeCellRenderer() {
+      public Component getTreeCellRendererComponent(final JTree tree,
+                                                    final Object value,
+                                                    final boolean selected,
+                                                    final boolean expanded,
+                                                    final boolean leaf,
+                                                    final int row,
+                                                    final boolean hasFocus) {
+        final Component baseComponent = baseRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+
+        final Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
+        if (!handler.isVisible(userObject)) {
+          return baseComponent;
+        }
+
+        final Color foreground = selected ? UIUtil.getTreeSelectionForeground() : UIUtil.getTreeTextForeground();
+
+        Color background = selected ? UIUtil.getTreeSelectionBackground(hasFocus) : UIUtil.getTreeTextBackground();
+
+        panel.add(baseComponent, BorderLayout.CENTER);
+        panel.setBackground(background);
+        panel.setForeground(foreground);
+
+        CheckBoxState state = handler.getState(userObject);
+        checkbox.setSelected(state != CheckBoxState.UNCHECKED);
+        checkbox.setEnabled(state != CheckBoxState.PARTIAL);
+        checkbox.setBackground(background);
+        checkbox.setForeground(foreground);
+
+        return panel;
+      }
+    });
+
+    tree.addMouseListener(new MouseAdapter() {
+      public void mousePressed(MouseEvent e) {
+        int row = tree.getRowForLocation(e.getX(), e.getY());
+        if (row >= 0) {
+          TreePath path = tree.getPathForRow(row);
+          if (!isCheckboxEnabledFor(path, handler)) return;
+
+          Rectangle checkBounds = checkbox.getBounds();
+          checkBounds.setLocation(tree.getRowBounds(row).getLocation());
+          if (checkBounds.contains(e.getPoint())) {
+            handler.toggle(path, e);
+            e.consume();
+            tree.setSelectionRow(row);
+          }
+        }
+      }
+    });
+
+    tree.addKeyListener(new KeyAdapter() {
+      public void keyPressed(KeyEvent e) {
+        if (e.getKeyCode() == KeyEvent.VK_SPACE) {
+          TreePath[] treePaths = tree.getSelectionPaths();
+          if (treePaths != null) {
+            for (TreePath treePath : treePaths) {
+              if (!isCheckboxEnabledFor(treePath, handler)) continue;
+              handler.toggle(treePath, e);
+            }
+            e.consume();
+          }
+        }
+      }
+    });
+  }
+
+  private static boolean isCheckboxEnabledFor(TreePath path, CheckboxHandler handler) {
+    Object userObject = ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObject();
+    return handler.isVisible(userObject);
+  }
+
+  public interface CheckboxHandler {
+    void toggle(TreePath treePath, final InputEvent e);
+
+    boolean isVisible(Object userObject);
+
+    CheckBoxState getState(Object userObject);
+  }
+
+  public enum CheckBoxState {
+    CHECKED, UNCHECKED, PARTIAL
+  }
+
+  @NotNull
+  public static ExternalTaskExecutionInfo buildTaskInfo(@NotNull TaskData task) {
+    ExternalSystemTaskExecutionSettings settings = new ExternalSystemTaskExecutionSettings();
+    settings.setExternalProjectPath(task.getLinkedExternalProjectPath());
+    settings.setTaskNames(Collections.singletonList(task.getName()));
+    settings.setTaskDescriptions(Collections.singletonList(task.getDescription()));
+    settings.setExternalSystemIdString(task.getOwner().toString());
+    return new ExternalTaskExecutionInfo(settings, DefaultRunExecutor.EXECUTOR_ID);
+  }
+}
+
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemNodeAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemNodeAction.java
new file mode 100644 (file)
index 0000000..94fb31f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
+import com.intellij.openapi.externalSystem.service.settings.ExternalSystemConfigLocator;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/17/2014
+ */
+public abstract class ExternalSystemNodeAction<T> extends ExternalSystemAction {
+
+  private final Class<T> myExternalDataClazz;
+
+  public ExternalSystemNodeAction(Class<T> externalDataClazz) {
+    super();
+    myExternalDataClazz = externalDataClazz;
+  }
+
+  protected boolean isEnabled(AnActionEvent e) {
+    return super.isEnabled(e) && getSystemId(e) != null && getExternalData(e, myExternalDataClazz) != null;
+  }
+
+  protected abstract void perform(@NotNull Project project,
+                                  @NotNull ProjectSystemId projectSystemId,
+                                  @NotNull T externalData,
+                                  @NotNull AnActionEvent e);
+
+  @Override
+  public void actionPerformed(@NotNull AnActionEvent e) {
+    final Project project = getProject(e);
+    if (project == null) return;
+
+    ProjectSystemId projectSystemId = getSystemId(e);
+    if (projectSystemId == null) return;
+
+    final T data = getExternalData(e, myExternalDataClazz);
+    if (data == null) return;
+
+    perform(project, projectSystemId, data, e);
+  }
+
+  @Nullable
+  protected ExternalSystemUiAware getExternalSystemUiAware(AnActionEvent e) {
+    return ExternalSystemDataKeys.UI_AWARE.getData(e.getDataContext());
+  }
+
+  @SuppressWarnings("unchecked")
+  @Nullable
+  protected <T> T getExternalData(AnActionEvent e, Class<T> dataClass) {
+    ExternalSystemNode node = ContainerUtil.getFirstItem(ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext()));
+    return node != null && dataClass.isInstance(node.getData()) ? (T)node.getData() : null;
+  }
+
+  @Nullable
+  protected VirtualFile getExternalConfig(@NotNull ExternalConfigPathAware data, ProjectSystemId externalSystemId) {
+    String path = data.getLinkedExternalProjectPath();
+    LocalFileSystem fileSystem = LocalFileSystem.getInstance();
+    VirtualFile externalSystemConfigPath = fileSystem.refreshAndFindFileByPath(path);
+    if (externalSystemConfigPath == null) {
+      return null;
+    }
+
+    VirtualFile toOpen = externalSystemConfigPath;
+    for (ExternalSystemConfigLocator locator : ExternalSystemConfigLocator.EP_NAME.getExtensions()) {
+      if (externalSystemId.equals(locator.getTargetExternalSystemId())) {
+        toOpen = locator.adjust(toOpen);
+        if (toOpen == null) {
+          return null;
+        }
+        break;
+      }
+    }
+    return toOpen.isDirectory() ? null : toOpen;
+  }
+}
\ No newline at end of file
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemRunConfigurationMenu.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemRunConfigurationMenu.java
new file mode 100644 (file)
index 0000000..843ecce
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.execution.*;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.RunConfigurationNode;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/20/2014
+ */
+public class ExternalSystemRunConfigurationMenu extends DefaultActionGroup implements DumbAware {
+  @Override
+  public void update(AnActionEvent e) {
+    for (AnAction action : getChildActionsOrStubs()) {
+      if (action instanceof ExecuteExternalSystemRunConfigurationAction) {
+        remove(action);
+      }
+    }
+
+    final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
+
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1 || !(selectedNodes.get(0) instanceof RunConfigurationNode)) return;
+
+    final RunnerAndConfigurationSettings settings = ((RunConfigurationNode)selectedNodes.get(0)).getSettings();
+
+    if (settings == null || project == null) return;
+
+    Executor[] executors = ExecutorRegistry.getInstance().getRegisteredExecutors();
+    for (int i = executors.length; --i >= 0; ) {
+      final ProgramRunner runner = RunnerRegistry.getInstance().getRunner(executors[i].getId(), settings.getConfiguration());
+      AnAction action = new ExecuteExternalSystemRunConfigurationAction(executors[i], runner != null, project, settings);
+      addAction(action, Constraints.FIRST);
+    }
+
+    super.update(e);
+  }
+
+  private static class ExecuteExternalSystemRunConfigurationAction extends AnAction {
+    private final Executor myExecutor;
+    private final boolean myEnabled;
+    private final Project myProject;
+    private final RunnerAndConfigurationSettings mySettings;
+
+    public ExecuteExternalSystemRunConfigurationAction(Executor executor,
+                                                       boolean enabled,
+                                                       Project project,
+                                                       RunnerAndConfigurationSettings settings) {
+      super(executor.getActionName(), null, executor.getIcon());
+      myExecutor = executor;
+      myEnabled = enabled;
+      myProject = project;
+      mySettings = settings;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent event) {
+      if (myEnabled) {
+        ProgramRunnerUtil.executeConfiguration(myProject, mySettings, myExecutor);
+        final RunManagerEx runManagerEx = RunManagerEx.getInstanceEx(myProject);
+        runManagerEx.setSelectedConfiguration(mySettings);
+      }
+    }
+
+    @Override
+    public void update(@NotNull AnActionEvent e) {
+      super.update(e);
+      e.getPresentation().setEnabled(myEnabled);
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemToggleAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemToggleAction.java
new file mode 100644 (file)
index 0000000..c2ea887
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.actionSystem.ToggleAction;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public abstract class ExternalSystemToggleAction extends ToggleAction implements DumbAware {
+  @Override
+  public void update(AnActionEvent e) {
+    super.update(e);
+    Presentation p = e.getPresentation();
+    final boolean visible = isVisible(e);
+    p.setVisible(visible);
+    p.setEnabled(visible && isEnabled(e));
+  }
+
+  protected boolean isEnabled(AnActionEvent e) {
+    return hasProject(e);
+  }
+
+  protected boolean isVisible(AnActionEvent e) {
+    return true;
+  }
+
+  @Override
+  public final boolean isSelected(AnActionEvent e) {
+    if (!isEnabled(e)) return false;
+    return doIsSelected(e);
+  }
+
+  protected abstract boolean doIsSelected(AnActionEvent e);
+
+  protected Project getProject(AnActionEvent e) {
+    return CommonDataKeys.PROJECT.getData(e.getDataContext());
+  }
+
+  protected boolean hasProject(AnActionEvent e) {
+    return getProject(e) != null;
+  }
+
+  protected ProjectSystemId getSystemId(AnActionEvent e) {
+    return ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemTreeAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemTreeAction.java
new file mode 100644 (file)
index 0000000..92bb414
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public abstract class ExternalSystemTreeAction extends ExternalSystemAction {
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    return super.isEnabled(e) && getTree(e) != null;
+  }
+
+  @Nullable
+  protected static JTree getTree(AnActionEvent e) {
+    return ExternalSystemDataKeys.PROJECTS_TREE.getData(e.getDataContext());
+  }
+
+  public static class CollapseAll extends ExternalSystemTreeAction {
+    public void actionPerformed(AnActionEvent e) {
+      JTree tree = getTree(e);
+      if (tree == null) return;
+
+      int row = tree.getRowCount() - 1;
+      while (row >= 0) {
+        tree.collapseRow(row);
+        row--;
+      }
+    }
+  }
+
+  public static class ExpandAll extends ExternalSystemTreeAction {
+    public void actionPerformed(AnActionEvent e) {
+      JTree tree = getTree(e);
+      if (tree == null) return;
+
+      for (int i = 0; i < tree.getRowCount(); i++) {
+        tree.expandRow(i);
+      }
+    }
+  }
+}
+
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemViewGearAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemViewGearAction.java
new file mode 100644 (file)
index 0000000..a56c299
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/31/2014
+ */
+public abstract class ExternalSystemViewGearAction extends ExternalSystemToggleAction {
+
+  private ExternalProjectsView myView;
+
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    return getView() != null;
+  }
+
+  @Override
+  protected boolean doIsSelected(AnActionEvent e) {
+    final ExternalProjectsView view = getView();
+    return view != null && isSelected(view);
+  }
+
+  @Override
+  public void setSelected(AnActionEvent e, boolean state) {
+    final ExternalProjectsView view = getView();
+    if (view != null){
+      setSelected(view, state);
+    }
+  }
+
+  protected abstract boolean isSelected(@NotNull ExternalProjectsView view);
+
+  protected abstract void setSelected(@NotNull ExternalProjectsView view, boolean value);
+
+  @Nullable
+  protected ExternalProjectsView getView() {
+    return myView;
+  }
+
+  public void setView(ExternalProjectsView view) {
+    myView = view;
+  }
+}
index acc5aa4f08b463b1328bb2e3e55107fd5f4ccd11..65b1a0d8d4f577aef1009f58368c10695b098b39 100644 (file)
@@ -1,92 +1,59 @@
 package com.intellij.openapi.externalSystem.action;
 
-import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
-import com.intellij.openapi.externalSystem.service.settings.ExternalSystemConfigLocator;
+import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
+import com.intellij.openapi.externalSystem.model.project.ExternalEntityData;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
-import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 /**
- * @author Denis Zhdanov
- * @since 7/16/13 2:19 PM
+ * @author Vladislav.Soroka
+ * @since 10/17/2014
  */
-public class OpenExternalConfigAction extends AnAction implements DumbAware {
+public class OpenExternalConfigAction extends ExternalSystemNodeAction<ExternalConfigPathAware> {
 
   public OpenExternalConfigAction() {
+    super(ExternalConfigPathAware.class);
     getTemplatePresentation().setText(ExternalSystemBundle.message("action.open.config.text", "external"));
     getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.open.config.description", "external"));
   }
 
   @Override
-  public void update(AnActionEvent e) {
-    ProjectSystemId externalSystemId = ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
-    if (externalSystemId == null) {
-      e.getPresentation().setEnabled(false);
-      return;
-    }
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+
+    final ExternalEntityData externalData = getExternalData(e, ExternalEntityData.class);
+    if (!(externalData instanceof ExternalConfigPathAware)) return false;
 
+    VirtualFile config = getExternalConfig((ExternalConfigPathAware)externalData, externalData.getOwner());
+    if (config == null) return false;
+
+    ProjectSystemId externalSystemId = getSystemId(e);
     e.getPresentation().setText(ExternalSystemBundle.message("action.open.config.text", externalSystemId.getReadableName()));
     e.getPresentation().setDescription(ExternalSystemBundle.message("action.open.config.description", externalSystemId.getReadableName()));
-    e.getPresentation().setIcon(ExternalSystemUiUtil.getUiAware(externalSystemId).getProjectIcon());
+    final ExternalSystemUiAware uiAware = getExternalSystemUiAware(e);
+    if (uiAware != null) {
+      e.getPresentation().setIcon(uiAware.getProjectIcon());
+    }
 
-    VirtualFile config = getExternalConfig(e.getDataContext());
-    e.getPresentation().setEnabled(config != null);
+    return true;
   }
 
   @Override
-  public void actionPerformed(AnActionEvent e) {
-    Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
-    if (project == null) {
-      return;
-    }
-    
-    VirtualFile configFile = getExternalConfig(e.getDataContext());
-    if (configFile == null) {
-      return;
-    }
-    
-    OpenFileDescriptor descriptor = new OpenFileDescriptor(project, configFile);
-    FileEditorManager.getInstance(project).openTextEditor(descriptor, true); 
-  }
-
-  @Nullable
-  private static VirtualFile getExternalConfig(@NotNull DataContext context) {
-    ProjectSystemId externalSystemId = ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(context);
-    if (externalSystemId == null) {
-      return null;
-    }
-
-    ExternalProjectPojo projectPojo = ExternalSystemDataKeys.SELECTED_PROJECT.getData(context);
-    if (projectPojo == null) {
-      return null;
-    }
-
-    String path = projectPojo.getPath();
-    LocalFileSystem fileSystem = LocalFileSystem.getInstance();
-    VirtualFile externalSystemConfigPath = fileSystem.refreshAndFindFileByPath(path);
-    if (externalSystemConfigPath == null) {
-      return null;
-    }
-
-    VirtualFile toOpen = externalSystemConfigPath;
-    for (ExternalSystemConfigLocator locator : ExternalSystemConfigLocator.EP_NAME.getExtensions()) {
-      if (externalSystemId.equals(locator.getTargetExternalSystemId())) {
-        toOpen = locator.adjust(toOpen);
-        if (toOpen == null) {
-          return null;
-        }
-      }
+  protected void perform(@NotNull Project project,
+                         @NotNull ProjectSystemId systemId,
+                         @NotNull ExternalConfigPathAware configPathAware,
+                         @NotNull AnActionEvent e) {
+    VirtualFile configFile = getExternalConfig(configPathAware, systemId);
+    if (configFile != null) {
+      OpenFileDescriptor descriptor = new OpenFileDescriptor(project, configFile);
+      FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
     }
-    return toOpen.isDirectory() ? null : toOpen;
   }
 }
index e0e424be88870fb9a637babe5be3f4bf7c9e693e..b95465377d5a66f06651d14b4007dff741d8b556 100644 (file)
@@ -1,28 +1,33 @@
 package com.intellij.openapi.externalSystem.action;
 
-import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData;
+import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
 import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
 import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
-import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collections;
+import java.util.List;
 
 /**
  * * Forces the ide to retrieve the most up-to-date info about the linked external project and updates project state if necessary
@@ -31,44 +36,43 @@ import java.util.Collections;
  * @author Vladislav.Soroka
  * @since 9/18/13
  */
-public class RefreshExternalProjectAction extends AnAction implements DumbAware, AnAction.TransparentUpdate {
+public class RefreshExternalProjectAction extends ExternalSystemNodeAction<AbstractExternalEntityData> implements DumbAware {
 
   public RefreshExternalProjectAction() {
+    super(AbstractExternalEntityData.class);
     getTemplatePresentation().setText(ExternalSystemBundle.message("action.refresh.project.text", "external"));
     getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.refresh.project.description", "external"));
   }
 
   @Override
-  public void update(AnActionEvent e) {
-    ExternalActionUtil.MyInfo info = ExternalActionUtil.getProcessingInfo(e.getDataContext());
-    e.getPresentation().setEnabled(info.externalProject != null);
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return false;
+    final Object externalData = selectedNodes.get(0).getData();
+    return (externalData instanceof ProjectData || externalData instanceof ModuleData);
   }
 
   @Override
-  public void actionPerformed(AnActionEvent e) {
-    ExternalActionUtil.MyInfo info = ExternalActionUtil.getProcessingInfo(e.getDataContext());
-    if (info.settings == null || info.localSettings == null || info.externalProject == null || info.ideProject == null
-        || info.externalSystemId == null)
-    {
-      return;
-    }
-    ProjectSystemId externalSystemId = ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
-    if (externalSystemId == null) {
-      return;
-    }
-    
-    final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
-    if (project == null) {
-      e.getPresentation().setEnabled(false);
-      return;
-    }
+  public void perform(@NotNull final Project project,
+                      @NotNull ProjectSystemId projectSystemId,
+                      @NotNull AbstractExternalEntityData externalEntityData,
+                      @NotNull AnActionEvent e) {
+
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    final ExternalSystemNode<?> externalSystemNode = ContainerUtil.getFirstItem(selectedNodes);
+    assert externalSystemNode != null;
+
+    final ExternalConfigPathAware externalConfigPathAware =
+      externalSystemNode.getData() instanceof ExternalConfigPathAware ? (ExternalConfigPathAware)externalSystemNode.getData() : null;
+    assert externalConfigPathAware != null;
 
     // We save all documents because there is a possible case that there is an external system config file changed inside the ide.
     FileDocumentManager.getInstance().saveAllDocuments();
 
     final ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class);
     ExternalSystemUtil.refreshProject(
-      project, externalSystemId, info.externalProject.getPath(),
+      project, projectSystemId, externalConfigPathAware.getLinkedExternalProjectPath(),
       new ExternalProjectRefreshCallback() {
         @Override
         public void onSuccess(@Nullable final DataNode<ProjectData> externalProject) {
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RemoveExternalSystemRunConfigurationAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RemoveExternalSystemRunConfigurationAction.java
new file mode 100644 (file)
index 0000000..ea535f0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.RunConfigurationNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/13/2014
+ */
+public class RemoveExternalSystemRunConfigurationAction extends ExternalSystemAction {
+
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return false;
+    return selectedNodes.get(0) instanceof RunConfigurationNode;
+  }
+
+  @Override
+  public void actionPerformed(@NotNull AnActionEvent e) {
+    Project project = getProject(e);
+    assert project != null;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1 || !(selectedNodes.get(0) instanceof RunConfigurationNode)) return;
+
+    RunnerAndConfigurationSettings settings = ((RunConfigurationNode)selectedNodes.get(0)).getSettings();
+    assert settings != null;
+
+    int res = Messages.showYesNoDialog(project, "Delete \"" + settings.getName() + "\"?", "Confirmation", Messages.getQuestionIcon());
+    if (res == Messages.YES) {
+      ((RunManagerEx)RunManager.getInstance(project)).removeConfiguration(settings);
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ShowExternalSystemSettingsAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ShowExternalSystemSettingsAction.java
new file mode 100644 (file)
index 0000000..bb57cde
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public class ShowExternalSystemSettingsAction extends ExternalSystemAction {
+
+  public ShowExternalSystemSettingsAction() {
+    getTemplatePresentation().setText(ExternalSystemBundle.message("action.open.settings.text", "external"));
+    getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.open.settings.description", "external"));
+  }
+
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+
+    ProjectSystemId systemId = getSystemId(e);
+    if (systemId == null) return false;
+
+    e.getPresentation().setText(ExternalSystemBundle.message("action.open.settings.text", systemId.getReadableName()));
+    e.getPresentation().setDescription(ExternalSystemBundle.message("action.open.settings.description", systemId.getReadableName()));
+    return true;
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final ProjectSystemId systemId = getSystemId(e);
+    if (systemId != null) {
+      showSettingsFor(getProject(e), systemId);
+    }
+  }
+
+  protected static void showSettingsFor(Project project, @NotNull ProjectSystemId systemId) {
+    ShowSettingsUtil.getInstance().showSettingsDialog(project, systemId.getReadableName());
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ToggleAutoImportAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ToggleAutoImportAction.java
new file mode 100644 (file)
index 0000000..fd2da81
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public class ToggleAutoImportAction extends ExternalSystemToggleAction {
+
+  public ToggleAutoImportAction() {
+    getTemplatePresentation().setText(ExternalSystemBundle.message("action.refresh.project.auto.text"));
+    getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.refresh.project.auto.description"));
+  }
+
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    if (getSystemId(e) == null) return false;
+
+    return ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext()) != null;
+  }
+
+  @Override
+  protected boolean isVisible(AnActionEvent e) {
+    if (!super.isVisible(e)) return false;
+    if (getSystemId(e) == null) return false;
+
+    return ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext()) != null;
+  }
+
+  @Override
+  protected boolean doIsSelected(AnActionEvent e) {
+    final ExternalProjectSettings projectSettings = getProjectSettings(e);
+
+    return projectSettings != null && projectSettings.isUseAutoImport();
+  }
+
+  @Override
+  public void setSelected(AnActionEvent e, boolean state) {
+    final ExternalProjectSettings projectSettings = getProjectSettings(e);
+    if (projectSettings != null) {
+      if (state != projectSettings.isUseAutoImport()) {
+        projectSettings.setUseAutoImport(state);
+        ExternalSystemApiUtil.getSettings(getProject(e), getSystemId(e)).getPublisher()
+          .onUseAutoImportChange(state, projectSettings.getExternalProjectPath());
+      }
+    }
+  }
+
+  @Nullable
+  private ExternalProjectSettings getProjectSettings(AnActionEvent e) {
+    final ProjectNode projectNode = ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext());
+    if (projectNode == null || projectNode.getData() == null) return null;
+    final AbstractExternalSystemSettings externalSystemSettings = ExternalSystemApiUtil.getSettings(getProject(e), getSystemId(e));
+    return externalSystemSettings.getLinkedProjectSettings(projectNode.getData().getLinkedExternalProjectPath());
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignRunConfigurationShortcutAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignRunConfigurationShortcutAction.java
new file mode 100644 (file)
index 0000000..191d9af
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.action.ExternalSystemAction;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.RunConfigurationNode;
+import com.intellij.openapi.keymap.impl.ui.EditKeymapsDialog;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemKeymapExtension.getActionPrefix;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/28/2014
+ */
+public class AssignRunConfigurationShortcutAction extends ExternalSystemAction {
+
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return false;
+    return selectedNodes.get(0) instanceof RunConfigurationNode;
+  }
+
+  @Override
+  public void actionPerformed(@NotNull AnActionEvent e) {
+    Project project = getProject(e);
+    assert project != null;
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1 || !(selectedNodes.get(0) instanceof RunConfigurationNode)) return;
+
+    RunnerAndConfigurationSettings settings = ((RunConfigurationNode)selectedNodes.get(0)).getSettings();
+    assert settings != null;
+
+    ExternalSystemRunConfiguration runConfiguration = (ExternalSystemRunConfiguration)settings.getConfiguration();
+    String actionIdPrefix = getActionPrefix(project, runConfiguration.getSettings().getExternalProjectPath());
+    String actionId = actionIdPrefix + settings.getName();
+    if (ActionManager.getInstance().getAction(actionId) != null) {
+      new EditKeymapsDialog(project, actionId).show();
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignShortcutAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignShortcutAction.java
new file mode 100644 (file)
index 0000000..76208ed
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.action.ExternalSystemNodeAction;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemShortcutsManager;
+import com.intellij.openapi.keymap.impl.ui.EditKeymapsDialog;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/28/2014
+ */
+public class AssignShortcutAction extends ExternalSystemNodeAction<TaskData> {
+
+  public AssignShortcutAction() {
+    super(TaskData.class);
+  }
+
+  @Override
+  protected void perform(@NotNull Project project,
+                         @NotNull ProjectSystemId projectSystemId,
+                         @NotNull TaskData taskData,
+                         @NotNull AnActionEvent e) {
+    final ExternalSystemShortcutsManager shortcutsManager = ExternalProjectsManager.getInstance(project).getShortcutsManager();
+    String actionId = shortcutsManager.getActionId(taskData.getLinkedExternalProjectPath(), taskData.getName());
+    if (actionId != null) {
+      new EditKeymapsDialog(project, actionId).show();
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/GroupTasksAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/GroupTasksAction.java
new file mode 100644 (file)
index 0000000..a538267
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.action.ExternalSystemViewGearAction;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/31/2014
+ */
+public class GroupTasksAction extends ExternalSystemViewGearAction {
+  @Override
+  protected boolean isSelected(@NotNull ExternalProjectsView view) {
+    return view.getGroupTasks();
+  }
+
+  @Override
+  protected void setSelected(@NotNull ExternalProjectsView view, boolean value) {
+    view.setGroupTasks(value);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/RunExternalSystemTaskAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/RunExternalSystemTaskAction.java
new file mode 100644 (file)
index 0000000..597402a
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.externalSystem.action.ExternalSystemActionUtil;
+import com.intellij.openapi.externalSystem.action.ExternalSystemNodeAction;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/27/2014
+ */
+public class RunExternalSystemTaskAction extends ExternalSystemNodeAction<TaskData> {
+
+  public RunExternalSystemTaskAction() {
+    super(TaskData.class);
+  }
+
+  @Override
+  protected void perform(@NotNull Project project,
+                         @NotNull ProjectSystemId projectSystemId,
+                         @NotNull TaskData taskData,
+                         @NotNull AnActionEvent e) {
+    final ExternalTaskExecutionInfo taskExecutionInfo = ExternalSystemActionUtil.buildTaskInfo(taskData);
+    ExternalSystemUtil.runTask(taskExecutionInfo.getSettings(), taskExecutionInfo.getExecutorId(), project, projectSystemId);
+
+    final DataContext dataContext = e.getDataContext();
+    final ConfigurationContext context = ConfigurationContext.getFromContext(dataContext);
+    RunnerAndConfigurationSettings configuration = context.findExisting();
+    RunManagerEx runManager = (RunManagerEx)context.getRunManager();
+    if (configuration == null) {
+      configuration = context.getConfiguration();
+      if (configuration == null) {
+        return;
+      }
+      runManager.setTemporaryConfiguration(configuration);
+    }
+    runManager.setSelectedConfiguration(configuration);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterCompileTasksAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterCompileTasksAction.java
new file mode 100644 (file)
index 0000000..a01a104
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleAfterCompileTasksAction extends ToggleTaskActivationAction {
+  protected ToggleAfterCompileTasksAction() {
+    super(ExternalSystemTaskActivator.Phase.AFTER_COMPILE);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterRebuildTasksAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterRebuildTasksAction.java
new file mode 100644 (file)
index 0000000..fbbd52b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleAfterRebuildTasksAction extends ToggleTaskActivationAction {
+  protected ToggleAfterRebuildTasksAction() {
+    super(ExternalSystemTaskActivator.Phase.AFTER_REBUILD);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterSyncTaskAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleAfterSyncTaskAction.java
new file mode 100644 (file)
index 0000000..60fc873
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleAfterSyncTaskAction extends ToggleTaskActivationAction {
+  protected ToggleAfterSyncTaskAction() {
+    super(ExternalSystemTaskActivator.Phase.AFTER_SYNC);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeCompileTasksAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeCompileTasksAction.java
new file mode 100644 (file)
index 0000000..b19620a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleBeforeCompileTasksAction extends ToggleTaskActivationAction {
+  protected ToggleBeforeCompileTasksAction() {
+    super(ExternalSystemTaskActivator.Phase.BEFORE_COMPILE);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRebuildTasksAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRebuildTasksAction.java
new file mode 100644 (file)
index 0000000..c0403c7
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleBeforeRebuildTasksAction extends ToggleTaskActivationAction {
+  protected ToggleBeforeRebuildTasksAction() {
+    super(ExternalSystemTaskActivator.Phase.BEFORE_REBUILD);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRunTaskAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleBeforeRunTaskAction.java
new file mode 100644 (file)
index 0000000..6bef367
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/29/2014
+ */
+public class ToggleBeforeRunTaskAction extends ToggleTaskActivationAction {
+  protected ToggleBeforeRunTaskAction() {
+    super(null);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleTaskActivationAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleTaskActivationAction.java
new file mode 100644 (file)
index 0000000..1ca9732
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2014 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.action.task;
+
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.action.ExternalSystemToggleAction;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.RunConfigurationNode;
+import com.intellij.openapi.externalSystem.view.TaskNode;
+import com.intellij.util.SmartList;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator.RUN_CONFIGURATION_TASK_PREFIX;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/28/2014
+ */
+public abstract class ToggleTaskActivationAction extends ExternalSystemToggleAction {
+
+  private final ExternalSystemTaskActivator.Phase myPhase;
+
+  protected ToggleTaskActivationAction(ExternalSystemTaskActivator.Phase phase) {
+    myPhase = phase;
+  }
+
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    return super.isEnabled(e) && !getTasks(e).isEmpty();
+  }
+
+  @Override
+  protected boolean doIsSelected(AnActionEvent e) {
+    return hasTask(getTaskActivator(e), getTasks(e).get(0));
+  }
+
+  @Override
+  public void setSelected(AnActionEvent e, boolean state) {
+    List<TaskData> tasks = getTasks(e);
+    if (state) {
+      addTasks(getTaskActivator(e), tasks);
+    }
+    else {
+      removeTasks(getTaskActivator(e), tasks);
+    }
+  }
+
+  @NotNull
+  private static List<TaskData> getTasks(AnActionEvent e) {
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null) return Collections.emptyList();
+
+    List<TaskData> tasks = new SmartList<TaskData>();
+    for (ExternalSystemNode node : selectedNodes) {
+      if (node instanceof TaskNode) {
+        tasks.add((TaskData)node.getData());
+      }
+      else if (node instanceof RunConfigurationNode) {
+        final RunnerAndConfigurationSettings configurationSettings = ((RunConfigurationNode)node).getSettings();
+        final ExternalSystemRunConfiguration runConfiguration = (ExternalSystemRunConfiguration)configurationSettings.getConfiguration();
+        final ExternalSystemTaskExecutionSettings taskExecutionSettings = runConfiguration.getSettings();
+        tasks.add(new TaskData(taskExecutionSettings.getExternalSystemId(), RUN_CONFIGURATION_TASK_PREFIX + configurationSettings.getName(),
+                               taskExecutionSettings.getExternalProjectPath(), null));
+      }
+      else {
+        return Collections.emptyList();
+      }
+    }
+    return tasks;
+  }
+
+  protected boolean hasTask(ExternalSystemTaskActivator manager, TaskData taskData) {
+    if (taskData == null) return false;
+    return manager.isTaskOfPhase(taskData, myPhase);
+  }
+
+  private void addTasks(ExternalSystemTaskActivator taskActivator, List<TaskData> tasks) {
+    taskActivator.addTasks(tasks, myPhase);
+  }
+
+  private void removeTasks(ExternalSystemTaskActivator taskActivator, List<TaskData> tasks) {
+    taskActivator.removeTasks(tasks, myPhase);
+  }
+
+
+  private ExternalSystemTaskActivator getTaskActivator(AnActionEvent e) {
+    return ExternalProjectsManager.getInstance(getProject(e)).getTaskActivator();
+  }
+}
index 74295376e59f4c195f7cde1d04a95b1bc429212b..23ba428c35a355178dde7b415a78236aa3aff55b 100644 (file)
@@ -17,27 +17,29 @@ package com.intellij.openapi.externalSystem.model;
 
 import com.intellij.notification.NotificationGroup;
 import com.intellij.openapi.actionSystem.DataKey;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
-import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemRecentTasksList;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemTasksTreeModel;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
 import com.intellij.openapi.util.Key;
 import org.jetbrains.annotations.NotNull;
 
+import javax.swing.*;
+import java.util.List;
+
 /**
  * @author Denis Zhdanov
  * @since 2/7/12 11:19 AM
  */
 public class ExternalSystemDataKeys {
 
-  @NotNull public static final DataKey<ProjectSystemId>              EXTERNAL_SYSTEM_ID = DataKey.create("external.system.id");
-  @NotNull public static final DataKey<NotificationGroup>            NOTIFICATION_GROUP = DataKey.create("external.system.notification");
-  @NotNull public static final DataKey<ExternalSystemTasksTreeModel> ALL_TASKS_MODEL    = DataKey.create("external.system.all.tasks.model");
-  @NotNull public static final DataKey<ExternalTaskPojo>             SELECTED_TASK      = DataKey.create("external.system.selected.task");
-  @NotNull public static final DataKey<ExternalProjectPojo>          SELECTED_PROJECT   = DataKey.create("external.system.selected.project");
-
-  @NotNull public static final DataKey<ExternalSystemRecentTasksList> RECENT_TASKS_LIST
-    = DataKey.create("external.system.recent.tasks.list");
+  @NotNull public static final DataKey<ProjectSystemId> EXTERNAL_SYSTEM_ID = DataKey.create("external.system.id");
+  @NotNull public static final DataKey<NotificationGroup> NOTIFICATION_GROUP = DataKey.create("external.system.notification");
+  @NotNull public static final DataKey<ExternalProjectsView> VIEW = DataKey.create("external.system.view");
+  @NotNull public static final DataKey<ProjectNode> SELECTED_PROJECT_NODE = DataKey.create("external.system.selected.project.node");
+  @NotNull public static final DataKey<List<ExternalSystemNode>> SELECTED_NODES = DataKey.create("external.system.selected.nodes");
+  @NotNull public static final DataKey<ExternalSystemUiAware> UI_AWARE = DataKey.create("external.system.ui.aware");
+  @NotNull public static final DataKey<JTree> PROJECTS_TREE = DataKey.create("external.system.tree");
 
   @NotNull public static final Key<Boolean> NEWLY_IMPORTED_PROJECT = new Key<Boolean>("external.system.newly.imported");
   @NotNull public static final Key<Boolean> NEWLY_CREATED_PROJECT = new Key<Boolean>("external.system.newly.created");
index f7040a224c3f74e8ff7c41690a301f69ec1005ab..1c9afb70f45e7f5dfc504ab5fcbc862c8e0bd4a0 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder;
 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
 import com.intellij.openapi.externalSystem.service.project.ProjectRenameAware;
 import com.intellij.openapi.externalSystem.service.project.autoimport.ExternalSystemAutoImporter;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.service.ui.ExternalToolWindowManager;
 import com.intellij.openapi.externalSystem.service.vcs.ExternalSystemVcsRegistrar;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
@@ -45,6 +46,8 @@ public class ExternalSystemStartupActivity implements StartupActivity {
       @SuppressWarnings("unchecked")
       @Override
       public void run() {
+        ExternalProjectsManager.getInstance(project).init();
+
         for (ExternalSystemManager<?, ?, ?, ?, ?> manager : ExternalSystemApiUtil.getAllManagers()) {
           if (manager instanceof StartupActivity) {
             ((StartupActivity)manager).runActivity(project);
@@ -52,7 +55,8 @@ public class ExternalSystemStartupActivity implements StartupActivity {
         }
         if (project.getUserData(ExternalSystemDataKeys.NEWLY_IMPORTED_PROJECT) != Boolean.TRUE) {
           for (ExternalSystemManager manager : ExternalSystemManager.EP_NAME.getExtensions()) {
-            if (project.getUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT) == Boolean.TRUE) {
+            final boolean isNewProject = project.getUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT) == Boolean.TRUE;
+            if (isNewProject) {
               ExternalSystemUtil.refreshProjects(
                 new ImportSpecBuilder(project, manager.getSystemId())
               );
index 3eb1dd6f4420313ae495cf8d7d199a856d895d32..1ce056784648ced50af70d132f9bc5b6b76773b5 100644 (file)
@@ -5,27 +5,23 @@ import com.intellij.execution.configurations.ConfigurationType;
 import com.intellij.execution.configurations.RunConfiguration;
 import com.intellij.openapi.externalSystem.ExternalSystemManager;
 import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
 import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.service.ui.DefaultExternalSystemUiAware;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
-import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.NotNullLazyValue;
-import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.io.File;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Basic run configuration type for external system tasks.
@@ -133,35 +129,16 @@ public abstract class AbstractExternalSystemTaskConfigurationType implements Con
                                     @Nullable String externalProjectPath,
                                     @NotNull List<String> taskNames,
                                     @Nullable String executionName) {
-    ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
-    assert manager != null;
-    AbstractExternalSystemSettings<?, ?,?> s = manager.getSettingsProvider().fun(project);
-    Map<String/* project dir path */, String/* project file path */> rootProjectPaths = ContainerUtilRt.newHashMap();
-    for (ExternalProjectSettings projectSettings : s.getLinkedProjectsSettings()) {
-      String path = projectSettings.getExternalProjectPath();
-      if(path == null) continue;
-      final File rootProjectPathFile = new File(path).getParentFile();
-      if(rootProjectPathFile == null) continue;
-      rootProjectPaths.put(rootProjectPathFile.getAbsolutePath(), path);
-    }
-    
+
     String rootProjectPath = null;
     if (externalProjectPath != null) {
-      if (!rootProjectPaths.containsKey(externalProjectPath)) {
-        for (File f = new File(externalProjectPath), prev = null;
-             f != null && !FileUtil.filesEqual(f, prev);
-             prev = f, f = f.getParentFile())
-        {
-          rootProjectPath = rootProjectPaths.get(f.getAbsolutePath());
-          if (rootProjectPath != null) {
-            break;
-          }
-        }
+      final ExternalProjectInfo projectInfo = ExternalSystemUtil.getExternalProjectInfo(project, externalSystemId, externalProjectPath);
+      if (projectInfo != null) {
+        rootProjectPath = projectInfo.getExternalProjectPath();
       }
     }
-    
+
     StringBuilder buffer = new StringBuilder();
-    
     final String projectName;
     if (rootProjectPath == null) {
       projectName = null;
@@ -171,24 +148,23 @@ public abstract class AbstractExternalSystemTaskConfigurationType implements Con
     }
     if (!StringUtil.isEmptyOrSpaces(projectName)) {
       buffer.append(projectName);
-      buffer.append(" ");
+      buffer.append(' ');
     } else {
       buffer.append(externalProjectPath);
-      buffer.append(" ");
+      buffer.append(' ');
     }
 
-    buffer.append("[");
+    buffer.append('[');
     if (!StringUtil.isEmpty(executionName)) {
       buffer.append(executionName);
     }
     else if (!taskNames.isEmpty()) {
       for (String taskName : taskNames) {
-        buffer.append(taskName).append(" ");
+        buffer.append(taskName).append(' ');
       }
       buffer.setLength(buffer.length() - 1);
     }
-
-    buffer.append("]");
+    buffer.append(']');
 
     return buffer.toString();
   }
index 79b362e1e1e46b0b27f40a1a5c13815ec36dd2f0..81769681771db5206f4c8b978e5876ddd36288a6 100644 (file)
@@ -1,15 +1,12 @@
 package com.intellij.openapi.externalSystem.service.project;
 
-import com.intellij.openapi.externalSystem.model.project.LibraryData;
-import com.intellij.openapi.externalSystem.model.project.ModuleData;
-import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
+import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.*;
 import com.intellij.openapi.roots.libraries.Library;
 import com.intellij.openapi.roots.libraries.LibraryTable;
-import com.intellij.openapi.util.io.FileUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -81,4 +78,23 @@ public class ProjectStructureHelper {
     }
     return null;
   }
+
+  @Nullable
+  public OrderEntry findIdeModuleOrderEntry(LibraryDependencyData data, Project project) {
+    Module ownerIdeModule = findIdeModule(data.getOwnerModule(), project);
+    if (ownerIdeModule == null) return null;
+
+    ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(ownerIdeModule);
+
+    for (OrderEntry entry : moduleRootManager.getOrderEntries()) {
+      if (entry instanceof LibraryOrderEntry) {
+        if (((LibraryOrderEntry)entry).isModuleLevel() && data.getLevel() != LibraryLevel.MODULE) continue;
+      }
+
+      if (data.getInternalName().equals(entry.getPresentableName())) {
+        return entry;
+      }
+    }
+    return null;
+  }
 }
index fb4102adc1605c6ee04533a4219e625538818845..4b487ca55232cce52a0700d2a0ce4368ae9e87e2 100644 (file)
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
+import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.components.SettingsSavingComponent;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.ExternalSystemManager;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.model.internal.InternalExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.module.ModuleTypeId;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.Consumer;
+import com.intellij.util.Function;
+import com.intellij.util.SmartList;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.io.*;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * @author Vladislav.Soroka
@@ -38,11 +56,14 @@ import java.util.Map;
 public class ExternalProjectsDataStorage implements SettingsSavingComponent {
   private static final Logger LOG = Logger.getInstance(ExternalProjectsDataStorage.class);
 
+  private static final String STORAGE_VERSION = ExternalProjectsDataStorage.class.getSimpleName() + ".1";
+
   @NotNull
   private final Project myProject;
   @NotNull
-  private final Map<Pair<ProjectSystemId, String>, InternalExternalProjectInfo> myExternalRootProjects =
-    ContainerUtil.newConcurrentMap();
+  private final Map<Pair<ProjectSystemId, String>, InternalExternalProjectInfo> myExternalRootProjects = ContainerUtil.newConcurrentMap();
+
+  private final AtomicBoolean changed = new AtomicBoolean();
 
   public static ExternalProjectsDataStorage getInstance(@NotNull Project project) {
     return ServiceManager.getService(project, ExternalProjectsDataStorage.class);
@@ -52,16 +73,50 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     myProject = project;
   }
 
-  public void load() {
-    // TODO [Vlad] load data for the opened project
+  public synchronized void load() {
+    myExternalRootProjects.clear();
+    try {
+      final Collection<InternalExternalProjectInfo> projectInfos = load(myProject);
+      for (InternalExternalProjectInfo projectInfo : projectInfos) {
+        if (validate(projectInfo)) {
+          myExternalRootProjects.put(Pair.create(projectInfo.getProjectSystemId(), projectInfo.getExternalProjectPath()), projectInfo);
+        }
+      }
+    }
+    catch (IOException e) {
+      LOG.debug(e);
+    }
+
+    mergeLocalSettings();
+  }
+
+  private static boolean validate(InternalExternalProjectInfo externalProjectInfo) {
+    try {
+      final DataNode<ProjectData> projectStructure = externalProjectInfo.getExternalProjectStructure();
+      if (projectStructure == null) return false;
+
+      ProjectDataManager.getInstance().ensureTheDataIsReadyToUse(projectStructure);
+      return externalProjectInfo.getExternalProjectPath().equals(projectStructure.getData().getLinkedExternalProjectPath());
+    }
+    catch (Exception e) {
+      LOG.warn(e);
+    }
+    return false;
   }
 
   @Override
-  public void save() {
-    // TODO [Vlad] save data if changed
+  public synchronized void save() {
+    if (!changed.compareAndSet(true, false)) return;
+
+    try {
+      doSave(myProject, myExternalRootProjects.values());
+    }
+    catch (IOException e) {
+      LOG.debug(e);
+    }
   }
 
-  void add(@NotNull ExternalProjectInfo externalProjectInfo) {
+  synchronized void update(@NotNull ExternalProjectInfo externalProjectInfo) {
     final ProjectSystemId projectSystemId = externalProjectInfo.getProjectSystemId();
     final String projectPath = externalProjectInfo.getExternalProjectPath();
     DataNode<ProjectData> externalProjectStructure = externalProjectInfo.getExternalProjectStructure();
@@ -88,10 +143,172 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     merged.setLastImportTimestamp(lastImportTimestamp);
     merged.setLastSuccessfulImportTimestamp(lastSuccessfulImportTimestamp);
     myExternalRootProjects.put(key, merged);
+
+    changed.set(true);
   }
 
   @Nullable
-  ExternalProjectInfo get(@NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) {
+  synchronized ExternalProjectInfo get(@NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) {
     return myExternalRootProjects.get(Pair.create(projectSystemId, externalProjectPath));
   }
+
+  synchronized void remove(@NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) {
+    final InternalExternalProjectInfo removed = myExternalRootProjects.remove(Pair.create(projectSystemId, externalProjectPath));
+    if(removed != null) {
+      changed.set(true);
+    }
+  }
+
+  @NotNull
+  synchronized Collection<ExternalProjectInfo> list(@NotNull final ProjectSystemId projectSystemId) {
+    return ContainerUtil.mapNotNull(myExternalRootProjects.values(), new Function<InternalExternalProjectInfo, ExternalProjectInfo>() {
+      @Override
+      public ExternalProjectInfo fun(InternalExternalProjectInfo info) {
+        return projectSystemId.equals(info.getProjectSystemId()) ? info : null;
+      }
+    });
+  }
+
+  private void mergeLocalSettings() {
+    for (ExternalSystemManager<?, ?, ?, ?, ?> manager : ExternalSystemApiUtil.getAllManagers()) {
+      final ProjectSystemId systemId = manager.getSystemId();
+
+      AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(myProject);
+      final Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> availableProjects = settings.getAvailableProjects();
+      final Map<String, Collection<ExternalTaskPojo>> availableTasks = settings.getAvailableTasks();
+
+      for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> entry : availableProjects.entrySet()) {
+        final ExternalProjectPojo projectPojo = entry.getKey();
+        final String externalProjectPath = projectPojo.getPath();
+        final Pair<ProjectSystemId, String> key = Pair.create(systemId, externalProjectPath);
+        InternalExternalProjectInfo externalProjectInfo = myExternalRootProjects.get(key);
+        if (externalProjectInfo == null) {
+          final DataNode<ProjectData> dataNode = convert(systemId, projectPojo, entry.getValue(), availableTasks);
+          externalProjectInfo = new InternalExternalProjectInfo(systemId, externalProjectPath, dataNode);
+          myExternalRootProjects.put(key, externalProjectInfo);
+
+          changed.set(true);
+        }
+      }
+    }
+  }
+
+  private static DataNode<ProjectData> convert(@NotNull ProjectSystemId systemId,
+                                               @NotNull ExternalProjectPojo rootProject,
+                                               @NotNull Collection<ExternalProjectPojo> childProjects,
+                                               @NotNull Map<String, Collection<ExternalTaskPojo>> availableTasks) {
+    ProjectData projectData = new ProjectData(systemId, rootProject.getName(), rootProject.getPath(), rootProject.getPath());
+    DataNode<ProjectData> projectDataNode = new DataNode<ProjectData>(ProjectKeys.PROJECT, projectData, null);
+
+    for (ExternalProjectPojo childProject : childProjects) {
+      String moduleConfigPath = childProject.getPath();
+      ModuleData moduleData = new ModuleData(childProject.getName(), systemId,
+                                             ModuleTypeId.JAVA_MODULE, childProject.getName(),
+                                             moduleConfigPath, moduleConfigPath);
+      final DataNode<ModuleData> moduleDataNode = projectDataNode.createChild(ProjectKeys.MODULE, moduleData);
+
+      final Collection<ExternalTaskPojo> moduleTasks = availableTasks.get(moduleConfigPath);
+      if (moduleTasks != null) {
+        for (ExternalTaskPojo moduleTask : moduleTasks) {
+          TaskData taskData = new TaskData(systemId, moduleTask.getName(), moduleConfigPath, moduleTask.getDescription());
+          moduleDataNode.createChild(ProjectKeys.TASK, taskData);
+        }
+      }
+    }
+
+    return projectDataNode;
+  }
+
+  private static void doSave(@NotNull Project project, @NotNull Collection<InternalExternalProjectInfo> externalProjects) throws IOException {
+    final File projectConfigurationFile = getProjectConfigurationFile(project);
+    if (!FileUtil.createParentDirs(projectConfigurationFile)) {
+      throw new IOException("Unable to save " + projectConfigurationFile);
+    }
+
+    for (Iterator<InternalExternalProjectInfo> iterator = externalProjects.iterator(); iterator.hasNext(); ) {
+      InternalExternalProjectInfo externalProject = iterator.next();
+      if (!validate(externalProject)) {
+        iterator.remove();
+        continue;
+      }
+
+      ExternalSystemApiUtil.visit(externalProject.getExternalProjectStructure(), new Consumer<DataNode>() {
+        @Override
+        public void consume(DataNode dataNode) {
+          try {
+            dataNode.getDataBytes();
+          }
+          catch (IOException e) {
+            dataNode.clear(true);
+          }
+        }
+      });
+    }
+
+    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(projectConfigurationFile)));
+    try {
+      out.writeUTF(STORAGE_VERSION);
+      out.writeInt(externalProjects.size());
+      ObjectOutputStream os = new ObjectOutputStream(out);
+      try {
+        for (InternalExternalProjectInfo externalProject : externalProjects) {
+          os.writeObject(externalProject);
+        }
+      }
+      finally {
+        os.close();
+      }
+    }
+    finally {
+      out.close();
+    }
+  }
+
+  @NotNull
+  private static Collection<InternalExternalProjectInfo> load(@NotNull Project project) throws IOException {
+    SmartList<InternalExternalProjectInfo> projects = new SmartList<InternalExternalProjectInfo>();
+    final File configurationFile = getProjectConfigurationFile(project);
+    if (!configurationFile.isFile()) return projects;
+
+    DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(configurationFile)));
+
+    try {
+      final String storage_version = in.readUTF();
+      if (!STORAGE_VERSION.equals(storage_version)) return projects;
+      final int size = in.readInt();
+
+      ObjectInputStream os = new ObjectInputStream(in);
+      try {
+        for (int i = 0; i < size; i++) {
+          //noinspection unchecked
+          InternalExternalProjectInfo projectDataDataNode = (InternalExternalProjectInfo)os.readObject();
+          projects.add(projectDataDataNode);
+        }
+      }
+      catch (ClassNotFoundException e) {
+        IOException ioException = new IOException();
+        ioException.initCause(e);
+        throw ioException;
+      }
+      finally {
+        os.close();
+      }
+    }
+    finally {
+      in.close();
+    }
+    return projects;
+  }
+
+  private static File getProjectConfigurationFile(@NotNull Project project) {
+    return new File(getProjectConfigurationDir(), project.getLocationHash() + "/project.dat");
+  }
+
+  private static File getProjectConfigurationDir() {
+    return getExternalBuildSystemDir("Projects");
+  }
+
+  private static File getExternalBuildSystemDir(String folder) {
+    return new File(PathManager.getSystemPath(), "external_build_system" + "/" + folder).getAbsoluteFile();
+  }
 }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsManager.java
new file mode 100644 (file)
index 0000000..b726533
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.externalSystem.ExternalSystemManager;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsViewState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.Function;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.intellij.openapi.externalSystem.model.ProjectKeys.TASK;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/23/2014
+ */
+@State(name = "ExternalProjectsManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)})
+public class ExternalProjectsManager implements PersistentStateComponent<ExternalProjectsState>, Disposable {
+
+  private final AtomicBoolean isInitialized = new AtomicBoolean();
+  @NotNull
+  private ExternalProjectsState myState = new ExternalProjectsState();
+
+  @NotNull
+  private final Project myProject;
+  private final ExternalSystemRunManagerListener myRunManagerListener;
+  private final ExternalSystemTaskActivator myTaskActivator;
+  private final ExternalSystemShortcutsManager myShortcutsManager;
+  private final List<ExternalProjectsView> myProjectsViews = new SmartList<ExternalProjectsView>();
+
+
+  public static ExternalProjectsManager getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ExternalProjectsManager.class);
+  }
+
+  public ExternalProjectsManager(@NotNull Project project) {
+    myProject = project;
+    Disposer.register(project, this);
+    myShortcutsManager = new ExternalSystemShortcutsManager(project);
+    Disposer.register(this, myShortcutsManager);
+    myTaskActivator = new ExternalSystemTaskActivator(project);
+    myRunManagerListener = new ExternalSystemRunManagerListener(this);
+  }
+
+  @NotNull
+  public Project getProject() {
+    return myProject;
+  }
+
+  public ExternalSystemShortcutsManager getShortcutsManager() {
+    return myShortcutsManager;
+  }
+
+  public ExternalSystemTaskActivator getTaskActivator() {
+    return myTaskActivator;
+  }
+
+  public void registerView(@NotNull ExternalProjectsView externalProjectsView) {
+    init();
+    myProjectsViews.add(externalProjectsView);
+    externalProjectsView.loadState(
+      myState.getExternalSystemsState().get(externalProjectsView.getSystemId().getId()).getProjectsViewState());
+    externalProjectsView.init();
+  }
+
+  @Nullable
+  public ExternalProjectsView getExternalProjectsView(@NotNull ProjectSystemId systemId) {
+    for (ExternalProjectsView projectsView : myProjectsViews) {
+      if(projectsView.getSystemId().equals(systemId)) return projectsView;
+    }
+    return null;
+  }
+
+  public void init() {
+    synchronized (isInitialized) {
+      if (isInitialized.getAndSet(true)) return;
+
+      // load external projects data
+      ExternalProjectsDataStorage.getInstance(myProject).load();
+      myRunManagerListener.attach();
+
+      // init shortcuts manager
+      myShortcutsManager.init();
+      for (ExternalSystemManager<?, ?, ?, ?, ?> systemManager : ExternalSystemApiUtil.getAllManagers()) {
+        final Collection<ExternalProjectInfo> externalProjects =
+          ExternalProjectsDataStorage.getInstance(myProject).list(systemManager.getSystemId());
+        for (ExternalProjectInfo externalProject : externalProjects) {
+          if (externalProject.getExternalProjectStructure() == null) continue;
+          Collection<DataNode<TaskData>> taskData =
+            ExternalSystemApiUtil.findAllRecursively(externalProject.getExternalProjectStructure(), TASK);
+          myShortcutsManager.scheduleKeymapUpdate(taskData);
+        }
+
+        if (!externalProjects.isEmpty()) {
+          myShortcutsManager.scheduleRunConfigurationKeymapUpdate(systemManager.getSystemId());
+        }
+      }
+      // init task activation info
+      myTaskActivator.init();
+    }
+  }
+
+  public void updateExternalProjectData(ExternalProjectInfo externalProject) {
+    // update external projects data
+    ExternalProjectsDataStorage.getInstance(myProject).update(externalProject);
+
+    // update shortcuts manager
+    if (externalProject.getExternalProjectStructure() != null) {
+      final ProjectData projectData = externalProject.getExternalProjectStructure().getData();
+
+      ExternalSystemUtil.scheduleExternalViewStructureUpdate(myProject, projectData.getOwner());
+
+      Collection<DataNode<TaskData>> taskData =
+        ExternalSystemApiUtil.findAllRecursively(externalProject.getExternalProjectStructure(), TASK);
+      myShortcutsManager.scheduleKeymapUpdate(taskData);
+      myShortcutsManager.scheduleRunConfigurationKeymapUpdate(projectData.getOwner());
+    }
+  }
+
+  public void forgetExternalProjectData(@NotNull ProjectSystemId projectSystemId, @NotNull String linkedProjectPath) {
+    ExternalProjectsDataStorage.getInstance(myProject).remove(projectSystemId, linkedProjectPath);
+  }
+
+  @NotNull
+  @Override
+  public ExternalProjectsState getState() {
+    ApplicationManager.getApplication().assertIsDispatchThread();
+    for (ExternalProjectsView externalProjectsView : myProjectsViews) {
+      final ExternalProjectsViewState externalProjectsViewState = externalProjectsView.getState();
+      final ExternalProjectsState.State state = myState.getExternalSystemsState().get(externalProjectsView.getSystemId().getId());
+      assert state != null;
+      state.setProjectsViewState(externalProjectsViewState);
+    }
+    return myState;
+  }
+
+  @NotNull
+  public ExternalProjectsStateProvider getStateProvider() {
+    return new ExternalProjectsStateProvider() {
+      @Override
+      public List<TasksActivation> getAllTasksActivation() {
+        List<TasksActivation> result = new SmartList<TasksActivation>();
+        for (Map.Entry<String, ExternalProjectsState.State> systemState : myState.getExternalSystemsState().entrySet()) {
+          ProjectSystemId systemId = new ProjectSystemId(systemState.getKey());
+          for (Map.Entry<String, TaskActivationState> activationStateEntry : systemState.getValue().getExternalSystemsTaskActivation()
+            .entrySet()) {
+            result.add(new TasksActivation(systemId, activationStateEntry.getKey(), activationStateEntry.getValue()));
+          }
+        }
+
+        return result;
+      }
+
+      @Override
+      public List<TasksActivation> getTasksActivation(@NotNull final ProjectSystemId systemId) {
+        final Set<Map.Entry<String, TaskActivationState>> entries =
+          myState.getExternalSystemsState().get(systemId.getId()).getExternalSystemsTaskActivation().entrySet();
+        return ContainerUtil.map(entries, new Function<Map.Entry<String, TaskActivationState>, TasksActivation>() {
+          @Override
+          public TasksActivation fun(Map.Entry<String, TaskActivationState> entry) {
+            return new TasksActivation(systemId, entry.getKey(), entry.getValue());
+          }
+        });
+      }
+
+      @Override
+      public TaskActivationState getTasksActivation(@NotNull ProjectSystemId systemId, @NotNull String projectPath) {
+        return myState.getExternalSystemsState().get(systemId.getId()).getExternalSystemsTaskActivation().get(projectPath);
+      }
+    };
+  }
+
+  @Override
+  public void loadState(ExternalProjectsState state) {
+    myState = state == null ? new ExternalProjectsState() : state;
+  }
+
+  @Override
+  public void dispose() {
+    myProjectsViews.clear();
+    myRunManagerListener.detach();
+  }
+
+  public interface ExternalProjectsStateProvider {
+    class TasksActivation {
+      public final ProjectSystemId systemId;
+      public final String projectPath;
+      public final TaskActivationState state;
+
+      public TasksActivation(ProjectSystemId systemId,
+                             String projectPath,
+                             TaskActivationState state) {
+        this.systemId = systemId;
+        this.projectPath = projectPath;
+        this.state = state;
+      }
+    }
+
+    List<TasksActivation> getAllTasksActivation();
+
+    List<TasksActivation> getTasksActivation(@NotNull ProjectSystemId systemId);
+
+    TaskActivationState getTasksActivation(@NotNull ProjectSystemId systemId, @NotNull String projectPath);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsState.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsState.java
new file mode 100644 (file)
index 0000000..3a05326
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.openapi.externalSystem.view.ExternalProjectsViewState;
+import com.intellij.util.containers.FactoryMap;
+import com.intellij.util.xmlb.annotations.MapAnnotation;
+import com.intellij.util.xmlb.annotations.Property;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/23/2014
+ */
+public class ExternalProjectsState {
+
+
+  private final Map<String, State> myExternalSystemsState = new NullSafeMap<String, State>() {
+    @Nullable
+    @Override
+    protected State create(String key) {
+      return new State();
+    }
+  };
+
+  @Property(surroundWithTag = false)
+  @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
+    keyAttributeName = "id", entryTagName = "system")
+  public Map<String, State> getExternalSystemsState() {
+    return myExternalSystemsState;
+  }
+
+  @SuppressWarnings("UnusedDeclaration")
+  public void setExternalSystemsState(Map<String, State> externalSystemsState) {
+  }
+
+
+  @Tag("state")
+  public static class State {
+    private ExternalProjectsViewState projectsViewState = new ExternalProjectsViewState();
+
+    private final Map<String, TaskActivationState> myExternalSystemsTaskActivation = new FactoryMap<String, TaskActivationState>() {
+      @Nullable
+      @Override
+      protected TaskActivationState create(String key) {
+        return new TaskActivationState();
+      }
+
+      @Override
+      public TaskActivationState put(String key, TaskActivationState value) {
+        if(value == null) return null;
+        return super.put(key, value);
+      }
+    };
+
+    @Property(surroundWithTag = false)
+    @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
+      keyAttributeName = "path", entryTagName = "task", sortBeforeSave = false)
+    public Map<String, TaskActivationState> getExternalSystemsTaskActivation() {
+      return myExternalSystemsTaskActivation;
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    public void setExternalSystemsTaskActivation(Map<String, TaskActivationState> externalSystemsTaskActivation) {
+    }
+
+    @Property(surroundWithTag = false)
+    public ExternalProjectsViewState getProjectsViewState() {
+      return projectsViewState;
+    }
+
+    public void setProjectsViewState(ExternalProjectsViewState projectsViewState) {
+      this.projectsViewState = projectsViewState;
+    }
+  }
+
+  public static abstract class NullSafeMap<K,V> extends FactoryMap<K,V> {
+    @Override
+    public V put(K key, V value) {
+      if(value == null) return null;
+      return super.put(key, value);
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java
new file mode 100644 (file)
index 0000000..1122cb2
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.execution.ProgramRunnerUtil;
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.action.ExternalSystemAction;
+import com.intellij.openapi.externalSystem.action.ExternalSystemActionUtil;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.keymap.KeymapExtension;
+import com.intellij.openapi.keymap.KeymapGroup;
+import com.intellij.openapi.keymap.KeymapGroupFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import gnu.trove.THashSet;
+import icons.ExternalSystemIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/27/2014
+ */
+public class ExternalSystemKeymapExtension implements KeymapExtension {
+
+  public KeymapGroup createGroup(Condition<AnAction> condition, Project project) {
+    KeymapGroup result = KeymapGroupFactory.getInstance().createGroup(
+      ExternalSystemBundle.message("external.system.keymap.group"), ExternalSystemIcons.TaskGroup);
+    if (project == null) return result;
+
+    MultiMap<Pair<ProjectSystemId, String>, String> projectToActionsMapping = MultiMap.create();
+
+    ActionManager actionManager = ActionManager.getInstance();
+    for (String eachId : actionManager.getActionIds(getActionPrefix(project, null))) {
+      AnAction eachAction = actionManager.getAction(eachId);
+
+      if (!(eachAction instanceof MyExternalSystemAction)) continue;
+      if (condition != null && !condition.value(actionManager.getActionOrStub(eachId))) continue;
+
+      MyExternalSystemAction taskAction = (MyExternalSystemAction)eachAction;
+      projectToActionsMapping.putValue(Pair.create(taskAction.getSystemId(), taskAction.getGroup()), eachId);
+    }
+
+    Map<ProjectSystemId, KeymapGroup> keymapGroupMap = ContainerUtil.newHashMap();
+    for (Pair<ProjectSystemId, String> pair : projectToActionsMapping.keySet()) {
+      if (!keymapGroupMap.containsKey(pair.first)) {
+        final Icon projectIcon = ExternalSystemUiUtil.getUiAware(pair.first).getProjectIcon();
+        KeymapGroup group = KeymapGroupFactory.getInstance().createGroup(pair.first.getReadableName(), projectIcon);
+        result.addGroup(group);
+        keymapGroupMap.put(pair.first, group);
+      }
+    }
+
+
+    for (Map.Entry<Pair<ProjectSystemId, String>, Collection<String>> each : projectToActionsMapping.entrySet()) {
+      String groupName = each.getKey().second;
+      Collection<String> tasks = each.getValue();
+      if (tasks.isEmpty()) continue;
+      KeymapGroup group = KeymapGroupFactory.getInstance().createGroup(groupName, ExternalSystemIcons.TaskGroup);
+
+      final KeymapGroup systemGroup = keymapGroupMap.get(each.getKey().first);
+      if (systemGroup != null) {
+        systemGroup.addGroup(group);
+      }
+      else {
+        result.addGroup(group);
+      }
+      for (String actionId : tasks) {
+        group.addActionId(actionId);
+      }
+    }
+
+    return result;
+  }
+
+  public static void updateActions(Project project, Collection<DataNode<TaskData>> taskData) {
+    clearActions(project, taskData);
+    createActions(project, taskData);
+  }
+
+  private static void createActions(Project project, Collection<DataNode<TaskData>> taskData) {
+    ActionManager manager = ActionManager.getInstance();
+    for (DataNode<TaskData> each : taskData) {
+      final DataNode<ModuleData> moduleData = ExternalSystemApiUtil.findParent(each, ProjectKeys.MODULE);
+      if (moduleData == null) continue;
+      ExternalSystemTaskAction eachAction = new ExternalSystemTaskAction(project, moduleData.getData().getInternalName(), each.getData());
+
+      manager.unregisterAction(eachAction.getId());
+      manager.registerAction(eachAction.getId(), eachAction);
+    }
+  }
+
+  public static void clearActions(Project project) {
+    ActionManager manager = ActionManager.getInstance();
+    for (String each : manager.getActionIds(getActionPrefix(project, null))) {
+      manager.unregisterAction(each);
+    }
+  }
+
+  public static void clearActions(Project project, Collection<DataNode<TaskData>> taskData) {
+    ActionManager manager = ActionManager.getInstance();
+    for (DataNode<TaskData> each : taskData) {
+      for (String eachAction : manager.getActionIds(getActionPrefix(project, each.getData().getLinkedExternalProjectPath()))) {
+        manager.unregisterAction(eachAction);
+      }
+    }
+  }
+
+  public static String getActionPrefix(@NotNull Project project, @Nullable String path) {
+    return ExternalProjectsManager.getInstance(project).getShortcutsManager().getActionId(path, null);
+  }
+
+  public static void updateRunConfigurationActions(Project project, ProjectSystemId systemId) {
+    final AbstractExternalSystemTaskConfigurationType configurationType = ExternalSystemUtil.findConfigurationType(systemId);
+    if (configurationType == null) return;
+
+    Set<RunnerAndConfigurationSettings> settings = new THashSet<RunnerAndConfigurationSettings>(
+      RunManager.getInstance(project).getConfigurationSettingsList(configurationType));
+
+    ActionManager manager = ActionManager.getInstance();
+    for (RunnerAndConfigurationSettings configurationSettings : settings) {
+      ExternalSystemRunConfigurationAction runConfigurationAction =
+        new ExternalSystemRunConfigurationAction(project, configurationSettings);
+      String id = runConfigurationAction.getId();
+      manager.unregisterAction(id);
+      manager.registerAction(id, runConfigurationAction);
+    }
+  }
+
+  private abstract static class MyExternalSystemAction extends ExternalSystemAction {
+    public abstract String getId();
+
+    public abstract String getGroup();
+
+    public abstract ProjectSystemId getSystemId();
+  }
+
+  private static class ExternalSystemTaskAction extends MyExternalSystemAction {
+    private final String myId;
+    private final String myGroup;
+    private final TaskData myTaskData;
+
+    public ExternalSystemTaskAction(Project project, String group, TaskData taskData) {
+      myGroup = group;
+      myTaskData = taskData;
+      myId = getActionPrefix(project, taskData.getLinkedExternalProjectPath()) + taskData.getName();
+
+      Presentation template = getTemplatePresentation();
+      template.setText(myTaskData.getName(), false);
+      template.setIcon(ExternalSystemIcons.Task);
+    }
+
+    @Override
+    protected boolean isEnabled(AnActionEvent e) {
+      return hasProject(e);
+    }
+
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      final ExternalTaskExecutionInfo taskExecutionInfo = ExternalSystemActionUtil.buildTaskInfo(myTaskData);
+      ExternalSystemUtil.runTask(
+        taskExecutionInfo.getSettings(), taskExecutionInfo.getExecutorId(), getProject(e), myTaskData.getOwner());
+    }
+
+    public TaskData getTaskData() {
+      return myTaskData;
+    }
+
+    public String toString() {
+      return myTaskData.toString();
+    }
+
+    @Override
+    public String getGroup() {
+      return myGroup;
+    }
+
+    @Override
+    public ProjectSystemId getSystemId() {
+      return myTaskData.getOwner();
+    }
+
+    @Override
+    public String getId() {
+      return myId;
+    }
+  }
+
+  private static class ExternalSystemRunConfigurationAction extends MyExternalSystemAction {
+    private final String myId;
+    private final String myGroup;
+    private final RunnerAndConfigurationSettings myConfigurationSettings;
+    private final ProjectSystemId systemId;
+
+    public ExternalSystemRunConfigurationAction(Project project, RunnerAndConfigurationSettings configurationSettings) {
+      myConfigurationSettings = configurationSettings;
+      ExternalSystemRunConfiguration runConfiguration = (ExternalSystemRunConfiguration)configurationSettings.getConfiguration();
+      systemId = runConfiguration.getSettings().getExternalSystemId();
+
+      ExternalSystemUiAware uiAware = ExternalSystemUiUtil.getUiAware(systemId);
+      myGroup = uiAware.getProjectRepresentationName(runConfiguration.getSettings().getExternalProjectPath(), null);
+      String actionIdPrefix = getActionPrefix(project, runConfiguration.getSettings().getExternalProjectPath());
+      myId = actionIdPrefix + configurationSettings.getName();
+
+      Presentation template = getTemplatePresentation();
+      template.setText(myConfigurationSettings.getName(), false);
+      template.setIcon(runConfiguration.getIcon());
+    }
+
+    @Override
+    protected boolean isEnabled(AnActionEvent e) {
+      return hasProject(e);
+    }
+
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      ProgramRunnerUtil.executeConfiguration(getProject(e), myConfigurationSettings, DefaultRunExecutor.getRunExecutorInstance());
+    }
+
+    public String toString() {
+      return myConfigurationSettings.toString();
+    }
+
+    @Override
+    public String getGroup() {
+      return myGroup;
+    }
+
+    @Override
+    public ProjectSystemId getSystemId() {
+      return systemId;
+    }
+
+    public String getId() {
+      return myId;
+    }
+  }
+}
+
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemRunManagerListener.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemRunManagerListener.java
new file mode 100644 (file)
index 0000000..3edc1ae
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunManagerAdapter;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.openapi.externalSystem.ExternalSystemManager;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator.*;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/14/2014
+ */
+class ExternalSystemRunManagerListener extends RunManagerAdapter {
+
+  private ExternalProjectsManager myManager;
+  private final Map<Integer, Pair<String, RunnerAndConfigurationSettings>> myMap;
+
+  public ExternalSystemRunManagerListener(ExternalProjectsManager manager) {
+    myManager = manager;
+    myMap = ContainerUtil.newConcurrentMap();
+  }
+
+  @Override
+  public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) {
+    add(myMap, settings);
+  }
+
+  @Override
+  public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) {
+    if (settings.getConfiguration() instanceof ExternalSystemRunConfiguration) {
+      final Pair<String, RunnerAndConfigurationSettings> pair = myMap.remove(System.identityHashCode(settings));
+      if (pair == null) return;
+
+      final ExternalProjectsManager.ExternalProjectsStateProvider stateProvider = myManager.getStateProvider();
+      final ExternalSystemTaskExecutionSettings taskExecutionSettings =
+        ((ExternalSystemRunConfiguration)settings.getConfiguration()).getSettings();
+
+      if(taskExecutionSettings.getExternalProjectPath() == null) return;
+
+      final TaskActivationState activation =
+        stateProvider.getTasksActivation(taskExecutionSettings.getExternalSystemId(), taskExecutionSettings.getExternalProjectPath());
+
+      for (Phase phase : Phase.values()) {
+        for (Iterator<String> iterator = activation.getTasks(phase).iterator(); iterator.hasNext(); ) {
+          String task = iterator.next();
+          if (pair.first.equals(task)) {
+            iterator.remove();
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void runConfigurationChanged(@NotNull RunnerAndConfigurationSettings settings) {
+    if (settings.getConfiguration() instanceof ExternalSystemRunConfiguration) {
+      final Pair<String, RunnerAndConfigurationSettings> pair = myMap.get(System.identityHashCode(settings));
+      if (pair != null) {
+        final ExternalProjectsManager.ExternalProjectsStateProvider stateProvider = myManager.getStateProvider();
+        final ExternalSystemTaskExecutionSettings taskExecutionSettings =
+          ((ExternalSystemRunConfiguration)settings.getConfiguration()).getSettings();
+
+        if(taskExecutionSettings.getExternalProjectPath() == null) return;
+
+        final TaskActivationState activation =
+          stateProvider.getTasksActivation(taskExecutionSettings.getExternalSystemId(), taskExecutionSettings.getExternalProjectPath());
+
+        for (Phase phase : Phase.values()) {
+          final Set<String> modifiableActivationTasks = activation.getTasks(phase);
+          for (String task : ContainerUtil.newTroveSet(modifiableActivationTasks)) {
+            if (pair.first.equals(task)) {
+              modifiableActivationTasks.remove(task);
+              final String runConfigurationActivationTaskName = getRunConfigurationActivationTaskName(settings);
+              modifiableActivationTasks.add(runConfigurationActivationTaskName);
+              myMap.put(System.identityHashCode(settings), Pair.create(runConfigurationActivationTaskName, settings));
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public void attach() {
+    myMap.clear();
+
+    for (ExternalSystemManager<?, ?, ?, ?, ?> systemManager : ExternalSystemApiUtil.getAllManagers()) {
+      final AbstractExternalSystemTaskConfigurationType configurationType =
+        ExternalSystemUtil.findConfigurationType(systemManager.getSystemId());
+      if (configurationType == null) continue;
+      final List<RunnerAndConfigurationSettings> configurationSettingsList =
+        RunManager.getInstance(myManager.getProject()).getConfigurationSettingsList(configurationType);
+      for (RunnerAndConfigurationSettings configurationSettings : configurationSettingsList) {
+        add(myMap, configurationSettings);
+      }
+    }
+
+    ((RunManagerEx)RunManager.getInstance(myManager.getProject())).addRunManagerListener(this);
+  }
+
+  public void detach() {
+    myMap.clear();
+    ((RunManagerEx)RunManager.getInstance(myManager.getProject())).removeRunManagerListener(this);
+  }
+
+  private static void add(@NotNull Map<Integer, Pair<String, RunnerAndConfigurationSettings>> map,
+                          @NotNull RunnerAndConfigurationSettings settings) {
+    if (settings.getConfiguration() instanceof ExternalSystemRunConfiguration) {
+      map.put(System.identityHashCode(settings), Pair.create(getRunConfigurationActivationTaskName(settings), settings));
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java
new file mode 100644 (file)
index 0000000..911b633
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.Shortcut;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.keymap.Keymap;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.keymap.KeymapManagerListener;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.keymap.ex.KeymapManagerEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/27/2014
+ */
+public class ExternalSystemShortcutsManager implements Disposable {
+
+  private static final String ACTION_ID_PREFIX = "ExternalSystem_";
+  @NotNull
+  private final Project myProject;
+  private ExternalSystemKeyMapListener myKeyMapListener;
+  private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
+
+  public ExternalSystemShortcutsManager(@NotNull Project project) {
+    myProject = project;
+  }
+
+  public void init() {
+    myKeyMapListener = new ExternalSystemKeyMapListener();
+  }
+
+  public String getActionId(@Nullable String projectPath, @Nullable String taskName) {
+    StringBuilder result = new StringBuilder(ACTION_ID_PREFIX);
+    result.append(myProject.getLocationHash());
+
+    if (projectPath != null) {
+      String portablePath = FileUtil.toSystemIndependentName(projectPath);
+      result.append(new File(portablePath).getParentFile().getName());
+      result.append(Integer.toHexString(portablePath.hashCode()));
+
+      if (taskName != null) result.append(taskName);
+    }
+
+    return result.toString();
+  }
+
+  public String getDescription(@Nullable String projectPath, @Nullable String taskName) {
+    String actionId = getActionId(projectPath, taskName);
+    if (actionId == null) return "";
+
+    Keymap activeKeymap = KeymapManager.getInstance().getActiveKeymap();
+    Shortcut[] shortcuts = activeKeymap.getShortcuts(actionId);
+    if (shortcuts == null || shortcuts.length == 0) return "";
+
+    return KeymapUtil.getShortcutsText(shortcuts);
+  }
+
+  private void fireShortcutsUpdated() {
+    for (Listener listener : myListeners) {
+      listener.shortcutsUpdated();
+    }
+  }
+
+  public void addListener(Listener listener) {
+    myListeners.add(listener);
+  }
+
+  public interface Listener {
+    void shortcutsUpdated();
+  }
+
+  private class ExternalSystemKeyMapListener implements KeymapManagerListener, Keymap.Listener {
+    private Keymap myCurrentKeymap = null;
+
+    public ExternalSystemKeyMapListener() {
+      KeymapManager keymapManager = KeymapManager.getInstance();
+      listenTo(keymapManager.getActiveKeymap());
+      keymapManager.addKeymapManagerListener(this);
+    }
+
+    @Override
+    public void activeKeymapChanged(Keymap keymap) {
+      listenTo(keymap);
+      fireShortcutsUpdated();
+    }
+
+    private void listenTo(Keymap keymap) {
+      if (myCurrentKeymap != null) {
+        myCurrentKeymap.removeShortcutChangeListener(this);
+      }
+      myCurrentKeymap = keymap;
+      if (myCurrentKeymap != null) {
+        myCurrentKeymap.addShortcutChangeListener(this);
+      }
+    }
+
+    @Override
+    public void onShortcutChanged(String actionId) {
+      fireShortcutsUpdated();
+    }
+
+    public void stopListen() {
+      listenTo(null);
+      KeymapManagerEx.getInstanceEx().removeKeymapManagerListener(this);
+    }
+  }
+
+  public void scheduleKeymapUpdate(Collection<DataNode<TaskData>> taskData) {
+    ExternalSystemKeymapExtension.updateActions(myProject, taskData);
+  }
+
+  public void scheduleRunConfigurationKeymapUpdate(@NotNull ProjectSystemId externalSystemId) {
+    ExternalSystemKeymapExtension.updateRunConfigurationActions(myProject, externalSystemId);
+  }
+
+  @Override
+  public void dispose() {
+    if (myKeyMapListener != null) {
+      myKeyMapListener.stopListen();
+    }
+    ExternalSystemKeymapExtension.clearActions(myProject);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemTaskActivator.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemTaskActivator.java
new file mode 100644 (file)
index 0000000..0535170
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileTask;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager.ExternalProjectsStateProvider;
+import com.intellij.openapi.externalSystem.task.TaskCallback;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Function;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.FactoryMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/28/2014
+ */
+public class ExternalSystemTaskActivator {
+
+  public static final String RUN_CONFIGURATION_TASK_PREFIX = "run: ";
+  @NotNull private final Project myProject;
+  private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
+
+  public ExternalSystemTaskActivator(@NotNull Project project) {
+    myProject = project;
+  }
+
+  @NotNull
+  public static String getRunConfigurationActivationTaskName(@NotNull RunnerAndConfigurationSettings settings) {
+    return RUN_CONFIGURATION_TASK_PREFIX + settings.getName();
+  }
+
+  public void init() {
+    CompilerManager compilerManager = CompilerManager.getInstance(myProject);
+
+    class MyCompileTask implements CompileTask {
+      private final boolean myBefore;
+
+      MyCompileTask(boolean before) {
+        myBefore = before;
+      }
+
+      @Override
+      public boolean execute(CompileContext context) {
+        return doExecute(myBefore, context);
+      }
+    }
+
+    compilerManager.addBeforeTask(new MyCompileTask(true));
+    compilerManager.addAfterTask(new MyCompileTask(false));
+
+    fireTasksChanged();
+  }
+
+  public String getDescription(ProjectSystemId systemId, String projectPath, String taskName) {
+    List<String> result = new ArrayList<String>();
+    final ExternalProjectsStateProvider stateProvider =
+      ExternalProjectsManager.getInstance(myProject).getStateProvider();
+    final TaskActivationState taskActivationState = stateProvider.getTasksActivation(systemId, projectPath);
+    if (taskActivationState == null) return null;
+
+    for (Phase phase : Phase.values()) {
+      if (taskActivationState.getTasks(phase).contains(taskName)) {
+        result.add(ExternalSystemBundle.message(phase.myMessageKey));
+      }
+    }
+    return StringUtil.join(result, ", ");
+  }
+
+  private boolean doExecute(boolean before, CompileContext context) {
+
+    final ExternalProjectsStateProvider stateProvider = ExternalProjectsManager.getInstance(myProject).getStateProvider();
+
+    final Queue<Pair<ProjectSystemId, ExternalSystemTaskExecutionSettings>> tasksQueue =
+      new LinkedList<Pair<ProjectSystemId, ExternalSystemTaskExecutionSettings>>();
+
+    //noinspection MismatchedQueryAndUpdateOfCollection
+    Map<ProjectSystemId, Map<String, RunnerAndConfigurationSettings>> lazyConfigurationsMap =
+      new FactoryMap<ProjectSystemId, Map<String, RunnerAndConfigurationSettings>>() {
+        @Nullable
+        @Override
+        protected Map<String, RunnerAndConfigurationSettings> create(ProjectSystemId key) {
+          final AbstractExternalSystemTaskConfigurationType configurationType =
+            ExternalSystemUtil.findConfigurationType(key);
+          if (configurationType == null) return null;
+          return ContainerUtil.map2Map(RunManager.getInstance(myProject).getConfigurationSettingsList(configurationType),
+                                       new Function<RunnerAndConfigurationSettings, Pair<String, RunnerAndConfigurationSettings>>() {
+                                         @Override
+                                         public Pair<String, RunnerAndConfigurationSettings> fun(RunnerAndConfigurationSettings configurationSettings) {
+                                           return Pair.create(configurationSettings.getName(), configurationSettings);
+                                         }
+                                       });
+        }
+      };
+
+    for (final ExternalProjectsStateProvider.TasksActivation activation : stateProvider.getAllTasksActivation()) {
+      Set<String> tasks = ContainerUtil.newTroveSet(activation.state.getTasks(before ? Phase.BEFORE_COMPILE : Phase.AFTER_COMPILE));
+      if (context.isRebuild()) {
+        tasks = ContainerUtil.union(tasks, (before ? activation.state.beforeRebuildTask : activation.state.afterRebuildTask));
+      }
+      if (tasks.isEmpty()) continue;
+
+      for (Iterator<String> iterator = tasks.iterator(); iterator.hasNext(); ) {
+        String task = iterator.next();
+        if (task.length() > RUN_CONFIGURATION_TASK_PREFIX.length() && task.startsWith(RUN_CONFIGURATION_TASK_PREFIX)) {
+          iterator.remove();
+          final String configurationName = task.substring(RUN_CONFIGURATION_TASK_PREFIX.length());
+
+          Map<String, RunnerAndConfigurationSettings> settings = lazyConfigurationsMap.get(activation.systemId);
+          if (settings == null) continue;
+
+          RunnerAndConfigurationSettings configurationSettings = settings.get(configurationName);
+          if (configurationSettings == null) continue;
+
+          final RunConfiguration runConfiguration = configurationSettings.getConfiguration();
+          if (configurationName.equals(configurationSettings.getName()) && runConfiguration instanceof ExternalSystemRunConfiguration) {
+            tasksQueue.add(Pair.create(activation.systemId, ((ExternalSystemRunConfiguration)runConfiguration).getSettings()));
+          }
+        }
+      }
+
+      if (tasks.isEmpty()) continue;
+
+      ExternalSystemTaskExecutionSettings executionSettings = new ExternalSystemTaskExecutionSettings();
+      executionSettings.setExternalSystemIdString(activation.systemId.toString());
+      executionSettings.setExternalProjectPath(activation.projectPath);
+      executionSettings.getTaskNames().addAll(tasks);
+      tasksQueue.add(Pair.create(activation.systemId, executionSettings));
+    }
+
+    return runTasksQueue(tasksQueue);
+  }
+
+  private boolean runTasksQueue(final Queue<Pair<ProjectSystemId, ExternalSystemTaskExecutionSettings>> tasksQueue) {
+    final Pair<ProjectSystemId, ExternalSystemTaskExecutionSettings> pair = tasksQueue.poll();
+    if (pair == null) return true;
+
+    final ProjectSystemId systemId = pair.first;
+    final ExternalSystemTaskExecutionSettings executionSettings = pair.getSecond();
+
+    final Semaphore targetDone = new Semaphore();
+    targetDone.down();
+    final Ref<Boolean> result = new Ref<Boolean>(false);
+    ExternalSystemUtil.runTask(executionSettings, DefaultRunExecutor.EXECUTOR_ID, myProject, systemId,
+                               new TaskCallback() {
+                                 @Override
+                                 public void onSuccess() {
+                                   result.set(runTasksQueue(tasksQueue));
+                                   targetDone.up();
+                                 }
+
+                                 @Override
+                                 public void onFailure() {
+                                   targetDone.up();
+                                 }
+                               },
+                               ProgressExecutionMode.IN_BACKGROUND_ASYNC);
+    targetDone.waitFor();
+    return result.get();
+  }
+
+  public void addListener(Listener l) {
+    myListeners.add(l);
+  }
+
+  public boolean isTaskOfPhase(@NotNull TaskData taskData, @NotNull Phase phase) {
+    final ExternalProjectsStateProvider stateProvider = ExternalProjectsManager.getInstance(myProject).getStateProvider();
+    final TaskActivationState taskActivationState =
+      stateProvider.getTasksActivation(taskData.getOwner(), taskData.getLinkedExternalProjectPath());
+    if (taskActivationState == null) return false;
+
+    return taskActivationState.getTasks(phase).contains(taskData.getName());
+  }
+
+  public void addTasks(List<TaskData> tasks, Phase phase) {
+    if (tasks.isEmpty()) return;
+
+    final ExternalProjectsStateProvider stateProvider = ExternalProjectsManager.getInstance(myProject).getStateProvider();
+    for (TaskData task : tasks) {
+      final TaskActivationState taskActivationState = stateProvider.getTasksActivation(task.getOwner(),
+                                                                                       task.getLinkedExternalProjectPath());
+      taskActivationState.getTasks(phase).add(task.getName());
+    }
+
+    fireTasksChanged();
+  }
+
+  public void removeTasks(List<TaskData> tasks, Phase phase) {
+    if (tasks.isEmpty()) return;
+
+    final ExternalProjectsStateProvider stateProvider = ExternalProjectsManager.getInstance(myProject).getStateProvider();
+
+    for (TaskData task : tasks) {
+      final TaskActivationState taskActivationState = stateProvider.getTasksActivation(task.getOwner(),
+                                                                                       task.getLinkedExternalProjectPath());
+      taskActivationState.getTasks(phase).remove(task.getName());
+    }
+
+    fireTasksChanged();
+  }
+
+  public void fireTasksChanged() {
+    for (Listener each : myListeners) {
+      each.tasksActivationChanged();
+    }
+  }
+
+  public enum Phase {
+    AFTER_SYNC("external.system.task.after.sync"),
+    BEFORE_COMPILE("external.system.task.before.compile"),
+    AFTER_COMPILE("external.system.task.after.compile"),
+    BEFORE_REBUILD("external.system.task.before.rebuild"),
+    AFTER_REBUILD("external.system.task.after.rebuild");
+
+    public final String myMessageKey;
+
+    Phase(String messageKey) {
+      myMessageKey = messageKey;
+    }
+  }
+
+  public interface Listener {
+    void tasksActivationChanged();
+  }
+}
index 7f638bbaa5a119b90c80dd1741845247c21cba65..b8c2a4f963ea70db8dcc9227c9bdc595f2281abc 100644 (file)
@@ -24,6 +24,8 @@ import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.NotNullLazyValue;
+import com.intellij.util.Consumer;
+import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import com.intellij.util.containers.Stack;
@@ -72,11 +74,6 @@ public class ProjectDataManager {
     };
   }
 
-  @Nullable
-  public List<ProjectDataService<?, ?>> getDataServices(Key<?> key) {
-    return myServices.getValue().get(key);
-  }
-
   @Nullable
   public ProjectDataService<?, ?> getDataService(Key<?> key) {
     final List<ProjectDataService<?, ?>> dataServices = myServices.getValue().get(key);
@@ -86,7 +83,7 @@ public class ProjectDataManager {
 
   @SuppressWarnings("unchecked")
   public <T> void importData(@NotNull Collection<DataNode<?>> nodes, @NotNull Project project, boolean synchronous) {
-    if(project.isDisposed()) return;
+    if (project.isDisposed()) return;
 
     Map<Key<?>, List<DataNode<?>>> grouped = ExternalSystemApiUtil.group(nodes);
     for (Map.Entry<Key<?>, List<DataNode<?>>> entry : grouped.entrySet()) {
@@ -101,9 +98,9 @@ public class ProjectDataManager {
 
   @SuppressWarnings("unchecked")
   public <T> void importData(@NotNull Key<T> key, @NotNull Collection<DataNode<T>> nodes, @NotNull Project project, boolean synchronous) {
-    if(project.isDisposed()) return;
+    if (project.isDisposed()) return;
 
-    ensureTheDataIsReadyToUse(nodes);
+    ensureTheDataIsReadyToUse((Collection)nodes);
     List<ProjectDataService<?, ?>> services = myServices.getValue().get(key);
     if (services == null) {
       LOG.warn(String.format(
@@ -124,22 +121,33 @@ public class ProjectDataManager {
     importData(children, project, synchronous);
   }
 
-  @SuppressWarnings("unchecked")
-  private <T> void ensureTheDataIsReadyToUse(@NotNull Collection<DataNode<T>> nodes) {
-    Map<Key<?>, List<ProjectDataService<?, ?>>> servicesByKey = myServices.getValue();
-    Stack<DataNode<T>> toProcess = ContainerUtil.newStack(nodes);
-    while (!toProcess.isEmpty()) {
-      DataNode<T> node = toProcess.pop();
-      List<ProjectDataService<?, ?>> services = servicesByKey.get(node.getKey());
-      if (services != null) {
-        for (ProjectDataService<?, ?> service : services) {
-          node.prepareData(service.getClass().getClassLoader());
+  public void ensureTheDataIsReadyToUse(DataNode dataNode) {
+    final Map<Key<?>, List<ProjectDataService<?, ?>>> servicesByKey = myServices.getValue();
+    ExternalSystemApiUtil.visit(dataNode, new Consumer<DataNode>() {
+      @Override
+      public void consume(DataNode dataNode) {
+        List<ProjectDataService<?, ?>> services = servicesByKey.get(dataNode.getKey());
+        if (services != null) {
+          try {
+            dataNode.prepareData(ContainerUtil.map2Array(services, ClassLoader.class, new Function<ProjectDataService<?, ?>, ClassLoader>() {
+              @Override
+              public ClassLoader fun(ProjectDataService<?, ?> service) {
+                return service.getClass().getClassLoader();
+              }
+            }));
+          }
+          catch (Exception e) {
+            LOG.debug(e);
+            dataNode.clear(true);
+          }
         }
       }
+    });
+  }
 
-      for (DataNode<?> dataNode : node.getChildren()) {
-        toProcess.push((DataNode<T>)dataNode);
-      }
+  private void ensureTheDataIsReadyToUse(@NotNull Collection<DataNode<?>> nodes) {
+    for (DataNode<?> node : nodes) {
+      ensureTheDataIsReadyToUse(node);
     }
   }
 
@@ -152,13 +160,21 @@ public class ProjectDataManager {
   }
 
   public void updateExternalProjectData(@NotNull Project project, @NotNull ExternalProjectInfo externalProjectInfo) {
-    if(!project.isDisposed()) {
-      ExternalProjectsDataStorage.getInstance(project).add(externalProjectInfo);
+    if (!project.isDisposed()) {
+      ExternalProjectsManager.getInstance(project).updateExternalProjectData(externalProjectInfo);
     }
   }
 
   @Nullable
-  public ExternalProjectInfo getExternalProjectData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) {
+  public ExternalProjectInfo getExternalProjectData(@NotNull Project project,
+                                                    @NotNull ProjectSystemId projectSystemId,
+                                                    @NotNull String externalProjectPath) {
     return !project.isDisposed() ? ExternalProjectsDataStorage.getInstance(project).get(projectSystemId, externalProjectPath) : null;
   }
+
+  @NotNull
+  public Collection<ExternalProjectInfo> getExternalProjectsData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId) {
+    return !project.isDisposed() ?
+           ExternalProjectsDataStorage.getInstance(project).list(projectSystemId) : ContainerUtil.<ExternalProjectInfo>emptyList();
+  }
 }
index 34c18a9b3194f23a520ffb964af9c4869fdbeee6..43b19c2cabb4926f1fb6c308e9db0364ed7f3f49 100644 (file)
@@ -47,7 +47,7 @@ public class ProjectDataServiceImpl implements ProjectDataService<ProjectData, P
     }
     DataNode<ProjectData> node = toImport.iterator().next();
     ProjectData projectData = node.getData();
-    
+
     if (!ExternalSystemApiUtil.isNewProjectConstruction() && !ExternalSystemUtil.isOneToOneMapping(project, node)) {
       return;
     }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/TaskActivationState.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/TaskActivationState.java
new file mode 100644 (file)
index 0000000..2099e40
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2014 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.project.manage;
+
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+* @author Vladislav.Soroka
+* @since 10/30/2014
+*/
+@Tag("activation")
+public class TaskActivationState {
+  @Tag("after_sync")
+  @AbstractCollection(surroundWithTag = false, elementTag = "task", elementValueAttribute = "name")
+  public Set<String> afterSyncTasks = new TreeSet<String>();
+
+  @Tag("before_compile")
+  @AbstractCollection(surroundWithTag = false, elementTag = "task", elementValueAttribute = "name")
+  public Set<String> beforeCompileTasks = new TreeSet<String>();
+
+  @Tag("after_compile")
+  @AbstractCollection(surroundWithTag = false, elementTag = "task", elementValueAttribute = "name")
+  public Set<String> afterCompileTasks = new TreeSet<String>();
+
+  @Tag("after_rebuild")
+  @AbstractCollection(surroundWithTag = false, elementTag = "task", elementValueAttribute = "name")
+  public Set<String> afterRebuildTask = new TreeSet<String>();
+
+  @Tag("before_rebuild")
+  @AbstractCollection(surroundWithTag = false, elementTag = "task", elementValueAttribute = "name")
+  public Set<String> beforeRebuildTask = new TreeSet<String>();
+
+  @NotNull
+  public Set<String> getTasks(@NotNull ExternalSystemTaskActivator.Phase phase) {
+    switch (phase) {
+      case AFTER_COMPILE:
+        return afterCompileTasks;
+      case AFTER_SYNC:
+        return afterSyncTasks;
+      case BEFORE_COMPILE:
+        return beforeCompileTasks;
+      case AFTER_REBUILD:
+        return afterRebuildTask;
+      case BEFORE_REBUILD:
+        return beforeRebuildTask;
+      default:
+        throw new RuntimeException();
+    }
+  }
+}
index b0714303b9f56ddfa1e529cc1379036f6521a2dc..40b043651f6cebcc4bd58398dbf196dca801e252 100644 (file)
@@ -42,18 +42,13 @@ public abstract class AbstractToolWindowService<T extends ExternalEntityData> im
     ExternalSystemApiUtil.executeOnEdt(false, new Runnable() {
       @Override
       public void run() {
-        ExternalSystemTasksTreeModel model = ExternalSystemUtil.getToolWindowElement(ExternalSystemTasksTreeModel.class,
-                                                                                     project,
-                                                                                     ExternalSystemDataKeys.ALL_TASKS_MODEL,
-                                                                                     toImport.iterator().next().getData().getOwner());
-        processData(toImport, project, model);
+        processData(toImport, project);
       }
     });
   }
 
   protected abstract void processData(@NotNull Collection<DataNode<T>> nodes,
-                                      @NotNull Project project,
-                                      @Nullable ExternalSystemTasksTreeModel model);
+                                      @NotNull Project project);
 
   @Override
   public void removeData(@NotNull Collection<? extends Void> toRemove, @NotNull Project project, boolean synchronous) {
index 16ee28307629dec0c9aeee3f144d9fa2ea0e4c0b..0e1a06efce21766d8a12ba63f0a1465b23854032 100644 (file)
@@ -23,7 +23,6 @@ import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemTasksTreeModel;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
@@ -32,7 +31,6 @@ import com.intellij.openapi.project.Project;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
@@ -62,8 +60,7 @@ public class ToolWindowModuleService extends AbstractToolWindowService<ModuleDat
 
   @Override
   protected void processData(@NotNull final Collection<DataNode<ModuleData>> nodes,
-                             @NotNull Project project,
-                             @Nullable final ExternalSystemTasksTreeModel model)
+                             @NotNull Project project)
   {
     if (nodes.isEmpty()) {
       return;
index a9a6657409469b770e218eaf1a8891669420257a..6467d7ed95b41fe5d4335ae456fef5b0d3bd09b6 100644 (file)
@@ -24,11 +24,9 @@ import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.model.task.TaskData;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemTasksTreeModel;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
 import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.project.Project;
 import com.intellij.util.Function;
@@ -74,8 +72,7 @@ public class ToolWindowTaskService extends AbstractToolWindowService<TaskData> {
 
   @Override
   protected void processData(@NotNull Collection<DataNode<TaskData>> nodes,
-                             @NotNull Project project,
-                             @Nullable final ExternalSystemTasksTreeModel model)
+                             @NotNull Project project)
   {
     if (nodes.isEmpty()) {
       return;
@@ -94,9 +91,5 @@ public class ToolWindowTaskService extends AbstractToolWindowService<TaskData> {
     Map<String, Collection<ExternalTaskPojo>> availableTasks = ContainerUtilRt.newHashMap(settings.getAvailableTasks());
     availableTasks.putAll(data);
     settings.setAvailableTasks(availableTasks);
-
-    if (model != null) {
-      ExternalSystemUiUtil.apply(settings, model);
-    }
   }
 }
index a5bf2827ef262fc4827dd4267edfd3041cb1cb6a..d6ec2d53159e09d19a86e284eac66880e2f95a5c 100644 (file)
  */
 package com.intellij.openapi.externalSystem.service.task.ui;
 
-import com.intellij.notification.NotificationGroup;
-import com.intellij.openapi.externalSystem.ExternalSystemManager;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.openapi.wm.ex.ToolWindowEx;
 import com.intellij.ui.content.ContentManager;
 import com.intellij.ui.content.impl.ContentImpl;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Locale;
-
 /**
  * @author Denis Zhdanov
  * @since 5/13/13 4:15 PM
  */
 public abstract class AbstractExternalSystemToolWindowFactory implements ToolWindowFactory, DumbAware {
 
-  @NotNull private final ProjectSystemId   myExternalSystemId;
-  @NotNull private final NotificationGroup myNotificationGroup;
+  @NotNull private final ProjectSystemId myExternalSystemId;
 
   protected AbstractExternalSystemToolWindowFactory(@NotNull ProjectSystemId id) {
     myExternalSystemId = id;
-    myNotificationGroup = NotificationGroup.toolWindowGroup("notification.group.id." + id.toString().toLowerCase(Locale.ENGLISH),
-                                                            myExternalSystemId.getReadableName());
   }
 
   @Override
   public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) {
     toolWindow.setTitle(myExternalSystemId.getReadableName());
     ContentManager contentManager = toolWindow.getContentManager();
-    String tasksTitle = ExternalSystemBundle.message("tool.window.title.tasks");
-    ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(myExternalSystemId);
-    assert manager != null;
-    ExternalSystemTasksPanel panel = new ExternalSystemTasksPanel(project, myExternalSystemId, myNotificationGroup);
-    ContentImpl tasksContent = new ContentImpl(panel, tasksTitle, true);
+    final ExternalProjectsView projectsView = new ExternalProjectsView(project, (ToolWindowEx)toolWindow, myExternalSystemId);
+    ExternalProjectsManager.getInstance(project).registerView(projectsView);
+    ContentImpl tasksContent = new ContentImpl(projectsView, ExternalSystemBundle.message("tool.window.title.projects"), true);
     contentManager.addContent(tasksContent);
   }
 }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModel.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModel.java
deleted file mode 100644 (file)
index 16ba604..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2000-2013 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.task.ui;
-
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
-import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
-import com.intellij.openapi.project.Project;
-import com.intellij.util.containers.ContainerUtilRt;
-import org.jetbrains.annotations.NotNull;
-
-import javax.swing.*;
-import java.util.List;
-
-/**
- * @author Denis Zhdanov
- * @since 6/7/13 3:28 PM
- */
-public class ExternalSystemRecentTaskListModel extends DefaultListModel {
-
-  @NotNull private final ProjectSystemId myExternalSystemId;
-  @NotNull private final Project         myProject;
-
-  public ExternalSystemRecentTaskListModel(@NotNull ProjectSystemId externalSystemId, @NotNull Project project) {
-    myExternalSystemId = externalSystemId;
-    myProject = project;
-    ensureSize(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-  }
-
-  @SuppressWarnings("unchecked")
-  public void setTasks(@NotNull List<ExternalTaskExecutionInfo> tasks) {
-    clear();
-    List<ExternalTaskExecutionInfo> tasksToUse = ContainerUtilRt.newArrayList(tasks);
-    for (ExternalTaskExecutionInfo task : tasksToUse) {
-      addElement(task);
-    }
-  }
-
-  @SuppressWarnings("unchecked")
-  public void setFirst(@NotNull ExternalTaskExecutionInfo task) {
-    insertElementAt(task, 0);
-    for (int i = 1; i < size(); i++) {
-      if (task.equals(getElementAt(i))) {
-        remove(i);
-        break;
-      }
-    }
-    ensureSize(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-  }
-
-  @NotNull
-  public List<ExternalTaskExecutionInfo> getTasks() {
-    List<ExternalTaskExecutionInfo> result = ContainerUtilRt.newArrayList();
-    for (int i = 0; i < size(); i++) {
-      Object e = getElementAt(i);
-      if (e instanceof ExternalTaskExecutionInfo) {
-        result.add((ExternalTaskExecutionInfo)e);
-      }
-    }
-    return result;
-  }
-
-  @SuppressWarnings("unchecked")
-  public void ensureSize(int elementsNumber) {
-    int toAdd = elementsNumber - size();
-    if (toAdd == 0) {
-      return;
-    }
-    if(toAdd < 0) {
-      removeRange(elementsNumber, size() - 1);
-    }
-    while (--toAdd >= 0) {
-      addElement(new MyEmptyDescriptor());
-    }
-  }
-
-  /**
-   * Asks current model to remove all 'recent task info' entries which point to tasks from external project with the given path.
-   * 
-   * @param externalProjectPath  target external project's path
-   */
-  public void forgetTasksFrom(@NotNull String externalProjectPath) {
-    for (int i = size() - 1; i >= 0; i--) {
-      Object e = getElementAt(i);
-      if (e instanceof ExternalTaskExecutionInfo) {
-        String path = ((ExternalTaskExecutionInfo)e).getSettings().getExternalProjectPath();
-        if (externalProjectPath.equals(path)
-            || externalProjectPath.equals(ExternalSystemApiUtil.getRootProjectPath(path, myExternalSystemId, myProject)))
-        {
-          removeElementAt(i);
-        }
-      }
-    }
-    ensureSize(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-  }
-
-  static class MyEmptyDescriptor {
-  }
-}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTasksList.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTasksList.java
deleted file mode 100644 (file)
index 4ef7b85..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2000-2013 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.task.ui;
-
-import com.intellij.execution.Executor;
-import com.intellij.execution.ExecutorRegistry;
-import com.intellij.execution.RunManager;
-import com.intellij.execution.configurations.ConfigurationType;
-import com.intellij.execution.configurations.RunConfiguration;
-import com.intellij.openapi.externalSystem.ExternalSystemManager;
-import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
-import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
-import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
-import com.intellij.openapi.externalSystem.service.ui.DefaultExternalSystemUiAware;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
-import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.ui.components.JBList;
-import com.intellij.util.Producer;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.*;
-import java.util.List;
-
-/**
- * @author Denis Zhdanov
- * @since 6/7/13 2:40 PM
- */
-public class ExternalSystemRecentTasksList extends JBList implements Producer<ExternalTaskExecutionInfo> {
-
-  @NotNull private static final JLabel EMPTY_RENDERER = new JLabel(" ");
-  
-  public ExternalSystemRecentTasksList(@NotNull ExternalSystemRecentTaskListModel model,
-                                       @NotNull final ProjectSystemId externalSystemId,
-                                       @NotNull final Project project)
-  {
-    super(model);
-    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-    ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
-    Icon icon = null;
-    if (manager instanceof ExternalSystemUiAware) {
-      icon = ((ExternalSystemUiAware)manager).getTaskIcon();
-    }
-    if (icon == null) {
-      icon = DefaultExternalSystemUiAware.INSTANCE.getTaskIcon();
-    }
-    setCellRenderer(new MyRenderer(project, icon, ExternalSystemUtil.findConfigurationType(externalSystemId)));
-    setVisibleRowCount(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-
-    registerKeyboardAction(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        ExternalTaskExecutionInfo task = produce();
-        if (task == null) {
-          return;
-        }
-        ExternalSystemUtil.runTask(task.getSettings(), task.getExecutorId(), project, externalSystemId);
-      }
-    }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
-
-    addMouseListener(new MouseAdapter() {
-      @Override
-      public void mouseClicked(MouseEvent e) {
-        if (e.getClickCount() < 2) {
-          return;
-        }
-
-        ExternalTaskExecutionInfo task = produce();
-        if (task == null) {
-          return;
-        }
-
-        ExternalSystemUtil.runTask(task.getSettings(), task.getExecutorId(), project, externalSystemId);
-      }
-    });
-  }
-
-  @Override
-  public ExternalSystemRecentTaskListModel getModel() {
-    return (ExternalSystemRecentTaskListModel)super.getModel();
-  }
-
-  public void setFirst(@NotNull ExternalTaskExecutionInfo task) {
-    ExternalTaskExecutionInfo selected = produce();
-    ExternalSystemRecentTaskListModel model = getModel();
-    model.setFirst(task);
-    clearSelection();
-    if (selected == null) {
-      return;
-    }
-    for (int i = 0; i < model.size(); i++) {
-      //noinspection SuspiciousMethodCalls
-      if (selected.equals(model.getElementAt(i))) {
-        addSelectionInterval(i, i);
-        return;
-      }
-    }
-  }
-
-  @Nullable
-  @Override
-  public ExternalTaskExecutionInfo produce() {
-    int[] indices = getSelectedIndices();
-    if (indices == null || indices.length != 1) {
-      return null;
-    }
-    Object e = getModel().getElementAt(indices[0]);
-    return e instanceof ExternalTaskExecutionInfo ? (ExternalTaskExecutionInfo)e : null;
-  }
-
-  private static class MyRenderer extends DefaultListCellRenderer {
-
-    @NotNull private final Icon myGenericTaskIcon;
-    @NotNull private final Project myProject;
-    @Nullable private ConfigurationType myConfigurationType;
-
-    MyRenderer(@NotNull Project project, @NotNull Icon genericTaskIcon, @Nullable ConfigurationType configurationType) {
-      myProject = project;
-      myGenericTaskIcon = genericTaskIcon;
-      myConfigurationType = configurationType;
-    }
-
-    @Override
-    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
-      Component renderer = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
-      if (value instanceof ExternalSystemRecentTaskListModel.MyEmptyDescriptor) {
-        return EMPTY_RENDERER;
-      }
-      else if (value instanceof ExternalTaskExecutionInfo) {
-        ExternalTaskExecutionInfo taskInfo = (ExternalTaskExecutionInfo)value;
-        String text = null;
-        if (myConfigurationType != null) {
-          List<RunConfiguration> configurations = RunManager.getInstance(myProject).getConfigurationsList(myConfigurationType);
-          for (RunConfiguration configuration : configurations) {
-            if (!(configuration instanceof ExternalSystemRunConfiguration)) {
-              continue;
-            }
-            ExternalSystemRunConfiguration c = (ExternalSystemRunConfiguration)configuration;
-            if (c.getSettings().equals(taskInfo.getSettings())) {
-              text = c.getName();
-            }
-          }
-        }
-        if (StringUtil.isEmpty(text)) {
-          text = AbstractExternalSystemTaskConfigurationType.generateName(myProject, taskInfo.getSettings());
-        }
-        
-        setText(text);
-        Icon icon = null;
-        String executorId = taskInfo.getExecutorId();
-        if (!StringUtil.isEmpty(executorId)) {
-          Executor executor = ExecutorRegistry.getInstance().getExecutorById(executorId);
-          if (executor != null) {
-            icon = executor.getIcon();
-          }
-        }
-
-        if (icon == null) {
-          icon = myGenericTaskIcon;
-        }
-        setIcon(icon);
-      }
-
-      return renderer;
-    }
-
-    @Override
-    public void setIcon(Icon icon) {
-      if (icon != null) {
-        // Don't allow to reset icon.
-        super.setIcon(icon);
-      }
-    }
-  }
-}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemTasksPanel.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemTasksPanel.java
deleted file mode 100644 (file)
index 2530fc9..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2000-2013 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.task.ui;
-
-import com.intellij.execution.Location;
-import com.intellij.execution.executors.DefaultRunExecutor;
-import com.intellij.ide.ui.customization.CustomizationUtil;
-import com.intellij.notification.NotificationGroup;
-import com.intellij.openapi.actionSystem.ActionGroup;
-import com.intellij.openapi.actionSystem.ActionManager;
-import com.intellij.openapi.actionSystem.ActionToolbar;
-import com.intellij.openapi.actionSystem.DataProvider;
-import com.intellij.openapi.externalSystem.ExternalSystemManager;
-import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
-import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
-import com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskLocation;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
-import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
-import com.intellij.openapi.fileTypes.PlainTextFileType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.SimpleToolWindowPanel;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiFileFactory;
-import com.intellij.ui.IdeBorderFactory;
-import com.intellij.ui.components.JBScrollPane;
-import com.intellij.util.Producer;
-import com.intellij.util.ui.UIUtil;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-
-import static com.intellij.openapi.externalSystem.util.ExternalSystemConstants.*;
-
-/**
- * @author Denis Zhdanov
- * @since 5/12/13 10:18 PM
- */
-public class ExternalSystemTasksPanel extends SimpleToolWindowPanel implements DataProvider {
-
-  @NotNull private final ExternalSystemRecentTasksList myRecentTasksList;
-  @NotNull private final ExternalSystemTasksTreeModel  myAllTasksModel;
-  @NotNull private final ExternalSystemTasksTree       myAllTasksTree;
-  @NotNull private final ProjectSystemId               myExternalSystemId;
-  @NotNull private final NotificationGroup             myNotificationGroup;
-  @NotNull private final Project                       myProject;
-
-  @Nullable private Producer<ExternalTaskExecutionInfo> mySelectedTaskProvider;
-
-  public ExternalSystemTasksPanel(@NotNull Project project,
-                                  @NotNull ProjectSystemId externalSystemId,
-                                  @NotNull NotificationGroup notificationGroup)
-  {
-    super(true);
-    myExternalSystemId = externalSystemId;
-    myNotificationGroup = notificationGroup;
-    myProject = project;
-
-    ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
-    assert manager != null;
-    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
-
-    ExternalSystemRecentTaskListModel recentTasksModel = new ExternalSystemRecentTaskListModel(externalSystemId, project);
-    recentTasksModel.setTasks(settings.getRecentTasks());
-    myRecentTasksList = new ExternalSystemRecentTasksList(recentTasksModel, externalSystemId, project) {
-      @Override
-      protected void processMouseEvent(MouseEvent e) {
-        if (e.getClickCount() > 0) {
-          mySelectedTaskProvider = myRecentTasksList;
-          myAllTasksTree.getSelectionModel().clearSelection();
-        }
-        super.processMouseEvent(e);
-      }
-    };
-
-    myAllTasksModel = new ExternalSystemTasksTreeModel(externalSystemId);
-    myAllTasksTree = new ExternalSystemTasksTree(myAllTasksModel, settings.getExpandStates(), project, externalSystemId) {
-      @Override
-      protected void processMouseEvent(MouseEvent e) {
-        if (e.getClickCount() > 0) {
-          mySelectedTaskProvider = myAllTasksTree;
-          myRecentTasksList.getSelectionModel().clearSelection();
-        }
-        super.processMouseEvent(e);
-      }
-    };
-    final String actionIdToUseForDoubleClick = DefaultRunExecutor.getRunExecutorInstance().getContextActionId();
-    myAllTasksTree.addMouseListener(new MouseAdapter() {
-      @Override
-      public void mouseClicked(MouseEvent e) {
-        if (e.getClickCount() >= 2 && !e.isPopupTrigger()) {
-          ExternalSystemUiUtil.executeAction(actionIdToUseForDoubleClick, e);
-        }
-      }
-    });
-    ExternalSystemUiUtil.apply(settings, myAllTasksModel);
-    CustomizationUtil.installPopupHandler(myAllTasksTree, TREE_ACTIONS_GROUP_ID, TREE_CONTEXT_MENU_PLACE);
-
-    ActionManager actionManager = ActionManager.getInstance();
-    ActionGroup group = (ActionGroup)actionManager.getAction(TOOL_WINDOW_TOOLBAR_ACTIONS_GROUP_ID);
-    ActionToolbar toolbar = actionManager.createActionToolbar(TOOL_WINDOW_PLACE, group, true);
-    toolbar.setTargetComponent(this);
-    setToolbar(toolbar.getComponent());
-
-    JPanel content = new JPanel(new GridBagLayout());
-    content.setOpaque(true);
-    content.setBackground(UIUtil.getListBackground());
-    JComponent recentTasksWithTitle = wrap(myRecentTasksList, ExternalSystemBundle.message("tasks.recent.title"));
-    content.add(recentTasksWithTitle, ExternalSystemUiUtil.getFillLineConstraints(0));
-    JBScrollPane scrollPane = new JBScrollPane(myAllTasksTree);
-    scrollPane.setBorder(null);
-    JComponent allTasksWithTitle = wrap(scrollPane, ExternalSystemBundle.message("tasks.all.title"));
-    content.add(allTasksWithTitle, ExternalSystemUiUtil.getFillLineConstraints(0).weighty(1).fillCell());
-    setContent(content);
-  }
-
-  private static JComponent wrap(@NotNull JComponent content, @NotNull String title) {
-    JPanel result = new JPanel(new BorderLayout());
-    result.setOpaque(false);
-    result.setBorder(IdeBorderFactory.createTitledBorder(title, false));
-    result.add(content, BorderLayout.CENTER);
-    return result;
-  }
-
-  @Nullable
-  @Override
-  public Object getData(@NonNls String dataId) {
-    if (ExternalSystemDataKeys.RECENT_TASKS_LIST.is(dataId)) {
-      return myRecentTasksList;
-    }
-    else if (ExternalSystemDataKeys.ALL_TASKS_MODEL.is(dataId)) {
-      return myAllTasksModel;
-    }
-    else if (ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.is(dataId)) {
-      return myExternalSystemId;
-    }
-    else if (ExternalSystemDataKeys.NOTIFICATION_GROUP.is(dataId)) {
-      return myNotificationGroup;
-    }
-    else if (ExternalSystemDataKeys.SELECTED_TASK.is(dataId)) {
-      return mySelectedTaskProvider == null ? null : mySelectedTaskProvider.produce();
-    }
-    else if (ExternalSystemDataKeys.SELECTED_PROJECT.is(dataId)) {
-      if (mySelectedTaskProvider != myAllTasksTree) {
-        return null;
-      }
-      else {
-        Object component = myAllTasksTree.getLastSelectedPathComponent();
-        if (component instanceof ExternalSystemNode) {
-          Object element = ((ExternalSystemNode)component).getDescriptor().getElement();
-          return element instanceof ExternalProjectPojo ? element : null;
-        }
-      }
-    }
-    else if (Location.DATA_KEY.is(dataId)) {
-      Location location = buildLocation();
-      return location == null ? super.getData(dataId) : location;
-    }
-    return null;
-  }
-
-  @Nullable
-  private Location buildLocation() {
-    if (mySelectedTaskProvider == null) {
-      return null;
-    }
-    ExternalTaskExecutionInfo task = mySelectedTaskProvider.produce();
-    if (task == null) {
-      return null;
-    }
-
-    String projectPath = task.getSettings().getExternalProjectPath();
-    String name = myExternalSystemId.getReadableName() + projectPath + StringUtil.join(task.getSettings().getTaskNames(), " ");
-    // We create a dummy text file instead of re-using external system file in order to avoid clashing with other configuration producers.
-    // For example gradle files are enhanced groovy scripts but we don't want to run them via regular IJ groovy script runners.
-    // Gradle tooling api should be used for running gradle tasks instead. IJ execution sub-system operates on Location objects
-    // which encapsulate PsiElement and groovy runners are automatically applied if that PsiElement IS-A GroovyFile.
-    PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(name, PlainTextFileType.INSTANCE, "nichts");
-
-    return new ExternalSystemTaskLocation(myProject, file, task);
-  }
-}
index d987be2e9fe484fb6d745f2def277a5b99d06808..3a4cf7146e2d8a439e0602671177b8433a3e7696 100644 (file)
@@ -105,28 +105,6 @@ public class ExternalSystemUiUtil {
     component.add(Box.createVerticalGlue(), new GridBag().weightx(1).weighty(1).fillCell().coverLine());
   }
 
-  /**
-   * Applies data from the given settings object to the given model.
-   * 
-   * @param settings  target settings to use
-   * @param model     UI model to be synced with the given settings
-   */
-  public static void apply(@NotNull final AbstractExternalSystemLocalSettings settings, @NotNull final ExternalSystemTasksTreeModel model) {
-    UIUtil.invokeLaterIfNeeded(new Runnable() {
-      @Override
-      public void run() {
-        Map<ExternalProjectPojo,Collection<ExternalProjectPojo>> projects = settings.getAvailableProjects();
-        for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> entry : projects.entrySet()) {
-          model.ensureSubProjectsStructure(entry.getKey(), entry.getValue());
-        }
-        Map<String, Collection<ExternalTaskPojo>> tasks = settings.getAvailableTasks();
-        for (Map.Entry<String, Collection<ExternalTaskPojo>> entry : tasks.entrySet()) {
-          model.ensureTasks(entry.getKey(), entry.getValue());
-        } 
-      }
-    });
-  }
-
   public static void showUi(@NotNull Object o, boolean show) {
     for (Class<?> clazz = o.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
       for (Field field : clazz.getDeclaredFields()) {
index 62bf8a5dcad7118116556b814ddd558231f97255..8f7384efd8148bb1a32e73d6f07218e3653d22cf 100644 (file)
@@ -39,7 +39,6 @@ import com.intellij.openapi.externalSystem.importing.ImportSpec;
 import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder;
 import com.intellij.openapi.externalSystem.model.*;
 import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
@@ -55,14 +54,14 @@ import com.intellij.openapi.externalSystem.service.notification.NotificationSour
 import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.service.project.ProjectStructureHelper;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.service.project.manage.ModuleDataService;
 import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.service.settings.ExternalSystemConfigLocator;
-import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemRecentTasksList;
-import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
 import com.intellij.openapi.externalSystem.task.TaskCallback;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.progress.PerformInBackgroundOption;
 import com.intellij.openapi.progress.ProgressIndicator;
@@ -142,7 +141,6 @@ public class ExternalSystemUtil {
     }
   }
 
-  @SuppressWarnings("unchecked")
   @Nullable
   public static <T> T getToolWindowElement(@NotNull Class<T> clazz,
                                            @NotNull Project project,
@@ -170,6 +168,7 @@ public class ExternalSystemUtil {
       if (component instanceof DataProvider) {
         final Object data = ((DataProvider)component).getData(key.getName());
         if (data != null && clazz.isInstance(data)) {
+          //noinspection unchecked
           return (T)data;
         }
       }
@@ -300,6 +299,13 @@ public class ExternalSystemUtil {
     return timeStamp;
   }
 
+  public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
+                                       @NotNull final Project project,
+                                       @NotNull final ProjectSystemId externalSystemId) {
+    //noinspection unchecked
+    ruleOrphanModules(orphanModules, project, externalSystemId, Consumer.EMPTY_CONSUMER);
+  }
+
   /**
    * There is a possible case that an external module has been un-linked from ide project. There are two ways to process
    * ide modules which correspond to that external project:
@@ -317,7 +323,8 @@ public class ExternalSystemUtil {
    */
   public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
                                        @NotNull final Project project,
-                                       @NotNull final ProjectSystemId externalSystemId)
+                                       @NotNull final ProjectSystemId externalSystemId,
+                                       @NotNull final Consumer<Boolean> result)
   {
     UIUtil.invokeLaterIfNeeded(new Runnable() {
       @Override
@@ -356,6 +363,7 @@ public class ExternalSystemUtil {
           }
         };
         boolean ok = dialog.showAndGet();
+        result.consume(ok);
         if (!ok) {
           return;
         }
@@ -700,6 +708,16 @@ public class ExternalSystemUtil {
     ProgramRunner runner = RunnerRegistry.getInstance().findRunnerById(runnerId);
     if (runner == null) return null;
 
+    RunnerAndConfigurationSettings settings = createExternalSystemRunnerAndConfigurationSettings(taskSettings, project, externalSystemId);
+    if (settings == null) return null;
+
+    return Pair.create(runner, new ExecutionEnvironment(executor, runner, settings, project));
+  }
+
+  @Nullable
+  public static RunnerAndConfigurationSettings createExternalSystemRunnerAndConfigurationSettings(@NotNull ExternalSystemTaskExecutionSettings taskSettings,
+                                                                                                  @NotNull Project project,
+                                                                                                  @NotNull ProjectSystemId externalSystemId) {
     AbstractExternalSystemTaskConfigurationType configurationType = findConfigurationType(externalSystemId);
     if (configurationType == null) return null;
 
@@ -713,7 +731,7 @@ public class ExternalSystemUtil {
     runConfiguration.getSettings().setScriptParameters(taskSettings.getScriptParameters());
     runConfiguration.getSettings().setExecutionName(taskSettings.getExecutionName());
 
-    return Pair.create(runner, new ExecutionEnvironment(executor, runner, settings, project));
+    return settings;
   }
 
   @Nullable
@@ -729,32 +747,6 @@ public class ExternalSystemUtil {
     return null;
   }
 
-  /**
-   * Is expected to be called when given task info is about to be executed.
-   * <p/>
-   * Basically, this method updates recent tasks list at the corresponding external system tool window and
-   * persists new recent tasks state.
-   * 
-   * @param taskInfo  task which is about to be executed
-   * @param project   target project
-   */
-  public static void updateRecentTasks(@NotNull ExternalTaskExecutionInfo taskInfo, @NotNull Project project) {
-    ProjectSystemId externalSystemId = taskInfo.getSettings().getExternalSystemId();
-    ExternalSystemRecentTasksList recentTasksList = getToolWindowElement(ExternalSystemRecentTasksList.class,
-                                                                         project,
-                                                                         ExternalSystemDataKeys.RECENT_TASKS_LIST,
-                                                                         externalSystemId);
-    if (recentTasksList == null) {
-      return;
-    }
-    recentTasksList.setFirst(taskInfo);
-    
-    ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
-    assert manager != null;
-    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
-    settings.setRecentTasks(recentTasksList.getModel().getTasks());
-  }
-
   @Nullable
   public static String getRunnerId(@NotNull String executorId) {
     return RUNNER_IDS.get(executorId);
@@ -904,6 +896,26 @@ public class ExternalSystemUtil {
     return file[0];
   }
 
+  public static void scheduleExternalViewStructureUpdate(final Project project, final ProjectSystemId systemId) {
+    ExternalProjectsView externalProjectsView = ExternalProjectsManager.getInstance(project).getExternalProjectsView(systemId);
+    if (externalProjectsView != null) {
+      externalProjectsView.scheduleStructureUpdate();
+    }
+  }
+
+  @Nullable
+  public static ExternalProjectInfo getExternalProjectInfo(@NotNull final Project project,
+                                                           @NotNull final ProjectSystemId projectSystemId,
+                                                           @NotNull final String externalProjectPath) {
+    final ExternalProjectSettings linkedProjectSettings =
+      ExternalSystemApiUtil.getSettings(project, projectSystemId).getLinkedProjectSettings(externalProjectPath);
+    if (linkedProjectSettings == null) return null;
+
+    return ProjectDataManager.getInstance().getExternalProjectData(
+      project, projectSystemId, linkedProjectSettings.getExternalProjectPath());
+  }
+
+
   private interface TaskUnderProgress {
     void execute(@NotNull ProgressIndicator indicator);
   }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsStructure.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsStructure.java
new file mode 100644 (file)
index 0000000..0eb2d5e
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.ui.treeStructure.SimpleTreeBuilder;
+import com.intellij.ui.treeStructure.SimpleTreeStructure;
+import com.intellij.util.Consumer;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/22/2014
+ */
+public class ExternalProjectsStructure extends SimpleTreeStructure {
+  private final Project myProject;
+  private final ExternalProjectsView myExternalProjectsView;
+  private final SimpleTreeBuilder myTreeBuilder;
+  private final RootNode myRoot;
+
+  private final Map<String, ExternalSystemNode> myNodeMapping = new THashMap<String, ExternalSystemNode>();
+
+  public ExternalProjectsStructure(Project project, ExternalProjectsView externalProjectsView, SimpleTree tree) {
+    myProject = project;
+    myExternalProjectsView = externalProjectsView;
+
+    configureTree(tree);
+
+    myTreeBuilder = new SimpleTreeBuilder(tree, (DefaultTreeModel)tree.getModel(), this, null);
+    Disposer.register(myProject, myTreeBuilder);
+
+    myRoot = new RootNode();
+    myTreeBuilder.initRoot();
+    myTreeBuilder.expand(myRoot, null);
+  }
+
+  public Project getProject() {
+    return myProject;
+  }
+
+  void updateFrom(SimpleNode node) {
+    myTreeBuilder.addSubtreeToUpdateByElement(node);
+  }
+
+  void updateUpTo(SimpleNode node) {
+    SimpleNode each = node;
+    while (each != null) {
+      updateFrom(each);
+      each = each.getParent();
+    }
+  }
+
+  @Override
+  public Object getRootElement() {
+    return myRoot;
+  }
+
+  private static void configureTree(final SimpleTree tree) {
+    tree.setRootVisible(false);
+    tree.setShowsRootHandles(true);
+  }
+
+  public void updateProjects(Collection<DataNode<ProjectData>> toImport) {
+    for (DataNode<ProjectData> each : toImport) {
+      final ProjectData projectData = each.getData();
+      ExternalSystemNode projectNode = findNodeFor(projectData.getLinkedExternalProjectPath());
+
+      if (projectNode instanceof ProjectNode) {
+        doMergeChildrenChanges(projectNode, each, new ProjectNode(myExternalProjectsView, each));
+      }
+      else {
+        ExternalSystemNode node = myNodeMapping.remove(projectData.getLinkedExternalProjectPath());
+        if (node != null) {
+          SimpleNode parent = node.getParent();
+          if (parent instanceof ExternalSystemNode) {
+            ((ExternalSystemNode)parent).remove(projectNode);
+          }
+        }
+
+        projectNode = new ProjectNode(myExternalProjectsView, each);
+        myNodeMapping.put(projectData.getLinkedExternalProjectPath(), projectNode);
+      }
+      if (toImport.size() == 0) {
+        myTreeBuilder.expand(projectNode, null);
+      }
+      doUpdateProject((ProjectNode)projectNode);
+    }
+  }
+
+  private void doMergeChildrenChanges(ExternalSystemNode currentNode, DataNode<?> newDataNode, ExternalSystemNode newNode) {
+    final ExternalSystemNode[] cached = currentNode.getCached();
+    if (cached != null) {
+      Map<Object, ExternalSystemNode> oldDataMap = ContainerUtil.newLinkedHashMap();
+      for (ExternalSystemNode node : cached) {
+        Object key = node.getData() != null ? node.getData() : node.getName();
+        oldDataMap.put(key, node);
+      }
+
+      Map<Object, ExternalSystemNode> newDataMap = ContainerUtil.newLinkedHashMap();
+      Map<Object, ExternalSystemNode> unchangedNewDataMap = ContainerUtil.newLinkedHashMap();
+      for (ExternalSystemNode node : newNode.getChildren()) {
+        Object key = node.getData() != null ? node.getData() : node.getName();
+        if (oldDataMap.remove(key) == null) {
+          newDataMap.put(key, node);
+        }
+        else {
+          unchangedNewDataMap.put(key, node);
+        }
+      }
+
+      currentNode.removeAll(oldDataMap.values());
+
+      for (ExternalSystemNode node : currentNode.getChildren()) {
+        Object key = node.getData() != null ? node.getData() : node.getName();
+        final ExternalSystemNode unchangedNewNode = unchangedNewDataMap.get(key);
+        if (unchangedNewNode != null) {
+          doMergeChildrenChanges(node, unchangedNewNode.myDataNode, unchangedNewNode);
+        }
+      }
+
+      updateFrom(currentNode);
+      currentNode.addAll(newDataMap.values());
+    }
+    //noinspection unchecked
+    currentNode.setDataNode(newDataNode);
+  }
+
+  private void doUpdateProject(ProjectNode node) {
+    ExternalSystemNode newParentNode = myRoot;
+    node.updateProject();
+    reconnectNode(node, newParentNode);
+  }
+
+  private static void reconnectNode(ProjectNode node, ExternalSystemNode newParentNode) {
+    ExternalSystemNode oldParentNode = node.getGroup();
+    if (oldParentNode == null || !oldParentNode.equals(newParentNode)) {
+      if (oldParentNode != null) {
+        oldParentNode.remove(node);
+      }
+      newParentNode.add(node);
+    }
+  }
+
+  @SuppressWarnings("SuspiciousMethodCalls")
+  private ExternalSystemNode findNodeFor(String projectPath) {
+    return myNodeMapping.get(projectPath);
+  }
+
+  public <T extends ExternalSystemNode> void updateNodes(@NotNull Class<T> nodeClass) {
+    for (T node : getNodes(nodeClass)) {
+      updateFrom(node);
+    }
+  }
+
+  public <T extends ExternalSystemNode> void visitNodes(@NotNull Class<T> nodeClass, @NotNull Consumer<T> consumer) {
+    for (T node : getNodes(nodeClass)) {
+      consumer.consume(node);
+    }
+  }
+
+  public class RootNode<T> extends ExternalSystemNode<T> {
+    public RootNode() {
+      super(myExternalProjectsView, null, null);
+    }
+
+    @Override
+    public boolean isVisible() {
+      return true;
+    }
+  }
+
+  public enum ErrorLevel {
+    NONE, ERROR
+  }
+
+  enum DisplayKind {
+    ALWAYS, NEVER, NORMAL
+  }
+
+  @NotNull
+  public <T extends ExternalSystemNode> List<T> getNodes(@NotNull Class<T> nodeClass) {
+    return doGetNodes(nodeClass, myRoot.getChildren(), new SmartList<T>());
+  }
+
+  @NotNull
+  private static <T extends ExternalSystemNode> List<T> doGetNodes(@NotNull Class<T> nodeClass,
+                                                                   SimpleNode[] nodes,
+                                                                   @NotNull List<T> result) {
+    if (nodes == null) return result;
+
+    for (SimpleNode node : nodes) {
+      if (nodeClass.isInstance(node)) {
+        //noinspection unchecked
+        result.add((T)node);
+      }
+      doGetNodes(nodeClass, node.getChildren(), result);
+    }
+    return result;
+  }
+
+  @NotNull
+  public <T extends ExternalSystemNode> List<T> getSelectedNodes(SimpleTree tree, Class<T> nodeClass) {
+    final List<T> filtered = new ArrayList<T>();
+    for (SimpleNode node : getSelectedNodes(tree)) {
+      if ((nodeClass != null) && (!nodeClass.isInstance(node))) {
+        filtered.clear();
+        break;
+      }
+      //noinspection unchecked
+      filtered.add((T)node);
+    }
+    return filtered;
+  }
+
+  private static List<SimpleNode> getSelectedNodes(SimpleTree tree) {
+    List<SimpleNode> nodes = new ArrayList<SimpleNode>();
+    TreePath[] treePaths = tree.getSelectionPaths();
+    if (treePaths != null) {
+      for (TreePath treePath : treePaths) {
+        nodes.add(tree.getNodeFor(treePath));
+      }
+    }
+    return nodes;
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsView.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsView.java
new file mode 100644 (file)
index 0000000..5bc77eb
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.execution.*;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.treeView.TreeState;
+import com.intellij.lang.Language;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.ExternalSystemManager;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.action.ExternalSystemViewGearAction;
+import com.intellij.openapi.externalSystem.model.*;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
+import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
+import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskLocation;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemShortcutsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
+import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListener;
+import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListenerAdapter;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
+import com.intellij.openapi.fileTypes.PlainTextFileType;
+import com.intellij.openapi.module.ModuleTypeId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.SimpleToolWindowPanel;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ex.ToolWindowEx;
+import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
+import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
+import com.intellij.openapi.wm.impl.ToolWindowImpl;
+import com.intellij.pom.Navigatable;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiFileFactory;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.impl.source.DummyHolderFactory;
+import com.intellij.ui.PopupHandler;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.util.Consumer;
+import com.intellij.util.DisposeAwareRunnable;
+import com.intellij.util.Function;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.messages.MessageBusConnection;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.TreeSelectionModel;
+import java.awt.*;
+import java.net.URL;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/19/2014
+ */
+public class ExternalProjectsView extends SimpleToolWindowPanel implements DataProvider {
+  public static final Logger LOG = Logger.getInstance(ExternalProjectsView.class);
+
+  @NotNull
+  private final Project myProject;
+  @NotNull
+  private final ExternalProjectsManager myProjectsManager;
+  @NotNull
+  private final ToolWindowEx myToolWindow;
+  @NotNull
+  private final ProjectSystemId myExternalSystemId;
+  @NotNull
+  private final ExternalSystemUiAware myUiAware;
+
+  @Nullable
+  private ExternalProjectsStructure myStructure;
+  private SimpleTree myTree;
+  @NotNull
+  private final NotificationGroup myNotificationGroup;
+
+  private ExternalProjectsViewState myState = new ExternalProjectsViewState();
+
+  public ExternalProjectsView(@NotNull Project project, @NotNull ToolWindowEx toolWindow, @NotNull ProjectSystemId externalSystemId) {
+    super(true, true);
+    myProject = project;
+    myToolWindow = toolWindow;
+    myExternalSystemId = externalSystemId;
+    myUiAware = ExternalSystemUiUtil.getUiAware(externalSystemId);
+    myProjectsManager = ExternalProjectsManager.getInstance(myProject);
+
+    String toolWindowId =
+      toolWindow instanceof ToolWindowImpl ? ((ToolWindowImpl)toolWindow).getId() : myExternalSystemId.getReadableName();
+
+    myNotificationGroup = NotificationGroup.toolWindowGroup(
+      "notification.group.id." + externalSystemId.getId().toLowerCase(Locale.ENGLISH), toolWindowId);
+  }
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (ExternalSystemDataKeys.VIEW.is(dataId)) return this;
+
+    if (PlatformDataKeys.HELP_ID.is(dataId)) return "reference.toolwindows.gradle";
+    if (CommonDataKeys.PROJECT.is(dataId)) return myProject;
+    if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) return extractVirtualFile();
+    if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) return extractVirtualFiles();
+    if (Location.DATA_KEY.is(dataId)) {
+      return extractLocation();
+    }
+    if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) return extractNavigatables();
+
+    if (ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.is(dataId)) return myExternalSystemId;
+    if (ExternalSystemDataKeys.UI_AWARE.is(dataId)) return myUiAware;
+    if (ExternalSystemDataKeys.SELECTED_PROJECT_NODE.is(dataId)) return getSelectedProjectNode();
+    if (ExternalSystemDataKeys.SELECTED_NODES.is(dataId)) return getSelectedNodes(ExternalSystemNode.class);
+    if (ExternalSystemDataKeys.PROJECTS_TREE.is(dataId)) return myTree;
+    if (ExternalSystemDataKeys.NOTIFICATION_GROUP.is(dataId)) return myNotificationGroup;
+
+    return super.getData(dataId);
+  }
+
+  @NotNull
+  public Project getProject() {
+    return myProject;
+  }
+
+  @NotNull
+  public ExternalSystemUiAware getUiAware() {
+    return myUiAware;
+  }
+
+  public ExternalSystemShortcutsManager getShortcutsManager() {
+    return myProjectsManager.getShortcutsManager();
+  }
+
+  public ExternalSystemTaskActivator getTaskActivator() {
+    return myProjectsManager.getTaskActivator();
+  }
+
+  @NotNull
+  public ProjectSystemId getSystemId() {
+    return myExternalSystemId;
+  }
+
+  @NotNull
+  public NotificationGroup getNotificationGroup() {
+    return myNotificationGroup;
+  }
+
+  public void init() {
+    initTree();
+
+    final ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(myProject);
+
+    final ToolWindowManagerAdapter listener = new ToolWindowManagerAdapter() {
+      boolean wasVisible = false;
+
+      @Override
+      public void stateChanged() {
+        if (myToolWindow.isDisposed()) return;
+        boolean visible = myToolWindow.isVisible();
+        if (!visible || wasVisible) {
+          wasVisible = visible;
+          return;
+        }
+        scheduleStructureUpdate();
+        wasVisible = true;
+      }
+    };
+    manager.addToolWindowManagerListener(listener);
+
+    Disposer.register(myProject, new Disposable() {
+      public void dispose() {
+        manager.removeToolWindowManagerListener(listener);
+      }
+    });
+
+    getShortcutsManager().addListener(new ExternalSystemShortcutsManager.Listener() {
+      @Override
+      public void shortcutsUpdated() {
+        scheduleTasksUpdate();
+
+        scheduleStructureRequest(new Runnable() {
+          public void run() {
+            assert myStructure != null;
+            myStructure.updateNodes(RunConfigurationNode.class);
+          }
+        });
+      }
+    });
+
+    getTaskActivator().addListener(new ExternalSystemTaskActivator.Listener() {
+      @Override
+      public void tasksActivationChanged() {
+        scheduleTasksUpdate();
+
+        scheduleStructureRequest(new Runnable() {
+          public void run() {
+            assert myStructure != null;
+            myStructure.updateNodes(RunConfigurationNode.class);
+          }
+        });
+      }
+    });
+
+    ((RunManagerEx)RunManager.getInstance(myProject)).addRunManagerListener(new RunManagerAdapter() {
+      private void changed() {
+        scheduleStructureRequest(new Runnable() {
+          public void run() {
+            assert myStructure != null;
+            myStructure.visitNodes(ModuleNode.class, new Consumer<ModuleNode>() {
+              @Override
+              public void consume(ModuleNode node) {
+                node.updateRunConfigurations();
+              }
+            });
+          }
+        });
+      }
+
+      @Override
+      public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) {
+        changed();
+      }
+
+      @Override
+      public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) {
+        changed();
+      }
+
+      @Override
+      public void runConfigurationChanged(@NotNull RunnerAndConfigurationSettings settings) {
+        changed();
+      }
+    });
+
+    ExternalSystemApiUtil.subscribe(myProject, myExternalSystemId, new ExternalSystemSettingsListenerAdapter(){
+      @Override
+      public void onUseAutoImportChange(boolean currentValue, @NotNull final String linkedProjectPath) {
+        scheduleStructureRequest(new Runnable() {
+          public void run() {
+            assert myStructure != null;
+            final List<ProjectNode> projectNodes = myStructure.getNodes(ProjectNode.class);
+            for (ProjectNode projectNode : projectNodes) {
+              final ProjectData projectData = projectNode.getData();
+              if(projectData != null && projectData.getLinkedExternalProjectPath().equals(linkedProjectPath)) {
+                projectNode.updateProject();
+                break;
+              }
+            }
+          }
+        });
+      }
+    });
+
+    ActionManager actionManager = ActionManager.getInstance();
+
+    DefaultActionGroup group = new DefaultActionGroup();
+    final AnAction gearAction = actionManager.getAction("ExternalSystem.GroupTasks");
+    if(gearAction instanceof ExternalSystemViewGearAction) {
+      ((ExternalSystemViewGearAction)gearAction).setView(this);
+      group.add(gearAction);
+    }
+
+    myToolWindow.setAdditionalGearActions(group);
+
+    scheduleStructureUpdate();
+  }
+
+  private void initStructure() {
+    myStructure = new ExternalProjectsStructure(myProject, this, myTree);
+  }
+
+  private void initTree() {
+    myTree = new SimpleTree();
+    myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
+
+    final ActionManager actionManager = ActionManager.getInstance();
+    ActionToolbar actionToolbar = actionManager.createActionToolbar(myExternalSystemId.getReadableName() + " View Toolbar",
+                                                                    (DefaultActionGroup)actionManager
+                                                                      .getAction("ExternalSystemView.ActionsToolbar"), true);
+
+    actionToolbar.setTargetComponent(myTree);
+    setToolbar(actionToolbar.getComponent());
+    setContent(ScrollPaneFactory.createScrollPane(myTree));
+
+    myTree.addMouseListener(new PopupHandler() {
+      public void invokePopup(final Component comp, final int x, final int y) {
+        final String id = getMenuId(getSelectedNodes(ExternalSystemNode.class));
+        if (id != null) {
+          final ActionGroup actionGroup = (ActionGroup)actionManager.getAction(id);
+          if (actionGroup != null) {
+            actionManager.createActionPopupMenu("", actionGroup).getComponent().show(comp, x, y);
+          }
+        }
+      }
+
+      @Nullable
+      private String getMenuId(Collection<? extends ExternalSystemNode> nodes) {
+        String id = null;
+        for (ExternalSystemNode node : nodes) {
+          String menuId = node.getMenuId();
+          if (menuId == null) {
+            return null;
+          }
+          if (id == null) {
+            id = menuId;
+          }
+          else if (!id.equals(menuId)) {
+            return null;
+          }
+        }
+        return id;
+      }
+    });
+  }
+
+  public void scheduleStructureUpdate() {
+    scheduleStructureRequest(new Runnable() {
+      public void run() {
+        final Collection<ExternalProjectInfo> projectsData =
+          ProjectDataManager.getInstance().getExternalProjectsData(myProject, myExternalSystemId);
+
+        final List<DataNode<ProjectData>> toImport =
+          ContainerUtil.mapNotNull(projectsData, new Function<ExternalProjectInfo, DataNode<ProjectData>>() {
+            @Override
+            public DataNode<ProjectData> fun(ExternalProjectInfo info) {
+              return info.getExternalProjectStructure();
+            }
+          });
+
+        assert myStructure != null;
+        myStructure.updateProjects(toImport);
+      }
+    });
+  }
+
+  protected boolean isUnitTestMode() {
+    return ApplicationManager.getApplication().isUnitTestMode();
+  }
+
+  public static void invokeLater(Project p, Runnable r) {
+    invokeLater(p, ModalityState.defaultModalityState(), r);
+  }
+
+  public static void invokeLater(final Project p, final ModalityState state, final Runnable r) {
+    if (isNoBackgroundMode()) {
+      r.run();
+    }
+    else {
+      ApplicationManager.getApplication().invokeLater(DisposeAwareRunnable.create(r, p), state);
+    }
+  }
+
+  public static boolean isNoBackgroundMode() {
+    return (ApplicationManager.getApplication().isUnitTestMode()
+            || ApplicationManager.getApplication().isHeadlessEnvironment());
+  }
+
+  public void updateUpTo(ExternalSystemNode node) {
+    if (myStructure != null) {
+      myStructure.updateUpTo(node);
+    }
+  }
+
+  @Nullable
+  protected ExternalProjectsStructure getStructure() {
+    return myStructure;
+  }
+
+  @NotNull
+  public List<ExternalSystemNode<?>> createNodes(@Nullable ExternalSystemNode<?> parent, @NotNull DataNode<?> dataNode) {
+    final List<ExternalSystemNode<?>> result = new SmartList<ExternalSystemNode<?>>();
+    final Map<Key<?>, List<DataNode<?>>> groups = ExternalSystemApiUtil.group(dataNode.getChildren());
+    for (ExternalSystemViewContributor contributor : ExternalSystemViewContributor.EP_NAME.getExtensions()) {
+      List<Key<?>> keys = contributor.getKeys();
+
+      final MultiMap<Key<?>, DataNode<?>> dataNodes = MultiMap.create();
+      for (Key<?> key : keys) {
+        final List<DataNode<?>> values = groups.get(key);
+        if(key != null && values != null) {
+          dataNodes.put(key, values);
+        }
+      }
+
+      if (dataNodes.isEmpty()) continue;
+
+      final List<ExternalSystemNode<?>> childNodes = contributor.createNodes(this, dataNodes);
+      result.addAll(childNodes);
+
+      if (parent == null) continue;
+
+      for (ExternalSystemNode childNode : childNodes) {
+        childNode.setParent(parent);
+      }
+    }
+
+    return result;
+  }
+
+  @Nullable
+  public ExternalProjectsViewState getState() {
+    ApplicationManager.getApplication().assertIsDispatchThread();
+    if (myStructure != null) {
+      try {
+        myState.treeState = new Element("root");
+        TreeState.createOn(myTree).writeExternal(myState.treeState);
+      }
+      catch (WriteExternalException e) {
+        LOG.warn(e);
+      }
+    }
+    return myState;
+  }
+
+  public void loadState(ExternalProjectsViewState state) {
+    myState = state;
+  }
+
+  public boolean getGroupTasks() {
+    return myState.groupTasks;
+  }
+
+  public void setGroupTasks(boolean value) {
+    if (myState.groupTasks != value) {
+      myState.groupTasks = value;
+      scheduleStructureRequest(new Runnable() {
+        public void run() {
+          assert myStructure != null;
+          final List<TasksNode> tasksNodes = myStructure.getNodes(TasksNode.class);
+          for (TasksNode tasksNode : tasksNodes) {
+            tasksNode.cleanUpCache();
+            updateUpTo(tasksNode);
+          }
+        }
+      });
+    }
+  }
+
+  private void scheduleTasksUpdate() {
+    scheduleStructureRequest(new Runnable() {
+      public void run() {
+        assert myStructure != null;
+        myStructure.updateNodes(TaskNode.class);
+      }
+    });
+  }
+
+  private void scheduleStructureRequest(final Runnable r) {
+    if (isUnitTestMode()) {
+      r.run();
+      return;
+    }
+
+    invokeLater(myProject, new Runnable() {
+      public void run() {
+        if (!myToolWindow.isVisible()) return;
+
+        boolean shouldCreate = myStructure == null;
+        if (shouldCreate) {
+          initStructure();
+        }
+
+        myTree.setPaintBusy(true);
+        try {
+          r.run();
+          if (shouldCreate) {
+            restoreTreeState();
+          }
+        }
+        finally {
+          myTree.setPaintBusy(false);
+        }
+      }
+    });
+  }
+
+  private void restoreTreeState() {
+    if (myState.treeState != null) {
+      TreeState treeState = new TreeState();
+      try {
+        treeState.readExternal(myState.treeState);
+        treeState.applyTo(myTree);
+      }
+      catch (InvalidDataException e) {
+        LOG.info(e);
+      }
+    }
+  }
+
+  private <T extends ExternalSystemNode> List<T> getSelectedNodes(Class<T> aClass) {
+    return myStructure != null ? myStructure.getSelectedNodes(myTree, aClass) : ContainerUtil.<T>emptyList();
+  }
+
+  private List<ProjectNode> getSelectedProjectNodes() {
+    return getSelectedNodes(ProjectNode.class);
+  }
+
+  @Nullable
+  private ProjectNode getSelectedProjectNode() {
+    final List<ProjectNode> projectNodes = getSelectedProjectNodes();
+    return projectNodes.size() == 1 ? projectNodes.get(0) : null;
+  }
+
+  @Nullable
+  private Location extractLocation() {
+    final List<ExternalSystemNode> selectedNodes = getSelectedNodes(ExternalSystemNode.class);
+    if (selectedNodes.isEmpty()) return null;
+
+    List<TaskData> tasks = ContainerUtil.newSmartList();
+
+    ExternalTaskExecutionInfo taskExecutionInfo = new ExternalTaskExecutionInfo();
+
+    String projectPath = null;
+
+    for (ExternalSystemNode node : selectedNodes) {
+      final Object data = node.getData();
+      if (data instanceof TaskData) {
+        final TaskData taskData = (TaskData)data;
+        if (projectPath == null) {
+          projectPath = taskData.getLinkedExternalProjectPath();
+        }
+        else if (!taskData.getLinkedExternalProjectPath().equals(projectPath)) {
+          return null;
+        }
+
+        taskExecutionInfo.getSettings().getTaskNames().add(taskData.getName());
+        taskExecutionInfo.getSettings().getTaskDescriptions().add(taskData.getDescription());
+        tasks.add(taskData);
+      }
+    }
+
+    if(tasks.isEmpty()) return null;
+
+    taskExecutionInfo.getSettings().setExternalSystemIdString(myExternalSystemId.toString());
+    taskExecutionInfo.getSettings().setExternalProjectPath(projectPath);
+
+    String name = myExternalSystemId.getReadableName() + projectPath + StringUtil.join(taskExecutionInfo.getSettings().getTaskNames(), " ");
+    // We create a dummy text file instead of re-using external system file in order to avoid clashing with other configuration producers.
+    // For example gradle files are enhanced groovy scripts but we don't want to run them via regular IJ groovy script runners.
+    // Gradle tooling api should be used for running gradle tasks instead. IJ execution sub-system operates on Location objects
+    // which encapsulate PsiElement and groovy runners are automatically applied if that PsiElement IS-A GroovyFile.
+    PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(name, PlainTextFileType.INSTANCE, "");
+    return new ExternalSystemTaskLocation(myProject, file, taskExecutionInfo);
+  }
+
+  private VirtualFile extractVirtualFile() {
+    for (ExternalSystemNode each : getSelectedNodes(ExternalSystemNode.class)) {
+      VirtualFile file = each.getVirtualFile();
+      if (file != null && file.isValid()) return file;
+    }
+
+    final ProjectNode projectNode = getSelectedProjectNode();
+    if (projectNode == null) return null;
+    VirtualFile file = projectNode.getVirtualFile();
+    if (file == null || !file.isValid()) return null;
+    return file;
+  }
+
+  private Object extractVirtualFiles() {
+    final List<VirtualFile> files = new ArrayList<VirtualFile>();
+    for (ExternalSystemNode each : getSelectedNodes(ExternalSystemNode.class)) {
+      VirtualFile file = each.getVirtualFile();
+      if (file != null && file.isValid()) files.add(file);
+    }
+    return files.isEmpty() ? null : VfsUtilCore.toVirtualFileArray(files);
+  }
+
+  private Object extractNavigatables() {
+    final List<Navigatable> navigatables = new ArrayList<Navigatable>();
+    for (ExternalSystemNode each : getSelectedNodes(ExternalSystemNode.class)) {
+      Navigatable navigatable = each.getNavigatable();
+      if (navigatable != null) navigatables.add(navigatable);
+    }
+    return navigatables.isEmpty() ? null : navigatables.toArray(new Navigatable[navigatables.size()]);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemNode.java
new file mode 100644 (file)
index 0000000..dbe5e0d
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.action.ExternalSystemActionUtil;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemShortcutsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.treeStructure.SimpleNode;
+import com.intellij.ui.treeStructure.SimpleTree;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.awt.event.InputEvent;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/15/2014
+ */
+public abstract class ExternalSystemNode<T> extends SimpleNode implements Comparable<ExternalSystemNode> {
+
+
+  @NotNull public static final Comparator<ExternalSystemNode> ORDER_AWARE_COMPARATOR = new Comparator<ExternalSystemNode>() {
+
+    @Override
+    public int compare(@NotNull ExternalSystemNode o1, @NotNull ExternalSystemNode o2) {
+      int order1 = getOrder(o1);
+      int order2 = getOrder(o2);
+      if (order1 == order2) return o1.compareTo(o2);
+      return order1 < order2 ? -1 : 1;
+    }
+
+    private int getOrder(@NotNull Comparable o) {
+      Order annotation = o.getClass().getAnnotation(Order.class);
+      if (annotation != null) {
+        return annotation.value();
+      }
+      return 0;
+    }
+  };
+
+
+  protected static final ExternalSystemNode[] NO_CHILDREN = new ExternalSystemNode[0];
+
+  private final ExternalProjectsView myExternalProjectsView;
+  private final List<ExternalSystemNode<?>> myChildrenList = ContainerUtil.newArrayList();
+  protected DataNode<T> myDataNode;
+  @Nullable
+  private ExternalSystemNode myParent;
+  private ExternalSystemNode[] myChildren;
+  private ExternalProjectsStructure.ErrorLevel myErrorLevel = ExternalProjectsStructure.ErrorLevel.NONE;
+  private ExternalProjectsStructure.ErrorLevel myTotalErrorLevel = null;
+
+  public ExternalSystemNode(@NotNull ExternalProjectsView externalProjectsView,
+                            @Nullable ExternalSystemNode parent) {
+    this(externalProjectsView, parent, null);
+  }
+
+  public ExternalSystemNode(@NotNull ExternalProjectsView externalProjectsView,
+                            @Nullable ExternalSystemNode parent,
+                            @Nullable DataNode<T> dataNode) {
+    super(externalProjectsView.getProject(), null);
+    myExternalProjectsView = externalProjectsView;
+    myDataNode = dataNode;
+    myParent = parent;
+  }
+
+  public void setParent(@Nullable ExternalSystemNode parent) {
+    myParent = parent;
+  }
+
+  @Nullable
+  public T getData() {
+    return myDataNode != null ? myDataNode.getData() : null;
+  }
+
+  @Override
+  public NodeDescriptor getParentDescriptor() {
+    return myParent;
+  }
+
+  protected ExternalProjectsView getExternalProjectsView() {
+    return myExternalProjectsView;
+  }
+
+  protected ExternalSystemUiAware getUiAware() {
+    return myExternalProjectsView.getUiAware();
+  }
+
+  protected ExternalProjectsStructure getStructure() {
+    return myExternalProjectsView.getStructure();
+  }
+
+  protected ExternalSystemShortcutsManager getShortcutsManager() {
+    return myExternalProjectsView.getShortcutsManager();
+  }
+
+  protected ExternalSystemTaskActivator getTaskActivator() {
+    return myExternalProjectsView.getTaskActivator();
+  }
+
+  @Nullable
+  public <T extends ExternalSystemNode> T findParent(Class<T> parentClass) {
+    ExternalSystemNode node = this;
+    while (true) {
+      node = node.myParent;
+      if (node == null || parentClass.isInstance(node)) {
+        //noinspection unchecked
+        return (T)node;
+      }
+    }
+  }
+
+  @Nullable
+  public <DataType> DataType findParentData(Class<DataType> parentDataClass) {
+    ExternalSystemNode node = this;
+    while (true) {
+      node = node.myParent;
+      if (node == null) return null;
+      if (node.getData() != null && parentDataClass.isInstance(node.getData())) {
+        //noinspection unchecked
+        return (DataType)node.getData();
+      }
+    }
+  }
+
+  public boolean isVisible() {
+    return getDisplayKind() != ExternalProjectsStructure.DisplayKind.NEVER;
+  }
+
+  public ExternalProjectsStructure.DisplayKind getDisplayKind() {
+    return ExternalProjectsStructure.DisplayKind.NORMAL;
+  }
+
+  @NotNull
+  public final ExternalSystemNode[] getChildren() {
+    if (myChildren == null) {
+      myChildren = buildChildren();
+      onChildrenBuilt();
+    }
+    return myChildren;
+  }
+
+  protected void onChildrenBuilt() {
+  }
+
+  @NotNull
+  private ExternalSystemNode[] buildChildren() {
+    List<? extends ExternalSystemNode> newChildrenCandidates = doBuildChildren();
+    if (newChildrenCandidates.isEmpty()) return NO_CHILDREN;
+
+    addAll(newChildrenCandidates, true);
+    sort(myChildrenList);
+    List<ExternalSystemNode> visibleNodes = new ArrayList<ExternalSystemNode>();
+    for (ExternalSystemNode each : myChildrenList) {
+      if (each.isVisible()) visibleNodes.add(each);
+    }
+    return visibleNodes.toArray(new ExternalSystemNode[visibleNodes.size()]);
+  }
+
+  public void cleanUpCache() {
+    myChildren = null;
+    myChildrenList.clear();
+    myTotalErrorLevel = null;
+  }
+
+  @Nullable
+  protected ExternalSystemNode[] getCached() {
+    return myChildren;
+  }
+
+  protected void sort(List<? extends ExternalSystemNode> list) {
+    Collections.sort(list, ORDER_AWARE_COMPARATOR);
+  }
+
+  protected boolean addAll(Collection<? extends ExternalSystemNode> externalSystemNodes) {
+    return addAll(externalSystemNodes, false);
+  }
+
+  private boolean addAll(Collection<? extends ExternalSystemNode> externalSystemNodes, boolean silently) {
+    if (externalSystemNodes.isEmpty()) return false;
+
+    for (ExternalSystemNode externalSystemNode : externalSystemNodes) {
+      externalSystemNode.setParent(this);
+      myChildrenList.add(externalSystemNode);
+    }
+    if (!silently) {
+      childrenChanged();
+    }
+
+    return true;
+  }
+
+  protected boolean add(ExternalSystemNode externalSystemNode) {
+    return addAll(ContainerUtil.list(externalSystemNode));
+  }
+
+  protected boolean removeAll(Collection<ExternalSystemNode> externalSystemNodes) {
+    return removeAll(externalSystemNodes, false);
+  }
+
+  private boolean removeAll(Collection<ExternalSystemNode> externalSystemNodes, boolean silently) {
+    if (externalSystemNodes.isEmpty()) return false;
+
+    for (ExternalSystemNode externalSystemNode : externalSystemNodes) {
+      externalSystemNode.setParent(null);
+      myChildrenList.remove(externalSystemNode);
+    }
+    if (!silently) {
+      childrenChanged();
+    }
+
+    return true;
+  }
+
+  public void remove(ExternalSystemNode externalSystemNode) {
+    removeAll(ContainerUtil.list(externalSystemNode));
+  }
+
+  protected void childrenChanged() {
+    ExternalSystemNode each = this;
+    while (each != null) {
+      each.myTotalErrorLevel = null;
+      each = (ExternalSystemNode)each.getParent();
+    }
+
+    sort(myChildrenList);
+    final List<ExternalSystemNode<?>> visibleNodes = ContainerUtil.filter(myChildrenList, new Condition<ExternalSystemNode<?>>() {
+      @Override
+      public boolean value(ExternalSystemNode<?> node) {
+        return node.isVisible();
+      }
+    });
+    myChildren = visibleNodes.toArray(new ExternalSystemNode[visibleNodes.size()]);
+    myExternalProjectsView.updateUpTo(this);
+  }
+
+  public boolean hasChildren() {
+    return getChildren().length > 0;
+  }
+
+  @NotNull
+  protected List<? extends ExternalSystemNode> doBuildChildren() {
+    if (myDataNode != null && !myDataNode.getChildren().isEmpty()) {
+      return getExternalProjectsView().createNodes(this, myDataNode);
+    }
+    else {
+      return myChildrenList;
+    }
+  }
+
+  public void setDataNode(DataNode<T> dataNode) {
+    myDataNode = dataNode;
+  }
+
+  public ExternalProjectsStructure.ErrorLevel getTotalErrorLevel() {
+    if (myTotalErrorLevel == null) {
+      myTotalErrorLevel = calcTotalErrorLevel();
+    }
+    return myTotalErrorLevel;
+  }
+
+  private ExternalProjectsStructure.ErrorLevel calcTotalErrorLevel() {
+    ExternalProjectsStructure.ErrorLevel childrenErrorLevel = getChildrenErrorLevel();
+    return childrenErrorLevel.compareTo(myErrorLevel) > 0 ? childrenErrorLevel : myErrorLevel;
+  }
+
+  public ExternalProjectsStructure.ErrorLevel getChildrenErrorLevel() {
+    ExternalProjectsStructure.ErrorLevel result = ExternalProjectsStructure.ErrorLevel.NONE;
+    for (SimpleNode each : getChildren()) {
+      ExternalProjectsStructure.ErrorLevel eachLevel = ((ExternalSystemNode)each).getTotalErrorLevel();
+      if (eachLevel.compareTo(result) > 0) result = eachLevel;
+    }
+    return result;
+  }
+
+  public void setErrorLevel(ExternalProjectsStructure.ErrorLevel level) {
+    if (myErrorLevel == level) return;
+    myErrorLevel = level;
+    myExternalProjectsView.updateUpTo(this);
+  }
+
+  @Override
+  protected void doUpdate() {
+    setNameAndTooltip(getName(), null);
+  }
+
+  protected void setNameAndTooltip(String name, @Nullable String tooltip) {
+    setNameAndTooltip(name, tooltip, (String)null);
+  }
+
+  protected void setNameAndTooltip(String name, @Nullable String tooltip, @Nullable String hint) {
+    setNameAndTooltip(name, tooltip, getPlainAttributes());
+    if (!StringUtil.isEmptyOrSpaces(hint)) {
+      addColoredFragment(" (" + hint + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
+    }
+  }
+
+  protected void setNameAndTooltip(String name, @Nullable String tooltip, SimpleTextAttributes attributes) {
+    clearColoredText();
+    addColoredFragment(name, prepareAttributes(attributes));
+    getTemplatePresentation().setTooltip(tooltip);
+  }
+
+  private SimpleTextAttributes prepareAttributes(SimpleTextAttributes from) {
+    ExternalProjectsStructure.ErrorLevel level = getTotalErrorLevel();
+    Color waveColor = level == ExternalProjectsStructure.ErrorLevel.NONE ? null : JBColor.RED;
+    int style = from.getStyle();
+    if (waveColor != null) style |= SimpleTextAttributes.STYLE_WAVED;
+    return new SimpleTextAttributes(from.getBgColor(), from.getFgColor(), waveColor, style);
+  }
+
+  @Nullable
+  @NonNls
+  protected String getActionId() {
+    return null;
+  }
+
+  @Nullable
+  @NonNls
+  protected String getMenuId() {
+    return null;
+  }
+
+  protected String message(@NotNull String key, @NotNull Object... params) {
+    return ExternalSystemBundle.message(key);
+  }
+
+  @Nullable
+  public VirtualFile getVirtualFile() {
+    return null;
+  }
+
+  @Nullable
+  public Navigatable getNavigatable() {
+    return null;
+  }
+
+  @Override
+  public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
+    String actionId = getActionId();
+    if (actionId != null) {
+      ExternalSystemActionUtil.executeAction(actionId, inputEvent);
+    }
+  }
+
+  @Override
+  public int compareTo(@NotNull ExternalSystemNode node) {
+    return StringUtil.compare(this.getName(), node.getName(), true);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewContributor.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewContributor.java
new file mode 100644 (file)
index 0000000..7fee212
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/10/2014
+ */
+public abstract class ExternalSystemViewContributor {
+  public static final ExtensionPointName<ExternalSystemViewContributor> EP_NAME =
+    ExtensionPointName.create("com.intellij.externalSystemViewContributor");
+
+  @NotNull
+  public abstract List<Key<?>> getKeys();
+
+  @NotNull
+  public abstract List<ExternalSystemNode<?>> createNodes(
+    ExternalProjectsView externalProjectsView, MultiMap<Key<?>, DataNode<?>> dataNodes);
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewDefaultContributor.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewDefaultContributor.java
new file mode 100644 (file)
index 0000000..18fdfa0
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
+import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
+import com.intellij.openapi.externalSystem.service.project.ProjectStructureHelper;
+import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.pom.Navigatable;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/10/2014
+ */
+public class ExternalSystemViewDefaultContributor extends ExternalSystemViewContributor {
+
+  private static final Key<?>[] KEYS = new Key[]{
+    ProjectKeys.MODULE,
+    ProjectKeys.MODULE_DEPENDENCY,
+    ProjectKeys.LIBRARY_DEPENDENCY,
+    ProjectKeys.TASK
+  };
+
+
+  @NotNull
+  @Override
+  public List<Key<?>> getKeys() {
+    return Arrays.asList(KEYS);
+  }
+
+  @Override
+  @NotNull
+  public List<ExternalSystemNode<?>> createNodes(final ExternalProjectsView externalProjectsView,
+                                                 final MultiMap<Key<?>, DataNode<?>> dataNodes) {
+    final List<ExternalSystemNode<?>> result = new SmartList<ExternalSystemNode<?>>();
+
+    addModuleNodes(externalProjectsView, dataNodes, result);
+    // add tasks
+    result.add(new TasksNode(externalProjectsView, dataNodes.get(ProjectKeys.TASK)));
+    addDependenciesNode(externalProjectsView, dataNodes, result);
+
+    return result;
+  }
+
+  private static void addDependenciesNode(@NotNull ExternalProjectsView externalProjectsView,
+                                          @NotNull MultiMap<Key<?>, DataNode<?>> dataNodes,
+                                          @NotNull List<ExternalSystemNode<?>> result) {
+    final Collection<DataNode<?>> moduleDeps = dataNodes.get(ProjectKeys.MODULE_DEPENDENCY);
+    final Collection<DataNode<?>> libDeps = dataNodes.get(ProjectKeys.LIBRARY_DEPENDENCY);
+
+    if (!moduleDeps.isEmpty() || !libDeps.isEmpty()) {
+      final ExternalSystemNode depNode = new MyDependenciesNode(externalProjectsView);
+
+      for (DataNode<?> dataNode : moduleDeps) {
+        if (!(dataNode.getData() instanceof ModuleDependencyData)) continue;
+        //noinspection unchecked
+        depNode.add(new ModuleDependencyDataExternalSystemNode(externalProjectsView, (DataNode<ModuleDependencyData>)dataNode));
+      }
+
+      for (DataNode<?> dataNode : libDeps) {
+        if (!(dataNode.getData() instanceof LibraryDependencyData)) continue;
+        //noinspection unchecked
+        final ExternalSystemNode<LibraryDependencyData> libraryDependencyDataExternalSystemNode =
+          new LibraryDependencyDataExternalSystemNode(externalProjectsView, (DataNode<LibraryDependencyData>)dataNode);
+
+        depNode.add(libraryDependencyDataExternalSystemNode);
+        libraryDependencyDataExternalSystemNode.setErrorLevel(((LibraryDependencyData)dataNode.getData()).getTarget().isUnresolved()
+                                                              ? ExternalProjectsStructure.ErrorLevel.ERROR
+                                                              : ExternalProjectsStructure.ErrorLevel.NONE);
+      }
+
+      result.add(depNode);
+    }
+  }
+
+  private static void addModuleNodes(@NotNull ExternalProjectsView externalProjectsView,
+                                     @NotNull MultiMap<Key<?>, DataNode<?>> dataNodes,
+                                     @NotNull List<ExternalSystemNode<?>> result) {
+    final Collection<DataNode<?>> moduleDataNodes = dataNodes.get(ProjectKeys.MODULE);
+    if (!moduleDataNodes.isEmpty()) {
+      final AbstractExternalSystemSettings systemSettings =
+        ExternalSystemApiUtil.getSettings(externalProjectsView.getProject(), externalProjectsView.getSystemId());
+
+      for (DataNode<?> dataNode : moduleDataNodes) {
+        final ModuleData data = (ModuleData)dataNode.getData();
+
+        final ExternalProjectSettings projectSettings = systemSettings.getLinkedProjectSettings(data.getLinkedExternalProjectPath());
+        final boolean isRoot =
+          projectSettings != null && data.getLinkedExternalProjectPath().equals(projectSettings.getExternalProjectPath());
+        //noinspection unchecked
+        final ModuleNode moduleNode = new ModuleNode(externalProjectsView, (DataNode<ModuleData>)dataNode, isRoot);
+        result.add(moduleNode);
+      }
+    }
+  }
+
+  @Order(2)
+  private static class MyDependenciesNode extends ExternalSystemNode {
+    public MyDependenciesNode(ExternalProjectsView externalProjectsView) {
+      //noinspection unchecked
+      super(externalProjectsView, null, null);
+    }
+
+    @Override
+    protected void update(PresentationData presentation) {
+      super.update(presentation);
+      presentation.setIcon(AllIcons.Nodes.PpLibFolder);
+    }
+
+    @Override
+    public String getName() {
+      return "Dependencies";
+    }
+  }
+
+  private static class ModuleDependencyDataExternalSystemNode extends ExternalSystemNode<ModuleDependencyData> {
+
+    public ModuleDependencyDataExternalSystemNode(ExternalProjectsView externalProjectsView, DataNode<ModuleDependencyData> dataNode) {
+      super(externalProjectsView, null, dataNode);
+    }
+
+    @Override
+    protected void update(PresentationData presentation) {
+      super.update(presentation);
+      presentation.setIcon(getUiAware().getProjectIcon());
+
+      final ModuleDependencyData data = getData();
+      if (data != null) {
+        setNameAndTooltip(getName(), null, data.getScope().getDisplayName());
+      }
+    }
+
+    @Override
+    public String getName() {
+      final ModuleDependencyData data = getData();
+      return data != null ? data.getExternalName() : "";
+    }
+
+    @Override
+    public boolean isAlwaysLeaf() {
+      return true;
+    }
+  }
+
+  private static class LibraryDependencyDataExternalSystemNode extends ExternalSystemNode<LibraryDependencyData> {
+
+    public LibraryDependencyDataExternalSystemNode(ExternalProjectsView externalProjectsView, DataNode<LibraryDependencyData> dataNode) {
+      super(externalProjectsView, null, dataNode);
+    }
+
+    @Override
+    protected void update(PresentationData presentation) {
+      super.update(presentation);
+      presentation.setIcon(AllIcons.Nodes.PpLib);
+
+      final LibraryDependencyData data = getData();
+      if (data != null) {
+        setNameAndTooltip(getName(), null, data.getScope().getDisplayName());
+      }
+    }
+
+    @Override
+    public String getName() {
+      final LibraryDependencyData data = getData();
+      return data != null ? data.getExternalName() : "";
+    }
+
+    @Override
+    public boolean isAlwaysLeaf() {
+      return true;
+    }
+
+    @Nullable
+    @Override
+    public Navigatable getNavigatable() {
+      return new Navigatable() {
+        @Nullable
+        private OrderEntry myOrderEntry;
+
+        @Override
+        public void navigate(boolean requestFocus) {
+          if (myOrderEntry != null) {
+            ProjectSettingsService.getInstance(myProject).openLibraryOrSdkSettings(myOrderEntry);
+          }
+        }
+
+        @Override
+        public boolean canNavigate() {
+          myOrderEntry = getOrderEntry();
+          return myOrderEntry != null;
+        }
+
+        @Override
+        public boolean canNavigateToSource() {
+          return true;
+        }
+      };
+    }
+
+    @Nullable
+    private OrderEntry getOrderEntry() {
+      final LibraryDependencyData data = getData();
+      if (data == null) return null;
+      final Project project = getProject();
+      if (project == null) return null;
+      return ServiceManager.getService(ProjectStructureHelper.class).findIdeModuleOrderEntry(data, project);
+    }
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ModuleNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ModuleNode.java
new file mode 100644 (file)
index 0000000..31e88e9
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/7/2014
+ */
+@Order(1)
+public class ModuleNode extends ExternalSystemNode<ModuleData> {
+  private final boolean myIsRoot;
+  private final ModuleData myData;
+  private final RunConfigurationsNode myRunConfigurationsNode;
+
+  public ModuleNode(ExternalProjectsView externalProjectsView,
+                    DataNode<ModuleData> dataNode,
+                    boolean isRoot) {
+    super(externalProjectsView, null, dataNode);
+    myIsRoot = isRoot;
+    myData = dataNode.getData();
+    myRunConfigurationsNode = new RunConfigurationsNode(externalProjectsView, this);
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(getUiAware().getProjectIcon());
+
+    String hint = null;
+    if (myIsRoot) {
+      hint = "root";
+    }
+
+    setNameAndTooltip(getName(), myData.toString(), hint);
+  }
+
+  @NotNull
+  @Override
+  protected List<? extends ExternalSystemNode> doBuildChildren() {
+    List<ExternalSystemNode<?>> myChildNodes = ContainerUtil.newArrayList();
+    //noinspection unchecked
+    myChildNodes.addAll((Collection<? extends ExternalSystemNode<?>>)super.doBuildChildren());
+    myChildNodes.add(myRunConfigurationsNode);
+    return myChildNodes;
+  }
+
+  @Override
+  public String getName() {
+    return myData.getId();
+  }
+
+  @Nullable
+  @Override
+  protected String getMenuId() {
+    return "ExternalSystemView.ProjectMenu";
+  }
+
+  @Override
+  public boolean isVisible() {
+    return true;
+  }
+
+  @Override
+  public int compareTo(@NotNull ExternalSystemNode node) {
+    return myIsRoot ? -1 : (node instanceof ModuleNode && ((ModuleNode)node).myIsRoot) ? 1 : super.compareTo(node);
+  }
+
+  public void updateRunConfigurations() {
+    myRunConfigurationsNode.updateRunConfigurations();
+    childrenChanged();
+    getExternalProjectsView().updateUpTo(this);
+    getExternalProjectsView().updateUpTo(myRunConfigurationsNode);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ProjectNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ProjectNode.java
new file mode 100644 (file)
index 0000000..78bd31e
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
+import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.util.Condition;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/15/2014
+ */
+public class ProjectNode extends ExternalSystemNode<ProjectData> {
+  private String myTooltipCache;
+
+  public ProjectNode(ExternalProjectsView externalProjectsView, DataNode<ProjectData> projectDataNode) {
+    super(externalProjectsView, null, projectDataNode);
+    updateProject();
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(getUiAware().getProjectIcon());
+  }
+
+  public ExternalSystemNode getGroup() {
+    return (ExternalSystemNode)getParent();
+  }
+
+  @NotNull
+  @Override
+  protected List<? extends ExternalSystemNode> doBuildChildren() {
+    final List<? extends ExternalSystemNode> children = super.doBuildChildren();
+    final List<ExternalSystemNode> visibleChildren = ContainerUtil.filter(children, new Condition<ExternalSystemNode>() {
+      @Override
+      public boolean value(ExternalSystemNode node) {
+        return node.isVisible();
+      }
+    });
+    //noinspection unchecked
+    return visibleChildren.size() == 1 ? children.get(0).doBuildChildren() : children;
+  }
+
+  void updateProject() {
+    myTooltipCache = makeDescription();
+    getStructure().updateFrom(getParent());
+  }
+
+  @Override
+  public String getName() {
+    final ProjectData projectData = getData();
+    return projectData != null ? projectData.getExternalName() : "unspecified";
+  }
+
+  @Override
+  protected void doUpdate() {
+    String autoImportHint = null;
+    final ProjectData projectData = getData();
+    if (projectData != null) {
+      final AbstractExternalSystemSettings externalSystemSettings =
+        ExternalSystemApiUtil.getSettings(getExternalProjectsView().getProject(), getData().getOwner());
+      final ExternalProjectSettings projectSettings =
+        externalSystemSettings.getLinkedProjectSettings(projectData.getLinkedExternalProjectPath());
+      if (projectSettings != null && projectSettings.isUseAutoImport()) autoImportHint = "auto-import enabled";
+    }
+
+    setNameAndTooltip(getName(), myTooltipCache, autoImportHint);
+  }
+
+  @Override
+  protected SimpleTextAttributes getPlainAttributes() {
+    return super.getPlainAttributes();
+  }
+
+  private String makeDescription() {
+    StringBuilder desc = new StringBuilder();
+    final ProjectData projectData = getData();
+    desc.append("<html>" +
+                "<table>" +
+                "<tr>" +
+                "<td nowrap>" +
+                "<table>" +
+                "<tr>" +
+                "<td nowrap>Project:</td>" +
+                "<td nowrap>").append(getName())
+      .append("</td>" +
+              "</tr>")
+      .append(projectData != null ?
+              "<tr>" +
+              "<td nowrap>Location:</td>" +
+              "<td nowrap>" + projectData.getLinkedExternalProjectPath() : "")
+      .append("</td>" +
+              "</tr>" +
+              "</table>" +
+              "</td>" +
+              "</tr>");
+
+    appendProblems(desc);
+
+    desc.append("</table></html>");
+
+    return desc.toString();
+  }
+
+  private void appendProblems(StringBuilder desc) {
+    // TBD
+  }
+
+  @Override
+  protected void setNameAndTooltip(String name, @Nullable String tooltip, SimpleTextAttributes attributes) {
+    super.setNameAndTooltip(name, tooltip, attributes);
+  }
+
+  @Override
+  @Nullable
+  @NonNls
+  protected String getMenuId() {
+    return "ExternalSystemView.ProjectMenu";
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationNode.java
new file mode 100644 (file)
index 0000000..ebd55aa
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.execution.ProgramRunnerUtil;
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.impl.EditConfigurationsDialog;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.pom.Navigatable;
+import com.intellij.ui.treeStructure.SimpleTree;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.InputEvent;
+
+import static com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator.getRunConfigurationActivationTaskName;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/7/2014
+ */
+public class RunConfigurationNode extends ExternalSystemNode {
+  private final RunnerAndConfigurationSettings mySettings;
+
+  public RunConfigurationNode(@NotNull ExternalProjectsView externalProjectsView,
+                              RunConfigurationsNode parent,
+                              @NotNull RunnerAndConfigurationSettings settings) {
+    super(externalProjectsView, parent);
+    mySettings = settings;
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(ProgramRunnerUtil.getConfigurationIcon(mySettings, false));
+
+    final ExternalSystemRunConfiguration runConfiguration = (ExternalSystemRunConfiguration)mySettings.getConfiguration();
+    final ExternalSystemTaskExecutionSettings taskExecutionSettings = runConfiguration.getSettings();
+    final String shortcutHint = StringUtil.nullize(getShortcutsManager().getDescription(
+      taskExecutionSettings.getExternalProjectPath(), mySettings.getName()));
+    final String activatorHint = StringUtil.nullize(getTaskActivator().getDescription(
+      taskExecutionSettings.getExternalSystemId(), taskExecutionSettings.getExternalProjectPath(),
+      getRunConfigurationActivationTaskName(mySettings)));
+
+    String hint;
+    if (shortcutHint == null) {
+      hint = activatorHint;
+    }
+    else if (activatorHint == null) {
+      hint = shortcutHint;
+    }
+    else {
+      hint = shortcutHint + ", " + activatorHint;
+    }
+
+    setNameAndTooltip(getName(), StringUtil.join(taskExecutionSettings.getTaskNames(), " "), hint);
+  }
+
+  public RunnerAndConfigurationSettings getSettings() {
+    return mySettings;
+  }
+
+  @Override
+  public String getName() {
+    return mySettings.getName();
+  }
+
+  public boolean isAlwaysLeaf() {
+    return true;
+  }
+
+  @Nullable
+  @Override
+  protected String getMenuId() {
+    return "ExternalSystemView.RunConfigurationMenu";
+  }
+
+  public void updateRunConfiguration() {
+  }
+
+  @Override
+  public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
+    final Project project = getExternalProjectsView().getProject();
+    ProgramRunnerUtil.executeConfiguration(project, mySettings, DefaultRunExecutor.getRunExecutorInstance());
+    final RunManagerEx runManagerEx = RunManagerEx.getInstanceEx(project);
+    runManagerEx.setSelectedConfiguration(mySettings);
+  }
+
+  @Nullable
+  @Override
+  public Navigatable getNavigatable() {
+    return new Navigatable() {
+
+      @Override
+      public void navigate(boolean requestFocus) {
+        RunManager.getInstance(myProject).setSelectedConfiguration(mySettings);
+        EditConfigurationsDialog dialog = new EditConfigurationsDialog(myProject);
+        dialog.show();
+      }
+
+      @Override
+      public boolean canNavigate() {
+        return true;
+      }
+
+      @Override
+      public boolean canNavigateToSource() {
+        return false;
+      }
+    };
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationsNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/RunConfigurationsNode.java
new file mode 100644 (file)
index 0000000..ca82668
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashSet;
+import icons.ExternalSystemIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/7/2014
+ */
+@Order(3)
+public class RunConfigurationsNode extends ExternalSystemNode<Void> {
+
+  private final ModuleData myModuleData;
+
+  public RunConfigurationsNode(@NotNull ExternalProjectsView externalProjectsView, ModuleNode parent) {
+    super(externalProjectsView, parent, null);
+    myModuleData = parent.getData();
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(ExternalSystemIcons.TaskGroup);
+  }
+
+  @Override
+  public String getName() {
+    return message("external.system.view.nodes.run_configurations.name");
+  }
+
+  @Override
+  public boolean isVisible() {
+    return hasChildren() && super.isVisible();
+  }
+
+  @NotNull
+  @Override
+  protected List<? extends ExternalSystemNode> doBuildChildren() {
+    List<ExternalSystemNode> runConfigurationNodes = ContainerUtil.newArrayList();
+    final AbstractExternalSystemTaskConfigurationType configurationType = ExternalSystemUtil.findConfigurationType(myModuleData.getOwner());
+    if (configurationType == null) return Collections.emptyList();
+
+    Set<RunnerAndConfigurationSettings> settings = new THashSet<RunnerAndConfigurationSettings>(
+      RunManager.getInstance(myProject).getConfigurationSettingsList(configurationType));
+
+
+    String directory = PathUtil.getCanonicalPath(myModuleData.getLinkedExternalProjectPath());
+
+    for (RunnerAndConfigurationSettings cfg : settings) {
+      ExternalSystemRunConfiguration externalSystemRunConfiguration = (ExternalSystemRunConfiguration)cfg.getConfiguration();
+
+      if (directory.equals(PathUtil.getCanonicalPath(externalSystemRunConfiguration.getSettings().getExternalProjectPath()))) {
+        runConfigurationNodes.add(new RunConfigurationNode(getExternalProjectsView(), this, cfg));
+      }
+    }
+
+    return runConfigurationNodes;
+  }
+
+  public void updateRunConfigurations() {
+    cleanUpCache();
+    getExternalProjectsView().updateUpTo(this);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TaskNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TaskNode.java
new file mode 100644 (file)
index 0000000..8175c2f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/28/2014
+ */
+public class TaskNode extends ExternalSystemNode<TaskData> {
+  private TaskData myTaskData;
+
+  public TaskNode(@NotNull ExternalProjectsView externalProjectsView, @NotNull DataNode<TaskData> dataNode) {
+    super(externalProjectsView, null, dataNode);
+    myTaskData = dataNode.getData();
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(getUiAware().getTaskIcon());
+
+    final String shortcutHint = StringUtil.nullize(getShortcutsManager().getDescription(myTaskData.getLinkedExternalProjectPath(), myTaskData.getName()));
+    final String activatorHint = StringUtil.nullize(getTaskActivator().getDescription(
+      myTaskData.getOwner(), myTaskData.getLinkedExternalProjectPath(), myTaskData.getName()));
+
+    String hint;
+    if (shortcutHint == null) {
+      hint = activatorHint;
+    }
+    else if (activatorHint == null) {
+      hint = shortcutHint;
+    }
+    else {
+      hint = shortcutHint + ", " + activatorHint;
+    }
+
+    setNameAndTooltip(getName(), myTaskData.getDescription(), hint);
+  }
+
+  @Override
+  public String getName() {
+    return myTaskData.getName();
+  }
+
+  @Nullable
+  @Override
+  protected String getMenuId() {
+    return "ExternalSystemView.TaskMenu";
+  }
+
+  @Nullable
+  @Override
+  protected String getActionId() {
+    return "ExternalSystem.RunTask";
+  }
+
+  @Override
+  public boolean isAlwaysLeaf() {
+    return true;
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TasksNode.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/TasksNode.java
new file mode 100644 (file)
index 0000000..ad4f1e1
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2000-2014 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.view;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
+import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import icons.ExternalSystemIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 11/6/2014
+ */
+@Order(1)
+public class TasksNode extends ExternalSystemNode {
+
+  private final MultiMap<String, TaskNode> myTasksMap = new MultiMap<String, TaskNode>();
+
+  @SuppressWarnings("unchecked")
+  public TasksNode(ExternalProjectsView externalProjectsView, final Collection<DataNode<?>> dataNodes) {
+    super(externalProjectsView, null, null);
+
+    if (dataNodes != null && !dataNodes.isEmpty()) {
+      for (DataNode<?> dataNode : dataNodes) {
+        if (!(dataNode.getData() instanceof TaskData)) continue;
+        if (dataNode.getParent() != null && ProjectKeys.PROJECT.equals(dataNode.getParent().getKey())) break;
+        String group = ((TaskData)dataNode.getData()).getGroup();
+        if (group == null) group = "other";
+        myTasksMap.putValue(StringUtil.toLowerCase(group), new TaskNode(externalProjectsView, (DataNode<TaskData>)dataNode));
+      }
+    }
+  }
+
+  @Override
+  protected void update(PresentationData presentation) {
+    super.update(presentation);
+    presentation.setIcon(ExternalSystemIcons.TaskGroup);
+  }
+
+  @Override
+  public String getName() {
+    return "Tasks";
+  }
+
+  @Override
+  public boolean isVisible() {
+    return hasChildren() && super.isVisible();
+  }
+
+  @SuppressWarnings("unchecked")
+  @NotNull
+  @Override
+  protected List<? extends ExternalSystemNode> doBuildChildren() {
+    final List<ExternalSystemNode<?>> result = ContainerUtil.newArrayList();
+    final boolean isGroup = getExternalProjectsView().getGroupTasks();
+    if (isGroup) {
+      for (Map.Entry<String, Collection<TaskNode>> collectionEntry : myTasksMap.entrySet()) {
+        final String group = ObjectUtils.notNull(collectionEntry.getKey(), "other");
+        final ExternalSystemNode tasksGroupNode = new ExternalSystemNode(getExternalProjectsView(), null, null) {
+
+          @Override
+          protected void update(PresentationData presentation) {
+            super.update(presentation);
+            presentation.setIcon(ExternalSystemIcons.TaskGroup);
+          }
+
+          @Override
+          public String getName() {
+            return group;
+          }
+
+          @Override
+          public int compareTo(@NotNull ExternalSystemNode node) {
+            return "other".equals(group) ? 1 : super.compareTo(node);
+          }
+        };
+        tasksGroupNode.addAll(collectionEntry.getValue());
+        result.add(tasksGroupNode);
+      }
+    }
+    else {
+      result.addAll(myTasksMap.values());
+    }
+    return result;
+  }
+}
index 24be89247d98d7adf155fe668c80c5255d04ab72..38dd0388ff2b34a4a9e560c2cef20a6fd9b44745 100644 (file)
@@ -29,4 +29,5 @@ public class ExternalSystemIcons {
   }
 
   public static final Icon Task = load("/icons/task.png"); // 16x16
+  public static final Icon TaskGroup = load("/icons/taskGroup.png"); // 16x16
 }
diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModelTest.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/task/ui/ExternalSystemRecentTaskListModelTest.java
deleted file mode 100644 (file)
index 9bed36f..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2000-2013 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.task.ui;
-
-import com.intellij.openapi.command.impl.DummyProject;
-import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
-import com.intellij.openapi.externalSystem.test.ExternalSystemTestUtil;
-import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
-import com.intellij.util.containers.ContainerUtilRt;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.List;
-
-/**
- * @author Vladislav Soroka
- * @since 8/13/13
- */
-public class ExternalSystemRecentTaskListModelTest {
-  
-  private ExternalSystemRecentTaskListModel myModel;
-
-  @Before
-  public void setUp() {
-    myModel = new ExternalSystemRecentTaskListModel(ExternalSystemTestUtil.TEST_EXTERNAL_SYSTEM_ID, DummyProject.getInstance());
-  }
-  
-  @Test
-  public void testSetFirst() throws Exception {
-    List<ExternalTaskExecutionInfo> tasks = ContainerUtilRt.newArrayList();
-    for (int i = 0; i <= ExternalSystemConstants.RECENT_TASKS_NUMBER; i++) {
-      new ExternalTaskExecutionInfo(new ExternalSystemTaskExecutionSettings(), "task" + i);
-    }
-    myModel.setTasks(tasks);
-
-    myModel.setFirst(new ExternalTaskExecutionInfo(new ExternalSystemTaskExecutionSettings(), "newTask"));
-
-    Assert.assertEquals(ExternalSystemConstants.RECENT_TASKS_NUMBER, myModel.getSize());
-    myModel.setFirst(new ExternalTaskExecutionInfo(new ExternalSystemTaskExecutionSettings(), "task1"));
-    Assert.assertEquals(ExternalSystemConstants.RECENT_TASKS_NUMBER, myModel.getSize());
-  }
-
-  @Test
-  public void testEnsureSize() throws Exception {
-    List<ExternalTaskExecutionInfo> tasks = ContainerUtilRt.newArrayList();
-
-    // test task list widening
-    myModel.setTasks(tasks);
-    myModel.ensureSize(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-    Assert.assertEquals("task list widening failed", ExternalSystemConstants.RECENT_TASKS_NUMBER, myModel.getSize());
-
-    // test task list reduction
-    for (int i = 0; i < ExternalSystemConstants.RECENT_TASKS_NUMBER + 1; i++) {
-      tasks.add(new ExternalTaskExecutionInfo(new ExternalSystemTaskExecutionSettings(), "task" + i));
-    }
-    myModel.setTasks(tasks);
-    Assert.assertEquals(ExternalSystemConstants.RECENT_TASKS_NUMBER + 1, myModel.getSize());
-
-    myModel.ensureSize(ExternalSystemConstants.RECENT_TASKS_NUMBER);
-    Assert.assertEquals("task list reduction failed", ExternalSystemConstants.RECENT_TASKS_NUMBER, myModel.getSize());
-  }
-}
index c2bb2b5bd8191f383da478fec807f2f59e99cf75..ce1ce7c746fa6cd4e0fc4b64ad82da5e1ce5c4a9 100644 (file)
@@ -11,5 +11,6 @@
                     interface="com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener"/>
     <extensionPoint name="externalSystemExecutionConsoleManager"
                     interface="com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager"/>
+    <extensionPoint name="externalSystemViewContributor" interface="com.intellij.openapi.externalSystem.view.ExternalSystemViewContributor"/>
   </extensionPoints>
 </idea-plugin>
\ No newline at end of file
index 10bd0e8ba013082191f00fcf22d83075e4457c02..98d7413c2dcee24c9ec58945fa56c72a21c60857 100644 (file)
@@ -2,6 +2,8 @@
   <extensions defaultExtensionNs="com.intellij">
 
     <postStartupActivity implementation="com.intellij.openapi.externalSystem.service.ExternalSystemStartupActivity"/>
+
+    <keymapExtension implementation="com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemKeymapExtension"/>
     
     <!--Generic services-->
     <applicationService serviceImplementation="com.intellij.openapi.externalSystem.service.ExternalSystemFacadeManager"/>
@@ -20,6 +22,7 @@
 
     <!--Project structure management services-->
     <applicationService serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager"/>
+    <projectService serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager" order="first"/>
     <projectService serviceImplementation="com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsDataStorage"/>
     <externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.project.manage.ProjectDataServiceImpl"/>
     <externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.project.manage.LibraryDataService"/>
@@ -31,6 +34,7 @@
     <!--Tool window services-->
     <externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.task.ToolWindowModuleService"/>
     <externalProjectDataService implementation="com.intellij.openapi.externalSystem.service.task.ToolWindowTaskService"/>
+    <externalSystemViewContributor implementation="com.intellij.openapi.externalSystem.view.ExternalSystemViewDefaultContributor"/>
     
     <!--Execution-->
     <programRunner implementation="com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskRunner"/>
index fdb174aa37be633ca3fe4f193f9cfd9478e7e21a..b99b75929dc59713cfdb4f1175ae63172f67d7eb 100644 (file)
@@ -1,7 +1,6 @@
 <component>
   <actions>
-    
-    
+
     <action id="ExternalSystem.RefreshAllProjects"
             class="com.intellij.openapi.externalSystem.action.RefreshAllExternalProjectsAction"
             icon="AllIcons.Actions.Refresh"/>
     <action id="ExternalSystem.OpenConfig"
             class="com.intellij.openapi.externalSystem.action.OpenExternalConfigAction"
             use-shortcut-of="EditSource"/>
+    <action id="ExternalSystem.ShowSettings"
+            class="com.intellij.openapi.externalSystem.action.ShowExternalSystemSettingsAction"
+            icon="AllIcons.General.ProjectSettings">
+    </action>
+
+    <action id="ExternalSystem.ToggleAutoImport"
+            class="com.intellij.openapi.externalSystem.action.ToggleAutoImportAction">
+    </action>
+
+    <action id="ExternalSystem.ExpandAll" class="com.intellij.openapi.externalSystem.action.ExternalSystemTreeAction$ExpandAll"
+            text="Expand All"
+            icon="AllIcons.Actions.Expandall"
+            use-shortcut-of="ExpandAll"/>
+    <action id="ExternalSystem.CollapseAll" class="com.intellij.openapi.externalSystem.action.ExternalSystemTreeAction$CollapseAll"
+            text="Collapse All"
+            icon="AllIcons.Actions.Collapseall"
+            use-shortcut-of="CollapseAll"/>
+
+    <action id="ExternalSystem.RunTask"
+            class="com.intellij.openapi.externalSystem.action.task.RunExternalSystemTaskAction"
+            text="_Run Task"
+            description="Execute selected tasks"
+            icon="AllIcons.Actions.Execute">
+    </action>
+    <action id="ExternalSystem.AssignShortcut"
+            class="com.intellij.openapi.externalSystem.action.task.AssignShortcutAction" text="Assign Shortcut..."
+            description="Assign shortcut to the selected task">
+    </action>
+    <action id="ExternalSystem.AssignRunConfigurationShortcut"
+            class="com.intellij.openapi.externalSystem.action.task.AssignRunConfigurationShortcutAction" text="Assign Shortcut..."
+            description="Assign shortcut to the selected Run Configuration">
+    </action>
+
+    <action id="ExternalSystem.BeforeCompile"
+            class="com.intellij.openapi.externalSystem.action.task.ToggleBeforeCompileTasksAction" text="Execute Before Make"
+            description="Execute selected task before Make">
+    </action>
+    <action id="ExternalSystem.AfterCompile"
+            class="com.intellij.openapi.externalSystem.action.task.ToggleAfterCompileTasksAction" text="Execute After Make"
+            description="Execute selected task after Make">
+    </action>
+    <action id="ExternalSystem.BeforeRebuild"
+            class="com.intellij.openapi.externalSystem.action.task.ToggleBeforeRebuildTasksAction" text="Execute Before Rebuild"
+            description="Execute selected task before full rebuild">
+    </action>
+    <action id="ExternalSystem.AfterRebuild"
+            class="com.intellij.openapi.externalSystem.action.task.ToggleAfterRebuildTasksAction" text="Execute After Rebuild"
+            description="Execute selected task after full rebuild">
+    </action>
+    <action id="ExternalSystem.BeforeRun"
+            class="com.intellij.openapi.externalSystem.action.task.ToggleBeforeRunTaskAction" text="Execute Before Run/Debug..."
+            description="Execute selected task before launching Run/Debug configuration">
+    </action>
+
+    <action id="ExternalSystem.GroupTasks"
+            class="com.intellij.openapi.externalSystem.action.task.GroupTasksAction" text="Group Tasks">
+    </action>
+
+    <group id="ExternalSystemView.BaseProjectMenu">
+      <reference ref="ExternalSystem.OpenConfig"/>
+      <separator/>
+      <reference id="ExternalSystem.RefreshProject"/>
+      <reference id="ExternalSystem.DetachProject"/>
+
+    </group>
 
-    <!--Tool window actions-->
-    <group id="ExternalSystem.ToolWindow.Toolbar">
+    <group id="ExternalSystemView.ProjectMenu" popup="true">
+      <reference ref="ExternalSystemView.BaseProjectMenu"/>
+      <separator/>
+      <reference id="ExternalSystem.ToggleAutoImport"/>
+    </group>
+
+    <group id="ExternalSystemView.ActionsToolbar.LeftPanel">
       <reference id="ExternalSystem.RefreshAllProjects"/>
       <reference id="ExternalSystem.AttachProject"/>
       <reference ref="ExternalSystem.DetachProject"/>
+      <!--<reference id="ExternalSystem.Run/>-->
+    </group>
+    <group id="ExternalSystemView.ActionsToolbar.CenterPanel">
+      <separator/>
+      <reference id="ExternalSystem.ExpandAll"/>
+      <reference id="ExternalSystem.CollapseAll"/>
+    </group>
+    <group id="ExternalSystemView.ActionsToolbar.RightPanel">
     </group>
 
-    <!--Context menu action-->
-    <group id="ExternalSystem.Tree.Context" popup="true">
-      <reference ref="ExternalSystem.OpenConfig"/>
-      <reference id="ExternalSystem.RefreshProject"/>
-      <reference ref="ExternalSystem.DetachProject"/>
+    <group id="ExternalSystemView.ActionsToolbar">
+      <reference ref="ExternalSystemView.ActionsToolbar.LeftPanel"/>
+      <separator/>
+      <reference ref="ExternalSystemView.ActionsToolbar.CenterPanel"/>
+      <separator/>
+      <reference ref="ExternalSystemView.ActionsToolbar.RightPanel"/>
       <separator/>
+      <reference id="ExternalSystem.ShowSettings" order="last"/>
+    </group>
+
+    <group id="ExternalSystemView.TaskMenu" popup="true">
       <reference ref="RunContextGroup"/>
+      <separator/>
+      <reference id="EditSource"/>
+      <separator/>
+      <reference id="ExternalSystem.BeforeCompile"/>
+      <reference id="ExternalSystem.AfterCompile"/>
+      <reference id="ExternalSystem.BeforeRebuild"/>
+      <reference id="ExternalSystem.AfterRebuild"/>
+      <!--<reference id="ExternalSystem.BeforeRun"/>-->
+      <separator/>
+      <reference id="ExternalSystem.AssignShortcut"/>
+    </group>
+
+    <group id="ExternalSystemView.RunConfigurationMenu" popup="true"  class="com.intellij.openapi.externalSystem.action.ExternalSystemRunConfigurationMenu">
+      <separator/>
+      <action id="ExternalSystem.EditRunConfiguration"
+              text="Edit Run Configuration..." icon="AllIcons.Actions.Edit"
+              class="com.intellij.openapi.externalSystem.action.EditExternalSystemRunConfigurationAction" />
+      <action id="ExternalSystem.RemoveRunConfiguration"
+              text="Remove Run Configuration" icon="AllIcons.General.Remove"
+              class="com.intellij.openapi.externalSystem.action.RemoveExternalSystemRunConfigurationAction" />
+      <separator/>
+      <reference id="ExternalSystem.BeforeCompile"/>
+      <reference id="ExternalSystem.AfterCompile"/>
+      <reference id="ExternalSystem.BeforeRebuild"/>
+      <reference id="ExternalSystem.AfterRebuild"/>
+      <!--<reference id="ExternalSystem.BeforeRun"/>-->
+      <separator/>
+      <reference id="ExternalSystem.AssignRunConfigurationShortcut"/>
     </group>
-    
+
+
   </actions>
 </component>
\ No newline at end of file
diff --git a/plugins/gradle/resources/icons/offlineMode.png b/plugins/gradle/resources/icons/offlineMode.png
new file mode 100644 (file)
index 0000000..3276aca
Binary files /dev/null and b/plugins/gradle/resources/icons/offlineMode.png differ
index 4e4fea02a0c766c800bb2957c36a3a4b17178b3d..8302a603e38f571c6d501262bdfc710f1852b3a1 100644 (file)
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.settings.GradleLocalSettings"/>
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.project.GradleNotification"/>
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager"/>
+    <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.task.ExecuteGradleTaskHistoryService"/>
 
     <stepsBeforeRunProvider implementation="org.jetbrains.plugins.gradle.execution.GradleBeforeRunTaskProvider" />
     <configurationProducer implementation="org.jetbrains.plugins.gradle.service.execution.GradleRuntimeConfigurationProducer"/>
 
   <actions>
 
+    <action id="Gradle.ExecuteTask" class="org.jetbrains.plugins.gradle.action.GradleExecuteTaskAction" text="Execute Gradle Task"
+            icon="GradleIcons.Gradle">
+    </action>
+    <action id="Gradle.ToggleOfflineAction" class="org.jetbrains.plugins.gradle.action.ToggleOfflineAction"
+            text="Toggle Offline Mode" description="Toggle offline mode for Gradle builds"
+            icon="GradleIcons.OfflineMode"/>
+    
     <group id="Gradle.GenerateGroup">
       <action id="AddGradleDslPluginAction" class="org.jetbrains.plugins.gradle.codeInsight.actions.AddGradleDslPluginAction"/>
       <add-to-group group-id="GenerateGroup" anchor="first"/>
     </group>
 
+    <group>
+      <separator/>
+      <reference id="Gradle.ExecuteTask"/>
+      <add-to-group group-id="ExternalSystemView.ActionsToolbar.LeftPanel"/>
+    </group>
+
+    <group>
+      <reference id="Gradle.ToggleOfflineAction"/>
+      <add-to-group group-id="ExternalSystemView.ActionsToolbar.RightPanel"/>
+    </group>
+
   </actions>
 </idea-plugin>
index 4283f72073648085430cfa84a3045e74f7c30157..19f20258d467c0a4ce4409475d893f85f9043d20 100644 (file)
@@ -33,5 +33,6 @@ public class GradleIcons {
   public static final Icon GradleNavigate = load("/icons/gradleNavigate.png"); // 16x16
   public static final Icon GradlePlugin = load("/icons/gradlePlugin.png"); // 16x16
   public static final Icon GradleSync = load("/icons/gradleSync.png"); // 16x16
+  public static final Icon OfflineMode = load("/icons/offlineMode.png"); // 16x16
   public static final Icon ToolWindowGradle = load("/icons/toolWindowGradle.png"); // 13x13
 }
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/ToggleOfflineAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/ToggleOfflineAction.java
new file mode 100644 (file)
index 0000000..63203a2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2014 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.action.ExternalSystemToggleAction;
+import org.jetbrains.plugins.gradle.settings.GradleSettings;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/20/2014
+ */
+public class ToggleOfflineAction extends ExternalSystemToggleAction {
+
+  @Override
+  protected boolean doIsSelected(AnActionEvent e) {
+    return GradleSettings.getInstance(getProject(e)).isOfflineWork();
+  }
+
+  @Override
+  public void setSelected(AnActionEvent e, boolean state) {
+    GradleSettings.getInstance(getProject(e)).setOfflineWork(state);
+  }
+}
index 3e762741fb7acd1f1fc23ae45947d92ce447e9d2..036e61f5be1393b47187fedfb0147dc6887f0918 100644 (file)
@@ -330,6 +330,23 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     final Collection<TaskData> tasks = ContainerUtil.newArrayList();
     final String moduleConfigPath = ideModule.getData().getLinkedExternalProjectPath();
 
+    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
+
+    if (externalProject != null) {
+      for (ExternalTask task : externalProject.getTasks().values()) {
+        String taskName = task.getName();
+        if (taskName.trim().isEmpty() || isIdeaTask(taskName)) {
+          continue;
+        }
+        TaskData taskData = new TaskData(GradleConstants.SYSTEM_ID, taskName, moduleConfigPath, task.getDescription());
+        taskData.setGroup(task.getGroup());
+        ideModule.createChild(ProjectKeys.TASK, taskData);
+        tasks.add(taskData);
+      }
+
+      return tasks;
+    }
+
     for (GradleTask task : gradleModule.getGradleProject().getTasks()) {
       String taskName = task.getName();
       if (taskName == null || taskName.trim().isEmpty() || isIdeaTask(taskName)) {
@@ -641,7 +658,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
 
       if (unresolved) {
         // Gradle uses names like 'unresolved dependency - commons-collections commons-collections 3.2' for unresolved dependencies.
-        libraryName = binaryPath.getName().substring(UNRESOLVED_DEPENDENCY_PREFIX.length());
+        libraryName = binaryPath.getPath().substring(UNRESOLVED_DEPENDENCY_PREFIX.length());
         int i = libraryName.indexOf(' ');
         if (i >= 0) {
           i = CharArrayUtil.shiftForward(libraryName, i + 1, " ");
@@ -669,6 +686,13 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
             final String classifier = matcher.group(1);
             libraryName += (":" + classifier);
           }
+          else {
+            final String artifactId = StringUtil.trimEnd(StringUtil.trimEnd(libraryFileName, moduleVersion.getVersion()), "-");
+            libraryName = String.format("%s:%s:%s",
+                                        moduleVersion.getGroup(),
+                                        artifactId,
+                                        moduleVersion.getVersion());
+          }
         }
       }
     }
index 3f338ea3f58533b7e73efa9598be57de29c436fb..096b1e6d8d5b3f1b9cbb3e4b4517424d76ce378f 100644 (file)
@@ -101,7 +101,7 @@ class ExternalProjectBuilderImpl implements ModelBuilderService {
       ExternalTask externalTask = new DefaultExternalTask()
       externalTask.name = task.name
       externalTask.description = task.description
-      externalTask.group = task.group
+      externalTask.group = task.group ?: "other"
       externalTask.QName = task.path
       result.put(externalTask.QName, externalTask)
     }