blob: a2a7e6b5c76fcec3b6fc8de042d5f1c16e0c0047 [file] [log] [blame]
/*
* 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.roots.ui.configuration.projectRoot;
import com.intellij.CommonBundle;
import com.intellij.facet.Facet;
import com.intellij.facet.impl.ProjectFacetsConfigurator;
import com.intellij.facet.impl.ui.actions.AddFacetToModuleAction;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.ide.projectView.impl.ModuleGroup;
import com.intellij.ide.projectView.impl.ModuleGroupUtil;
import com.intellij.ide.util.projectWizard.ModuleBuilder;
import com.intellij.ide.util.projectWizard.NamePathComponent;
import com.intellij.ide.util.projectWizard.ProjectWizardUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.*;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.ClonableOrderEntry;
import com.intellij.openapi.roots.impl.ProjectRootManagerImpl;
import com.intellij.openapi.roots.impl.RootModelImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.navigation.Place;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.PathUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* User: anna
* Date: 02-Jun-2006
*/
public class ModuleStructureConfigurable extends BaseStructureConfigurable implements Place.Navigator {
private static final Comparator<MyNode> NODE_COMPARATOR = new Comparator<MyNode>() {
@Override
public int compare(final MyNode o1, final MyNode o2) {
final NamedConfigurable configurable1 = o1.getConfigurable();
final NamedConfigurable configurable2 = o2.getConfigurable();
if (configurable1.getClass() == configurable2.getClass()) {
return StringUtil.naturalCompare(o1.getDisplayName(), o2.getDisplayName());
}
final Object editableObject1 = configurable1.getEditableObject();
final Object editableObject2 = configurable2.getEditableObject();
if (editableObject2 instanceof Module && editableObject1 instanceof ModuleGroup) return -1;
if (editableObject1 instanceof Module && editableObject2 instanceof ModuleGroup) return 1;
if (editableObject2 instanceof Module && editableObject1 instanceof String) return 1;
if (editableObject1 instanceof Module && editableObject2 instanceof String) return -1;
if (editableObject2 instanceof ModuleGroup && editableObject1 instanceof String) return 1;
if (editableObject1 instanceof ModuleGroup && editableObject2 instanceof String) return -1;
return 0;
}
};
private boolean myPlainMode;
private final ModuleManager myModuleManager;
private final FacetEditorFacadeImpl myFacetEditorFacade = new FacetEditorFacadeImpl(this, TREE_UPDATER);
public ModuleStructureConfigurable(Project project, ModuleManager manager) {
super(project);
myModuleManager = manager;
}
@Override
protected String getComponentStateKey() {
return "ModuleStructureConfigurable.UI";
}
@Override
protected void initTree() {
super.initTree();
myTree.setRootVisible(false);
}
@Override
protected ArrayList<AnAction> getAdditionalActions() {
final ArrayList<AnAction> result = new ArrayList<AnAction>();
result.add(ActionManager.getInstance().getAction(IdeActions.GROUP_MOVE_MODULE_TO_GROUP));
return result;
}
@Override
public void addNode(MyNode nodeToAdd, MyNode parent) {
super.addNode(nodeToAdd, parent);
}
@Override
@NotNull
protected ArrayList<AnAction> createActions(final boolean fromPopup) {
final ArrayList<AnAction> result = super.createActions(fromPopup);
if (fromPopup || !Registry.is("ide.new.project.settings")) {
result.add(Separator.getInstance());
result.add(new MyGroupAction());
addCollapseExpandActions(result);
}
return result;
}
@Override
@NotNull
protected List<? extends AnAction> createCopyActions(boolean fromPopup) {
return Collections.singletonList(new MyCopyAction());
}
@Override
protected void loadTree() {
createProjectNodes();
((DefaultTreeModel)myTree.getModel()).reload();
myUiDisposed = false;
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
for (Module module : myModuleManager.getModules()) {
result.add(new ModuleProjectStructureElement(myContext, module));
}
return result;
}
@Override
protected void updateSelection(@Nullable final NamedConfigurable configurable) {
FacetStructureConfigurable.getInstance(myProject).disposeMultipleSettingsEditor();
ApplicationManager.getApplication().assertIsDispatchThread();
super.updateSelection(configurable);
if (configurable != null) {
updateModuleEditorSelection(configurable);
}
}
@Override
protected boolean isAutoScrollEnabled() {
return myAutoScrollEnabled;
}
@Override
protected boolean updateMultiSelection(final List<NamedConfigurable> selectedConfigurables) {
return FacetStructureConfigurable.getInstance(myProject).updateMultiSelection(selectedConfigurables, getDetailsComponent());
}
private void updateModuleEditorSelection(final NamedConfigurable configurable) {
if (configurable instanceof ModuleConfigurable){
final ModuleConfigurable moduleConfigurable = (ModuleConfigurable)configurable;
final ModuleEditor editor = moduleConfigurable.getModuleEditor();
if (editor != null) { //already deleted
editor.init(myHistory);
}
}
if (configurable instanceof FacetConfigurable) {
((FacetConfigurable)configurable).getEditor().onFacetSelected();
}
}
private void createProjectNodes() {
final Map<ModuleGroup, MyNode> moduleGroup2NodeMap = new HashMap<ModuleGroup, MyNode>();
final Module[] modules = myModuleManager.getModules();
for (final Module module : modules) {
ModuleConfigurable configurable = new ModuleConfigurable(myContext.myModulesConfigurator, module, TREE_UPDATER);
final MyNode moduleNode = new MyNode(configurable);
boolean nodesAdded = myFacetEditorFacade.addFacetsNodes(module, moduleNode);
nodesAdded |= addNodesFromExtensions(module, moduleNode);
if (nodesAdded) {
myTree.setShowsRootHandles(true);
}
final String[] groupPath = myPlainMode ? null : myContext.myModulesConfigurator.getModuleModel().getModuleGroupPath(module);
if (groupPath == null || groupPath.length == 0){
addNode(moduleNode, myRoot);
} else {
final MyNode moduleGroupNode = ModuleGroupUtil
.buildModuleGroupPath(new ModuleGroup(groupPath), myRoot, moduleGroup2NodeMap,
new Consumer<ModuleGroupUtil.ParentChildRelation<MyNode>>() {
@Override
public void consume(final ModuleGroupUtil.ParentChildRelation<MyNode> parentChildRelation) {
addNode(parentChildRelation.getChild(), parentChildRelation.getParent());
}
},
new Function<ModuleGroup, MyNode>() {
@Override
public MyNode fun(final ModuleGroup moduleGroup) {
final NamedConfigurable moduleGroupConfigurable =
createModuleGroupConfigurable(moduleGroup);
return new MyNode(moduleGroupConfigurable, true);
}
});
addNode(moduleNode, moduleGroupNode);
}
}
if (myProject.isDefault()) { //do not add modules node in case of template project
myRoot.removeAllChildren();
}
addRootNodesFromExtensions(myRoot, myProject);
//final LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTable(myProject);
//final LibrariesModifiableModel projectLibrariesProvider = new LibrariesModifiableModel(table);
//myLevel2Providers.put(LibraryTablesRegistrar.PROJECT_LEVEL, projectLibrariesProvider);
//
//myProjectNode.add(myLevel2Nodes.get(LibraryTablesRegistrar.PROJECT_LEVEL));
}
private void addRootNodesFromExtensions(final MyNode root, final Project project) {
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.addRootNodes(root, project, TREE_UPDATER);
}
}
private boolean addNodesFromExtensions(final Module module, final MyNode moduleNode) {
boolean nodesAdded = false;
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
nodesAdded |= extension.addModuleNodeChildren(module, moduleNode, TREE_UPDATER);
}
return nodesAdded;
}
public boolean updateProjectTree(final Module[] modules, final ModuleGroup group) {
if (myRoot.getChildCount() == 0) return false; //isn't visible
final MyNode [] nodes = new MyNode[modules.length];
int i = 0;
for (Module module : modules) {
MyNode node = findModuleNode(module);
LOG.assertTrue(node != null, "Module " + module.getName() + " is not in project.");
node.removeFromParent();
nodes[i ++] = node;
}
for (final MyNode moduleNode : nodes) {
final String[] groupPath = myPlainMode
? null
: group != null ? group.getGroupPath() : null;
if (groupPath == null || groupPath.length == 0){
addNode(moduleNode, myRoot);
} else {
final MyNode moduleGroupNode = ModuleGroupUtil
.updateModuleGroupPath(new ModuleGroup(groupPath), myRoot, new Function<ModuleGroup, MyNode>() {
@Override
@Nullable
public MyNode fun(final ModuleGroup group) {
return findNodeByObject(myRoot, group);
}
}, new Consumer<ModuleGroupUtil.ParentChildRelation<MyNode>>() {
@Override
public void consume(final ModuleGroupUtil.ParentChildRelation<MyNode> parentChildRelation) {
addNode(parentChildRelation.getChild(), parentChildRelation.getParent());
}
}, new Function<ModuleGroup, MyNode>() {
@Override
public MyNode fun(final ModuleGroup moduleGroup) {
final NamedConfigurable moduleGroupConfigurable = createModuleGroupConfigurable(moduleGroup);
return new MyNode(moduleGroupConfigurable, true);
}
});
addNode(moduleNode, moduleGroupNode);
}
Module module = (Module)moduleNode.getConfigurable().getEditableObject();
myFacetEditorFacade.addFacetsNodes(module, moduleNode);
addNodesFromExtensions(module, moduleNode);
}
TreeUtil.sort(myRoot, getNodeComparator());
((DefaultTreeModel)myTree.getModel()).reload(myRoot);
return true;
}
@Override
protected Comparator<MyNode> getNodeComparator() {
List<Comparator<MyNode>> comparators = ContainerUtil
.mapNotNull(ModuleStructureExtension.EP_NAME.getExtensions(), new Function<ModuleStructureExtension, Comparator<MyNode>>() {
@Override
public Comparator<MyNode> fun(final ModuleStructureExtension moduleStructureExtension) {
return moduleStructureExtension.getNodeComparator();
}
});
return new MergingComparator<MyNode>(ContainerUtil.concat(comparators, Collections.singletonList(NODE_COMPARATOR)));
}
@Override
public void init(final StructureConfigurableContext context) {
super.init(context);
addItemsChangeListener(new ItemsChangeListener() {
@Override
public void itemChanged(@Nullable Object deletedItem) {
if (deletedItem instanceof Library) {
final Library library = (Library)deletedItem;
final MyNode node = findNodeByObject(myRoot, library);
if (node != null) {
final TreeNode parent = node.getParent();
node.removeFromParent();
((DefaultTreeModel)myTree.getModel()).reload(parent);
}
myContext.getDaemonAnalyzer().removeElement(new LibraryProjectStructureElement(myContext, library));
}
}
@Override
public void itemsExternallyChanged() {
//do nothing
}
});
}
@Override
public void reset() {
super.reset();
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.reset(myProject);
}
}
@Override
public void apply() throws ConfigurationException {
final Set<MyNode> roots = new HashSet<MyNode>();
roots.add(myRoot);
checkApply(roots, ProjectBundle.message("rename.message.prefix.module"), ProjectBundle.message("rename.module.title"));
// let's apply extensions first, since they can write to/commit modifiable models
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
if (extension.isModified()) {
extension.apply();
}
}
if (myContext.myModulesConfigurator.isModified()) {
myContext.myModulesConfigurator.apply();
}
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.afterModelCommit();
}
}
@Override
public boolean isModified() {
if (myContext.myModulesConfigurator.isModified()) {
return true;
}
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
if (extension.isModified()) {
return true;
}
}
return false;
}
@Override
public void disposeUIResources() {
super.disposeUIResources();
myFacetEditorFacade.clearMaps(true);
myContext.myModulesConfigurator.disposeUIResources();
ModuleStructureConfigurable.super.disposeUIResources();
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.disposeUIResources();
}
}
@Override
public void dispose() {}
@Override
public JComponent createComponent() {
return new MyDataProviderWrapper(super.createComponent());
}
@Override
protected void processRemovedItems() {
// do nothing
}
@Override
protected boolean wasObjectStored(Object editableObject) {
return false;
}
@Override
public String getDisplayName() {
return ProjectBundle.message("project.roots.display.name");
}
@Override
@Nullable
@NonNls
public String getHelpTopic() {
final String topic = super.getHelpTopic();
if (topic != null) {
return topic;
}
return "reference.settingsdialog.project.structure.module";
}
public ActionCallback selectOrderEntry(@NotNull final Module module, @Nullable final OrderEntry orderEntry) {
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
final ActionCallback callback = extension.selectOrderEntry(module, orderEntry);
if (callback != null) {
return callback;
}
}
Place p = new Place();
p.putPath(ProjectStructureConfigurable.CATEGORY, this);
Runnable r = null;
final MasterDetailsComponent.MyNode node = findModuleNode(module);
if (node != null) {
p.putPath(TREE_OBJECT, module);
p.putPath(ModuleEditor.SELECTED_EDITOR_NAME, ClasspathEditor.NAME);
r = new Runnable() {
@Override
public void run() {
if (orderEntry != null) {
ModuleEditor moduleEditor = ((ModuleConfigurable)node.getConfigurable()).getModuleEditor();
ModuleConfigurationEditor editor = moduleEditor.getEditor(ClasspathEditor.NAME);
if (editor instanceof ClasspathEditor) {
((ClasspathEditor)editor).selectOrderEntry(orderEntry);
}
}
}
};
}
final ActionCallback result = ProjectStructureConfigurable.getInstance(myProject).navigateTo(p, true);
return r != null ? result.doWhenDone(r) : result;
}
public static ModuleStructureConfigurable getInstance(final Project project) {
return ServiceManager.getService(project, ModuleStructureConfigurable.class);
}
public Project getProject() {
return myProject;
}
public Module[] getModules() {
if (myContext.myModulesConfigurator != null) {
final ModifiableModuleModel model = myContext.myModulesConfigurator.getModuleModel();
return model.getModules();
} else {
return myModuleManager.getModules();
}
}
public void removeLibraryOrderEntry(final Module module, final Library library) {
final ModuleEditor moduleEditor = myContext.myModulesConfigurator.getModuleEditor(module);
LOG.assertTrue(moduleEditor != null, "Current module editor was not initialized");
final ModifiableRootModel modelProxy = moduleEditor.getModifiableRootModelProxy();
final OrderEntry[] entries = modelProxy.getOrderEntries();
for (OrderEntry entry : entries) {
if (entry instanceof LibraryOrderEntry && Comparing.strEqual(entry.getPresentableName(), library.getName())) {
modelProxy.removeOrderEntry(entry);
break;
}
}
myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module));
myTree.repaint();
}
public void addLibraryOrderEntry(final Module module, final Library library) {
Component parent = WindowManager.getInstance().suggestParentWindow(module.getProject());
final ModuleEditor moduleEditor = myContext.myModulesConfigurator.getModuleEditor(module);
LOG.assertTrue(moduleEditor != null, "Current module editor was not initialized");
final ModifiableRootModel modelProxy = moduleEditor.getModifiableRootModelProxy();
final OrderEntry[] entries = modelProxy.getOrderEntries();
for (OrderEntry entry : entries) {
if (entry instanceof LibraryOrderEntry && Comparing.strEqual(entry.getPresentableName(), library.getName())) {
if (Messages.showYesNoDialog(parent,
ProjectBundle.message("project.roots.replace.library.entry.message", entry.getPresentableName()),
ProjectBundle.message("project.roots.replace.library.entry.title"),
Messages.getInformationIcon()) == Messages.YES) {
modelProxy.removeOrderEntry(entry);
break;
}
}
}
modelProxy.addLibraryEntry(library);
myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module));
myTree.repaint();
}
@Nullable
public MyNode findModuleNode(final Module module) {
return findNodeByObject(myRoot, module);
}
public FacetEditorFacadeImpl getFacetEditorFacade() {
return myFacetEditorFacade;
}
public ProjectFacetsConfigurator getFacetConfigurator() {
return myContext.myModulesConfigurator.getFacetsConfigurator();
}
private void addModule(boolean anImport) {
final List<Module> modules = myContext.myModulesConfigurator.addModule(myTree, anImport);
if (modules != null) {
for (Module module : modules) {
addModuleNode(module);
}
}
}
private void addModuleNode(final Module module) {
final MyNode node = new MyNode(new ModuleConfigurable(myContext.myModulesConfigurator, module, TREE_UPDATER));
final TreePath selectionPath = myTree.getSelectionPath();
MyNode parent = null;
if (selectionPath != null) {
MyNode selected = (MyNode)selectionPath.getLastPathComponent();
final Object o = selected.getConfigurable().getEditableObject();
if (o instanceof ModuleGroup) {
myContext.myModulesConfigurator.getModuleModel().setModuleGroupPath(module, ((ModuleGroup)o).getGroupPath());
parent = selected;
} else if (o instanceof Module) { //create near selected
final ModifiableModuleModel modifiableModuleModel = myContext.myModulesConfigurator.getModuleModel();
final String[] groupPath = modifiableModuleModel.getModuleGroupPath((Module)o);
if (groupPath != null) {
modifiableModuleModel.setModuleGroupPath(module, groupPath);
parent = findNodeByObject(myRoot, new ModuleGroup(groupPath));
}
}
}
if (parent == null) parent = myRoot;
addNode(node, parent);
myFacetEditorFacade.addFacetsNodes(module, node);
addNodesFromExtensions(module, node);
((DefaultTreeModel)myTree.getModel()).reload(parent);
selectNodeInTree(node);
final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
daemonAnalyzer.queueUpdate(new ModuleProjectStructureElement(myContext, module));
daemonAnalyzer.queueUpdateForAllElementsWithErrors(); //missing modules added
}
@Nullable
public Module getSelectedModule() {
final Object selectedObject = getSelectedObject();
if (selectedObject instanceof Module) {
return (Module)selectedObject;
}
if (selectedObject instanceof Library) {
if (((Library)selectedObject).getTable() == null) {
final MyNode node = (MyNode)myTree.getSelectionPath().getLastPathComponent();
return (Module)((MyNode)node.getParent()).getConfigurable().getEditableObject();
}
}
return null;
}
@Override
@NotNull
@NonNls
public String getId() {
return "project.structure";
}
@Override
@Nullable
public Runnable enableSearch(String option) {
return null;
}
@Nullable
public Module getModule(final String moduleName) {
if (moduleName == null) return null;
return (myContext != null && myContext.myModulesConfigurator != null) ? myContext.myModulesConfigurator.getModule(moduleName) : myModuleManager.findModuleByName(moduleName);
}
public StructureConfigurableContext getContext() {
return myContext;
}
private static TextConfigurable<ModuleGroup> createModuleGroupConfigurable(final ModuleGroup moduleGroup) {
return new TextConfigurable<ModuleGroup>(moduleGroup, moduleGroup.toString(),
ProjectBundle.message("module.group.banner.text", moduleGroup.toString()),
ProjectBundle.message("project.roots.module.groups.text"),
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)) {
return true;
}
}
return false;
}
private void copyByExtension(final NamedConfigurable configurable) {
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
extension.copy(configurable, TREE_UPDATER);
}
}
private class MyDataProviderWrapper extends JPanel implements DataProvider {
public MyDataProviderWrapper(final JComponent component) {
super(new BorderLayout());
add(component, BorderLayout.CENTER);
}
@Override
@Nullable
public Object getData(@NonNls String dataId) {
if (DataKeys.MODULE_CONTEXT_ARRAY.is(dataId)){
final TreePath[] paths = myTree.getSelectionPaths();
if (paths != null) {
ArrayList<Module> modules = new ArrayList<Module>();
for (TreePath path : paths) {
MyNode node = (MyNode)path.getLastPathComponent();
final NamedConfigurable configurable = node.getConfigurable();
LOG.assertTrue(configurable != null, "already disposed");
final Object o = configurable.getEditableObject();
if (o instanceof Module) {
modules.add((Module)o);
}
}
return !modules.isEmpty() ? modules.toArray(new Module[modules.size()]) : null;
}
}
if (DataKeys.MODULE_CONTEXT.is(dataId)){
return getSelectedModule();
}
if (LangDataKeys.MODIFIABLE_MODULE_MODEL.is(dataId)){
return myContext.myModulesConfigurator.getModuleModel();
}
return null;
}
}
private class MyGroupAction extends ToggleAction implements DumbAware {
public MyGroupAction() {
super("", "", AllIcons.ObjectBrowser.CompactEmptyPackages);
}
@Override
public void update(final AnActionEvent e) {
super.update(e);
final Presentation presentation = e.getPresentation();
String text = ProjectBundle.message("project.roots.plain.mode.action.text.disabled");
if (myPlainMode){
text = ProjectBundle.message("project.roots.plain.mode.action.text.enabled");
}
presentation.setText(text);
presentation.setDescription(text);
if (myContext.myModulesConfigurator != null) {
presentation.setVisible(myContext.myModulesConfigurator.getModuleModel().hasModuleGroups());
}
}
@Override
public boolean isSelected(AnActionEvent e) {
return myPlainMode;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
myPlainMode = state;
DefaultMutableTreeNode selection = null;
final TreePath selectionPath = myTree.getSelectionPath();
if (selectionPath != null){
selection = (DefaultMutableTreeNode)selectionPath.getLastPathComponent();
}
final ModifiableModuleModel model = myContext.myModulesConfigurator.getModuleModel();
final Module[] modules = model.getModules();
for (Module module : modules) {
final String[] groupPath = model.getModuleGroupPath(module);
updateProjectTree(new Module[]{module}, groupPath != null ? new ModuleGroup(groupPath) : null);
}
if (state) {
removeModuleGroups();
}
if (selection != null){
TreeUtil.selectInTree(selection, true, myTree);
}
}
private void removeModuleGroups() {
for(int i = myRoot.getChildCount() - 1; i >=0; i--){
final MyNode node = (MyNode)myRoot.getChildAt(i);
if (node.getConfigurable().getEditableObject() instanceof ModuleGroup){
node.removeFromParent();
}
}
((DefaultTreeModel)myTree.getModel()).reload(myRoot);
}
}
@Override
protected AbstractAddGroup createAddAction() {
return new AbstractAddGroup(ProjectBundle.message("add.new.header.text")) {
@Override
@NotNull
public AnAction[] getChildren(@Nullable final AnActionEvent e) {
ArrayList<AnAction> result = new ArrayList<AnAction>();
AnAction addModuleAction = new AddModuleAction(false);
addModuleAction.getTemplatePresentation().setText("New Module");
result.add(addModuleAction);
AnAction importModuleAction = new AddModuleAction(true);
importModuleAction.getTemplatePresentation().setText("Import Module");
importModuleAction.getTemplatePresentation().setIcon(AllIcons.ToolbarDecorator.Import);
result.add(importModuleAction);
final Collection<AnAction> actions = AddFacetToModuleAction.createAddFrameworkActions(myFacetEditorFacade, myProject);
if (!actions.isEmpty()) {
result.add(new Separator(ProjectBundle.message("add.group.framework.separator")));
result.addAll(actions);
}
final NullableComputable<MyNode> selectedNodeRetriever = new NullableComputable<MyNode>() {
@Override
public MyNode compute() {
final TreePath selectionPath = myTree.getSelectionPath();
final Object lastPathComponent = selectionPath == null ? null : selectionPath.getLastPathComponent();
if (lastPathComponent instanceof MyNode) {
return (MyNode)lastPathComponent;
}
return null;
}
};
Collection<AnAction> actionsFromExtensions = new ArrayList<AnAction>();
for (final ModuleStructureExtension extension : ModuleStructureExtension.EP_NAME.getExtensions()) {
actionsFromExtensions.addAll(extension.createAddActions(selectedNodeRetriever, TREE_UPDATER, myProject, myRoot));
}
if (!actionsFromExtensions.isEmpty() && !result.isEmpty()) {
result.add(new Separator());
}
result.addAll(actionsFromExtensions);
return result.toArray(new AnAction[result.size()]);
}
};
}
@Override
protected List<Facet> removeFacet(final Facet facet) {
List<Facet> 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<Facet> 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;
}
@Override
@Nullable
protected String getEmptySelectionString() {
return ProjectBundle.message("empty.module.selection.string");
}
private class MyCopyAction extends AnAction implements DumbAware {
private MyCopyAction() {
super(CommonBundle.message("button.copy"), CommonBundle.message("button.copy"), COPY_ICON);
}
@Override
public void actionPerformed(final AnActionEvent e) {
final NamedConfigurable namedConfigurable = getSelectedConfigurable();
if (namedConfigurable instanceof ModuleConfigurable) {
try {
final ModuleEditor moduleEditor = ((ModuleConfigurable)namedConfigurable).getModuleEditor();
final String modulePresentation = IdeBundle.message("project.new.wizard.module.identification");
final NamePathComponent component = new NamePathComponent(IdeBundle.message("label.module.name"), IdeBundle.message("label.component.file.location", StringUtil.capitalize(modulePresentation)), IdeBundle.message("title.select.project.file.directory", modulePresentation),
IdeBundle.message("description.select.project.file.directory", StringUtil.capitalize(modulePresentation)), true,
false);
final Module originalModule = moduleEditor.getModule();
if (originalModule != null) {
component.setPath(FileUtil.toSystemDependentName(PathUtil.getParentPath(originalModule.getModuleFilePath())));
}
final DialogBuilder dialogBuilder = new DialogBuilder(myTree);
dialogBuilder.setTitle(ProjectBundle.message("copy.module.dialog.title"));
dialogBuilder.setCenterPanel(component);
dialogBuilder.setPreferredFocusComponent(component.getNameComponent());
dialogBuilder.setOkOperation(new Runnable() {
@Override
public void run() {
final String name = component.getNameValue();
if (name.length() == 0) {
Messages.showErrorDialog(ProjectBundle.message("enter.module.copy.name.error.message"), CommonBundle.message("title.error"));
return;
}
if (getModule(name) != null) {
Messages.showErrorDialog(ProjectBundle.message("module.0.already.exists.error.message", name), CommonBundle.message("title.error"));
return;
}
if (component.getPath().length() == 0) {
Messages.showErrorDialog(IdeBundle.message("prompt.enter.project.file.location", modulePresentation),
CommonBundle.message("title.error"));
return;
}
if (!ProjectWizardUtil
.createDirectoryIfNotExists(IdeBundle.message("directory.project.file.directory", modulePresentation), component.getPath(),
true)) {
Messages.showErrorDialog(ProjectBundle.message("path.0.is.invalid.error.message", component.getPath()), CommonBundle.message("title.error"));
return;
}
dialogBuilder.getDialogWrapper().close(DialogWrapper.OK_EXIT_CODE);
}
});
if (dialogBuilder.show() != DialogWrapper.OK_EXIT_CODE) return;
final ModifiableRootModel rootModel = moduleEditor.getModifiableRootModel();
final String path = component.getPath();
final ModuleBuilder builder = new ModuleBuilder() {
@Override
public void setupRootModel(final ModifiableRootModel modifiableRootModel) throws ConfigurationException {
if (rootModel.isSdkInherited()) {
modifiableRootModel.inheritSdk();
}
else {
modifiableRootModel.setSdk(rootModel.getSdk());
}
modifiableRootModel.getModuleExtension(CompilerModuleExtension.class).inheritCompilerOutputPath(true);
modifiableRootModel.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevelModuleExtension.getInstance(rootModel.getModule()).getLanguageLevel());
for (OrderEntry entry : rootModel.getOrderEntries()) {
if (entry instanceof JdkOrderEntry) continue;
if (entry instanceof ModuleSourceOrderEntry) continue;
if (entry instanceof ClonableOrderEntry) {
modifiableRootModel.addOrderEntry(((ClonableOrderEntry)entry).cloneEntry((RootModelImpl)modifiableRootModel,
(ProjectRootManagerImpl)ProjectRootManager
.getInstance(myProject),
VirtualFilePointerManager.getInstance()));
}
}
VirtualFile content = LocalFileSystem.getInstance().findFileByPath(component.getPath());
if (content == null) {
content = LocalFileSystem.getInstance().refreshAndFindFileByPath(component.getPath());
}
modifiableRootModel.addContentEntry(content);
}
@Override
public ModuleType getModuleType() {
return ModuleType.get(rootModel.getModule());
}
};
builder.setName(component.getNameValue());
builder.setModuleFilePath(path + "/" + builder.getName() + ModuleFileType.DOT_DEFAULT_EXTENSION);
final Module module = myContext.myModulesConfigurator.addModule(builder);
if (module != null) {
addModuleNode(module);
}
}
catch (Exception e1) {
LOG.error(e1);
}
}
else {
copyByExtension(namedConfigurable);
}
}
@Override
public void update(final AnActionEvent e) {
TreePath[] selectionPaths = myTree.getSelectionPaths();
if (selectionPaths == null || selectionPaths.length != 1) {
e.getPresentation().setEnabled(false);
} else {
final NamedConfigurable selectedConfigurable = getSelectedConfigurable();
e.getPresentation().setEnabled(selectedConfigurable instanceof ModuleConfigurable || canBeCopiedByExtension(selectedConfigurable));
}
}
}
private class AddModuleAction extends AnAction implements DumbAware {
private final boolean myImport;
public AddModuleAction(boolean anImport) {
super(ProjectBundle.message("add.new.module.text.full"), null, AllIcons.Actions.Module);
myImport = anImport;
}
@Override
public void actionPerformed(final AnActionEvent e) {
addModule(myImport);
}
}
private static class MergingComparator<T> implements Comparator<T> {
private final List<Comparator<T>> myDelegates;
public MergingComparator(final List<Comparator<T>> delegates) {
myDelegates = delegates;
}
@Override
public int compare(final T o1, final T o2) {
for (Comparator<T> delegate : myDelegates) {
int value = delegate.compare(o1, o2);
if (value != 0) return value;
}
return 0;
}
}
}