diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java index 25b4a7c01e6b..b5333ed80faa 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java @@ -354,10 +354,20 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change return myModuleModelCommitted; } - public boolean deleteModule(final Module module) { - ModuleEditor moduleEditor = getModuleEditor(module); - if (moduleEditor == null) return true; - return doRemoveModule(moduleEditor); + public List deleteModules(final Collection modules) { + List deleted = new ArrayList(); + List moduleEditors = new ArrayList(); + for (Module module : modules) { + ModuleEditor moduleEditor = getModuleEditor(module); + if (moduleEditor != null) { + deleted.add(module); + moduleEditors.add(moduleEditor); + } + } + if (doRemoveModules(moduleEditors)) { + return deleted; + } + return Collections.emptyList(); } @@ -464,37 +474,37 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change } - private boolean doRemoveModule(@NotNull ModuleEditor selectedEditor) { + private boolean doRemoveModules(@NotNull List selectedEditors) { + if (selectedEditors.isEmpty()) return true; String question; - if (myModuleEditors.size() == 1) { - question = ProjectBundle.message("module.remove.last.confirmation"); + if (myModuleEditors.size() == selectedEditors.size()) { + question = ProjectBundle.message("module.remove.last.confirmation", selectedEditors.size()); } else { - question = ProjectBundle.message("module.remove.confirmation", selectedEditor.getModule().getName()); + question = ProjectBundle.message("module.remove.confirmation", selectedEditors.get(0).getModule().getName(), selectedEditors.size()); } int result = - Messages.showYesNoDialog(myProject, question, ProjectBundle.message("module.remove.confirmation.title"), Messages.getQuestionIcon()); + Messages.showYesNoDialog(myProject, question, ProjectBundle.message("module.remove.confirmation.title", selectedEditors.size()), Messages.getQuestionIcon()); if (result != Messages.YES) { return false; } - // do remove - myModuleEditors.remove(selectedEditor.getModule()); + for (ModuleEditor editor : selectedEditors) { + myModuleEditors.remove(editor.getModule()); - // destroyProcess removed module - final Module moduleToRemove = selectedEditor.getModule(); - // remove all dependencies on the module that is about to be removed - List modifiableRootModels = new ArrayList(); - for (final ModuleEditor moduleEditor : myModuleEditors.values()) { - final ModifiableRootModel modifiableRootModel = moduleEditor.getModifiableRootModelProxy(); - ContainerUtil.addIfNotNull(modifiableRootModels, modifiableRootModel); + final Module moduleToRemove = editor.getModule(); + // remove all dependencies on the module which is about to be removed + List modifiableRootModels = new ArrayList(); + for (final ModuleEditor moduleEditor : myModuleEditors.values()) { + final ModifiableRootModel modifiableRootModel = moduleEditor.getModifiableRootModelProxy(); + ContainerUtil.addIfNotNull(modifiableRootModels, modifiableRootModel); + } + + ModuleDeleteProvider.removeModule(moduleToRemove, null, modifiableRootModels, myModuleModel); + Disposer.dispose(editor); } - - // destroyProcess editor - ModuleDeleteProvider.removeModule(moduleToRemove, null, modifiableRootModels, myModuleModel); processModuleCountChanged(); - Disposer.dispose(selectedEditor); - + return true; } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java index 4bdc77b87e59..b0141f0e12e1 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java @@ -49,6 +49,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -344,9 +345,8 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable { } @Override - protected void removeArtifact(Artifact artifact) { - myPackagingEditorContext.getOrCreateModifiableArtifactModel().removeArtifact(artifact); - myContext.getDaemonAnalyzer().removeElement(myPackagingEditorContext.getOrCreateArtifactElement(artifact)); + protected List> getRemoveHandlers() { + return Collections.singletonList(new ArtifactRemoveHandler()); } @Override @@ -373,6 +373,21 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable { public void dispose() { } + private class ArtifactRemoveHandler extends RemoveConfigurableHandler { + public ArtifactRemoveHandler() { + super(ArtifactConfigurableBase.class); + } + + @Override + public boolean remove(@NotNull Collection artifacts) { + for (Artifact artifact : artifacts) { + myPackagingEditorContext.getOrCreateModifiableArtifactModel().removeArtifact(artifact); + myContext.getDaemonAnalyzer().removeElement(myPackagingEditorContext.getOrCreateArtifactElement(artifact)); + } + return true; + } + } + private class AddArtifactAction extends DumbAwareAction { private final ArtifactType myType; private final ArtifactTemplate myArtifactTemplate; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java index 7ae03a0fee48..5fe35d213d33 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.impl.libraries.LibraryImpl; import com.intellij.openapi.roots.libraries.Library; @@ -38,6 +39,7 @@ import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.ui.NonEmptyInputValidator; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.tree.TreeUtil; @@ -277,63 +279,95 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl removePaths(pathsToRemove.toArray(new TreePath[pathsToRemove.size()])); } - @Override - protected boolean removeLibrary(final Library library) { - final LibraryTable table = library.getTable(); - if (table != null) { - final LibraryProjectStructureElement libraryElement = new LibraryProjectStructureElement(myContext, library); - final Collection usages = new ArrayList(myContext.getDaemonAnalyzer().getUsages(libraryElement)); - if (usages.size() > 0) { - final MultiMap containerType2Usage = new MultiMap(); - for (final ProjectStructureElementUsage usage : usages) { - containerType2Usage.putValue(usage.getContainingElement().getTypeName(), usage); + protected List> getRemoveHandlers() { + return Collections.singletonList(new RemoveConfigurableHandler(LibraryConfigurable.class) { + @Override + public boolean remove(@NotNull Collection libraries) { + List>> toRemove = new ArrayList>>(); + + String firstLibraryUsageDescription = null; + String firstLibraryWithUsageName = null; + int librariesWithUsages = 0; + for (Library library : libraries) { + final LibraryTable table = library.getTable(); + if (table == null) continue; + + final LibraryProjectStructureElement libraryElement = new LibraryProjectStructureElement(myContext, library); + final Collection usages = + new ArrayList(myContext.getDaemonAnalyzer().getUsages(libraryElement)); + if (usages.size() > 0) { + if (librariesWithUsages == 0) { + final MultiMap containerType2Usage = new MultiMap(); + for (final ProjectStructureElementUsage usage : usages) { + containerType2Usage.putValue(usage.getContainingElement().getTypeName(), usage); + } + + List types = new ArrayList(containerType2Usage.keySet()); + Collections.sort(types); + + final StringBuilder sb = new StringBuilder("Library '"); + Library libraryModel = myContext.getLibraryModel(library); + sb.append(libraryModel != null ? libraryModel.getName() : library.getName()).append("' is used in "); + for (int i = 0; i < types.size(); i++) { + if (i > 0 && i == types.size() - 1) { + sb.append(" and in "); + } + else if (i > 0) { + sb.append(", in "); + } + String type = types.get(i); + Collection usagesOfType = containerType2Usage.get(type); + if (usagesOfType.size() > 1) { + sb.append(usagesOfType.size()).append(" ").append(StringUtil.decapitalize(StringUtil.pluralize(type))); + } + else { + sb.append(StringUtil.decapitalize(usagesOfType.iterator().next().getContainingElement().getPresentableName())); + } + } + firstLibraryWithUsageName = library.getName(); + firstLibraryUsageDescription = sb.toString(); + } + librariesWithUsages++; + } + toRemove.add(Pair.create(libraryElement, usages)); } - - List types = new ArrayList(containerType2Usage.keySet()); - Collections.sort(types); - - final StringBuilder sb = new StringBuilder("Library '"); - Library libraryModel = myContext.getLibraryModel(library); - sb.append(libraryModel != null ? libraryModel.getName() : library.getName()).append("' is used in "); - for (int i = 0; i < types.size(); i++) { - if (i > 0 && i == types.size() - 1) { - sb.append(" and in "); - } - else if (i > 0) { - sb.append(", in "); - } - String type = types.get(i); - Collection usagesOfType = containerType2Usage.get(type); - if (usagesOfType.size() > 1) { - sb.append(usagesOfType.size()).append(" ").append(StringUtil.decapitalize(StringUtil.pluralize(type))); + if (librariesWithUsages > 0) { + String message; + if (librariesWithUsages == 1) { + message = firstLibraryUsageDescription + ".\nAre you sure you want to delete this library?"; } else { - sb.append(StringUtil.decapitalize(usagesOfType.iterator().next().getContainingElement().getPresentableName())); + message = ProjectBundle.message("libraries.remove.confirmation.text", firstLibraryWithUsageName, librariesWithUsages-1); + } + + if (Messages.OK != Messages.showOkCancelDialog(myProject, message, + ProjectBundle.message("libraries.remove.confirmation.title", librariesWithUsages), Messages.getQuestionIcon())) { + return false; } } - sb.append(".\n\nAre you sure you want to delete this library?"); - - if (Messages.OK == Messages.showOkCancelDialog(myProject, sb.toString(), - "Delete Library", Messages.getQuestionIcon())) { - - for (final ProjectStructureElementUsage usage : usages) { + for (Pair> pair : toRemove) { + for (ProjectStructureElementUsage usage : pair.getSecond()) { usage.removeSourceElement(); } - - getModelProvider().getModifiableModel().removeLibrary(library); - myContext.getDaemonAnalyzer().removeElement(libraryElement); - return true; + getModelProvider().getModifiableModel().removeLibrary(pair.getFirst().getLibrary()); + myContext.getDaemonAnalyzer().removeElement(pair.getFirst()); } - } else { - getModelProvider().getModifiableModel().removeLibrary(library); - myContext.getDaemonAnalyzer().removeElement(libraryElement); return true; } - } - return false; + @Override + public boolean canBeRemoved(@NotNull Collection libraries) { + for (Library library : libraries) { + LibraryTable table = library.getTable(); + if (table != null && !table.isEditable()) { + return false; + } + } + return true; + } + }); } @Override diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java index a0d32a3f4e11..5bd87aaf27aa 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseStructureConfigurable.java @@ -22,13 +22,9 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; -import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzerListener; import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement; import com.intellij.openapi.ui.MasterDetailsComponent; @@ -39,14 +35,12 @@ import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; -import com.intellij.packaging.artifacts.Artifact; import com.intellij.ui.TreeSpeedSearch; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.navigation.Place; import com.intellij.util.Function; import com.intellij.util.IconUtil; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.*; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -323,24 +317,53 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i @Nullable protected abstract AbstractAddGroup createAddAction(); + protected List> getRemoveHandlers() { + return Collections.emptyList(); + } + + @NotNull + private MultiMap groupNodes(List nodes) { + List> handlers = getRemoveHandlers(); + MultiMap grouped = new LinkedMultiMap(); + for (MyNode node : nodes) { + final NamedConfigurable configurable = node.getConfigurable(); + if (configurable == null) continue; + RemoveConfigurableHandler handler = findHandler(handlers, configurable.getClass()); + if (handler == null) continue; + + grouped.putValue(handler, node); + } + return grouped; + } + + private static RemoveConfigurableHandler findHandler(List> handlers, + Class configurableClass) { + for (RemoveConfigurableHandler handler : handlers) { + if (handler.getConfigurableClass().isAssignableFrom(configurableClass)) { + return handler; + } + } + return null; + } + protected class MyRemoveAction extends MyDeleteAction { public MyRemoveAction() { super(new Condition() { @Override public boolean value(final Object[] objects) { - Object[] editableObjects = ContainerUtil.mapNotNull(objects, new Function() { - @Override - public Object fun(Object object) { - if (object instanceof MyNode) { - final NamedConfigurable namedConfigurable = ((MyNode)object).getConfigurable(); - if (namedConfigurable != null) { - return namedConfigurable.getEditableObject(); - } - } - return null; + List nodes = new ArrayList(); + for (Object object : objects) { + if (!(object instanceof MyNode)) return false; + nodes.add((MyNode)object); + } + MultiMap map = groupNodes(nodes); + for (Map.Entry> entry : map.entrySet()) { + //noinspection unchecked + if (!entry.getKey().canBeRemoved(getEditableObjects(entry.getValue()))) { + return false; } - }, new Object[0]); - return editableObjects.length == objects.length && canBeRemoved(editableObjects); + } + return true; } }); } @@ -350,77 +373,40 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i final TreePath[] paths = myTree.getSelectionPaths(); if (paths == null) return; - final Set pathsToRemove = new HashSet(); - for (TreePath path : paths) { - if (removeFromModel(path)) { - pathsToRemove.add(path); + List removedNodes = removeFromModel(paths); + removeNodes(removedNodes); + } + + private List removeFromModel(final TreePath[] paths) { + List nodes = ContainerUtil.mapNotNull(paths, new Function() { + @Override + public MyNode fun(TreePath path) { + Object node = path.getLastPathComponent(); + return node instanceof MyNode ? (MyNode)node : null; + } + }); + MultiMap grouped = groupNodes(nodes); + + List removedNodes = new ArrayList(); + for (Map.Entry> entry : grouped.entrySet()) { + //noinspection unchecked + boolean removed = entry.getKey().remove(getEditableObjects(entry.getValue())); + if (removed) { + removedNodes.addAll(entry.getValue()); } } - removePaths(pathsToRemove.toArray(new TreePath[pathsToRemove.size()])); - } - - private boolean removeFromModel(final TreePath selectionPath) { - final Object last = selectionPath.getLastPathComponent(); - - if (!(last instanceof MyNode)) return false; - - final MyNode node = (MyNode)last; - final NamedConfigurable configurable = node.getConfigurable(); - if (configurable == null) return false; - final Object editableObject = configurable.getEditableObject(); - - return removeObject(editableObject); + return removedNodes; } } - protected boolean canBeRemoved(final Object[] editableObjects) { - for (Object editableObject : editableObjects) { - if (!canObjectBeRemoved(editableObject)) return false; + private static List getEditableObjects(Collection value) { + List objects = new ArrayList(); + for (MyNode node : value) { + objects.add(node.getConfigurable().getEditableObject()); } - return true; + return objects; } - private static boolean canObjectBeRemoved(Object editableObject) { - if (editableObject instanceof Sdk || - editableObject instanceof Module || - editableObject instanceof Facet || - editableObject instanceof Artifact) { - return true; - } - if (editableObject instanceof Library) { - final LibraryTable table = ((Library)editableObject).getTable(); - return table == null || table.isEditable(); - } - return false; - } - - protected boolean removeObject(final Object editableObject) { - // todo keep only removeModule() and removeFacet() here because other removeXXX() are empty here and overridden in subclasses? Override removeObject() instead? - if (editableObject instanceof Sdk) { - removeJdk((Sdk)editableObject); - } - else if (editableObject instanceof Module) { - if (!removeModule((Module)editableObject)) return false; - } - else if (editableObject instanceof Facet) { - if (removeFacet((Facet)editableObject).isEmpty()) return false; - } - else if (editableObject instanceof Library) { - if (!removeLibrary((Library)editableObject)) return false; - } - else if (editableObject instanceof Artifact) { - removeArtifact((Artifact)editableObject); - } - return true; - } - - protected void removeArtifact(Artifact artifact) { - } - - - protected boolean removeLibrary(Library library) { - return false; - } protected void removeFacetNodes(@NotNull List facets) { for (Facet facet : facets) { @@ -431,17 +417,6 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i } } - protected List removeFacet(final Facet facet) { - return myContext.myModulesConfigurator.getFacetsConfigurator().removeFacet(facet); - } - - protected boolean removeModule(final Module module) { - return true; - } - - protected void removeJdk(final Sdk editableObject) { - } - protected abstract static class AbstractAddGroup extends ActionGroup implements ActionGroupWithPreselection { protected AbstractAddGroup(String text, Icon icon) { diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetStructureConfigurable.java index b299ef262b34..a3539f7abfc0 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/FacetStructureConfigurable.java @@ -283,16 +283,6 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable { return actions; } - @Override - protected List removeFacet(final Facet facet) { - List removed = super.removeFacet(facet); - ModuleStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); - for (Facet removedFacet : removed) { - myContext.getDaemonAnalyzer().removeElement(new FacetProjectStructureElement(myContext, removedFacet)); - } - return removed; - } - @Override protected boolean updateMultiSelection(final List selectedConfigurables) { return updateMultiSelection(selectedConfigurables, getDetailsComponent()); @@ -352,6 +342,11 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable { return null; } + @Override + protected List> getRemoveHandlers() { + return Collections.singletonList(new FacetRemoveHandler()); + } + @Override protected void processRemovedItems() { } @@ -407,6 +402,24 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable { public void dispose() { } + private class FacetRemoveHandler extends RemoveConfigurableHandler { + public FacetRemoveHandler() { + super(FacetConfigurable.class); + } + + @Override + public boolean remove(@NotNull Collection facets) { + for (Facet facet : facets) { + List removed = myContext.myModulesConfigurator.getFacetsConfigurator().removeFacet(facet); + ModuleStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); + for (Facet removedFacet : removed) { + myContext.getDaemonAnalyzer().removeElement(new FacetProjectStructureElement(myContext, removedFacet)); + } + } + return true; + } + } + private class FacetConfigurableNode extends MyNode { public FacetConfigurableNode(final FacetConfigurable facetConfigurable) { super(facetConfigurable); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkListConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkListConfigurable.java index 42bee7fca91d..e5da6977035a 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkListConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/JdkListConfigurable.java @@ -38,10 +38,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreePath; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; public class JdkListConfigurable extends BaseStructureConfigurable { private final ProjectSdksModel myJdksTreeModel; @@ -212,9 +209,8 @@ public class JdkListConfigurable extends BaseStructureConfigurable { } @Override - protected void removeJdk(final Sdk jdk) { - myJdksTreeModel.removeSdk(jdk); - myContext.getDaemonAnalyzer().removeElement(new SdkProjectStructureElement(myContext, jdk)); + protected List> getRemoveHandlers() { + return Collections.singletonList(new SdkRemoveHandler()); } @Override @@ -223,4 +219,19 @@ public class JdkListConfigurable extends BaseStructureConfigurable { String getEmptySelectionString() { return "Select an SDK to view or edit its details here"; } + + private class SdkRemoveHandler extends RemoveConfigurableHandler { + public SdkRemoveHandler() { + super(JdkConfigurable.class); + } + + @Override + public boolean remove(@NotNull Collection sdks) { + for (Sdk sdk : sdks) { + myJdksTreeModel.removeSdk(sdk); + myContext.getDaemonAnalyzer().removeElement(new SdkProjectStructureElement(myContext, sdk)); + } + return true; + } + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java index 040bffd84619..5418abf19f8c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java @@ -113,10 +113,17 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple private final FacetEditorFacadeImpl myFacetEditorFacade = new FacetEditorFacadeImpl(this, TREE_UPDATER); + private final List> myRemoveHandlers; public ModuleStructureConfigurable(Project project, ModuleManager manager) { super(project); myModuleManager = manager; + myRemoveHandlers = new ArrayList>(); + myRemoveHandlers.add(new ModuleRemoveHandler()); + myRemoveHandlers.add(new FacetInModuleRemoveHandler()); + for (ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { + myRemoveHandlers.addAll(extension.getRemoveHandlers()); + } } @Override @@ -639,30 +646,6 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple PlatformIcons.CLOSED_MODULE_GROUP_ICON); } - @Override - protected boolean canBeRemoved(final Object[] editableObjects) { - if (super.canBeRemoved(editableObjects)) { - return true; - } - for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { - if (extension.canBeRemoved(editableObjects)) { - return true; - } - } - - return false; - } - - @Override - protected boolean removeObject(final Object editableObject) { - for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { - if (extension.removeObject(editableObject)) { - return true; - } - } - return super.removeObject(editableObject); - } - private boolean canBeCopiedByExtension(final NamedConfigurable configurable) { for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { if (extension.canBeCopied(configurable)) { @@ -678,6 +661,46 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple } } + private class FacetInModuleRemoveHandler extends RemoveConfigurableHandler { + public FacetInModuleRemoveHandler() { + super(FacetConfigurable.class); + } + + @Override + public boolean remove(@NotNull Collection facets) { + for (Facet facet : facets) { + List removed = myContext.myModulesConfigurator.getFacetsConfigurator().removeFacet(facet); + FacetStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); + } + return true; + } + } + + private class ModuleRemoveHandler extends RemoveConfigurableHandler { + public ModuleRemoveHandler() { + super(ModuleConfigurable.class); + } + + @Override + public boolean remove(@NotNull Collection modules) { + ModulesConfigurator modulesConfigurator = myContext.myModulesConfigurator; + List deleted = modulesConfigurator.deleteModules(modules); + if (deleted.isEmpty()) { + return false; + } + for (Module module : deleted) { + List removed = modulesConfigurator.getFacetsConfigurator().removeAllFacets(module); + FacetStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); + myContext.getDaemonAnalyzer().removeElement(new ModuleProjectStructureElement(myContext, module)); + + for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { + extension.moduleRemoved(module); + } + } + return true; + } + } + private class MyDataProviderWrapper extends JPanel implements DataProvider { public MyDataProviderWrapper(final JComponent component) { super(new BorderLayout()); @@ -826,27 +849,8 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple } @Override - protected List removeFacet(final Facet facet) { - List removed = super.removeFacet(facet); - FacetStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); - return removed; - } - - @Override - protected boolean removeModule(final Module module) { - ModulesConfigurator modulesConfigurator = myContext.myModulesConfigurator; - if (!modulesConfigurator.deleteModule(module)) { - //wait for confirmation - return false; - } - List removed = modulesConfigurator.getFacetsConfigurator().removeAllFacets(module); - FacetStructureConfigurable.getInstance(myProject).removeFacetNodes(removed); - myContext.getDaemonAnalyzer().removeElement(new ModuleProjectStructureElement(myContext, module)); - - for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) { - extension.moduleRemoved(module); - } - return true; + protected List> getRemoveHandlers() { + return myRemoveHandlers; } @Override diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureExtension.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureExtension.java index 76ba8231c1fe..e0ed358b5c02 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureExtension.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureExtension.java @@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.List; public abstract class ModuleStructureExtension { @@ -45,12 +46,8 @@ public abstract class ModuleStructureExtension { public void disposeUIResources() { } - public boolean canBeRemoved(final Object[] editableObjects) { - return false; - } - - public boolean removeObject(final Object editableObject) { - return false; + public List> getRemoveHandlers() { + return Collections.emptyList(); } public Collection createAddActions(final NullableComputable selectedNodeRetriever, diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/RemoveConfigurableHandler.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/RemoveConfigurableHandler.java new file mode 100644 index 000000000000..78862ef2f021 --- /dev/null +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/RemoveConfigurableHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots.ui.configuration.projectRoot; + +import com.intellij.openapi.ui.NamedConfigurable; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * @author nik + */ +public abstract class RemoveConfigurableHandler { + private final Class> myConfigurableClass; + + public RemoveConfigurableHandler(Class> configurableClass) { + myConfigurableClass = configurableClass; + } + + public Class> getConfigurableClass() { + return myConfigurableClass; + } + + public boolean canBeRemoved(@NotNull Collection objects) { + return true; + } + + public abstract boolean remove(@NotNull Collection objects); +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index fc9d152df660..45c1bf44a8a3 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -789,10 +789,17 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom } protected void removePaths(final TreePath... paths) { + List nodes = new ArrayList(); + for (TreePath path : paths) { + nodes.add((MyNode)path.getLastPathComponent()); + } + removeNodes(nodes); + } + + protected void removeNodes(final List nodes) { MyNode parentNode = null; int idx = -1; - for (TreePath path : paths) { - final MyNode node = (MyNode)path.getLastPathComponent(); + for (MyNode node : nodes) { final NamedConfigurable namedConfigurable = node.getConfigurable(); final Object editableObject = namedConfigurable.getEditableObject(); parentNode = (MyNode)node.getParent(); @@ -804,7 +811,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom namedConfigurable.disposeUIResources(); } - if (paths.length > 0) { + if (!nodes.isEmpty()) { if (parentNode != null && idx != -1) { DefaultMutableTreeNode toSelect = null; if (idx < parentNode.getChildCount()) { diff --git a/platform/projectModel-impl/src/messages/ProjectBundle.properties b/platform/projectModel-impl/src/messages/ProjectBundle.properties index 2e9d6d574e7c..7514198998ed 100644 --- a/platform/projectModel-impl/src/messages/ProjectBundle.properties +++ b/platform/projectModel-impl/src/messages/ProjectBundle.properties @@ -144,9 +144,9 @@ module.add.error.message=Error adding module to project: {0} module.add.error.title=Add Module module.add.action=Add module.remove.action=Remove -module.remove.last.confirmation=Are you sure you want to remove the only module from this project?\nNo files will be deleted on disk. -module.remove.confirmation=Remove module ''{0}'' from the project?\nNo files will be deleted on disk. -module.remove.confirmation.title=Remove Module +module.remove.last.confirmation=Are you sure you want to remove {0, choice, 1#the only module|2#all modules} from this project?\nNo files will be deleted on disk. +module.remove.confirmation=Remove {1, choice, 1#module ''''{0}''''|2#{1} modules} from the project?\nNo files will be deleted on disk. +module.remove.confirmation.title=Remove {0, choice, 1#Module|2#Modules} module.classpath.button.edit=Ed&it... module.libraries.include.all.button=Include All module.libraries.exclude.all.button=Exclude All @@ -244,6 +244,9 @@ libraries.node.text.module=Libraries contain classes that add up various functio Expand the node and select child items to manage Module libraries.

\ \ To attach a library to a module, select that module, click the Dependencies tab, click Add and specify the library location. +libraries.remove.confirmation.title=Remove {0, choice, 1#Library|2#Libraries} +libraries.remove.confirmation.text=Library ''{0}'' and {1} more {1, choice, 1#library|2#libraries} are used in the project.\n \ + Are you sure you want to delete all selected libraries? jdks.node.display.name=SDKs project.language.level.combo.item=Project default add.action.name=Add new ...